reorganise code - gramscii - A simple editor for ASCII box-and-arrow charts DIR Log DIR Files DIR Refs DIR Tags DIR README DIR LICENSE --- DIR commit 8e3d23b921d9bbcb7c53017bacff8a3050a34b55 DIR parent 3d53fcefe3ca3e6f25d1731c2caa0c1c0e676453 HTML Author: KatolaZ <katolaz@freaknet.org> Date: Sat, 27 Jul 2019 06:57:12 +0100 reorganise code Diffstat: M Makefile | 6 +++--- M TODO | 6 +++++- A arg.h | 37 +++++++++++++++++++++++++++++++ M config.h | 14 +++++++++----- A draw.c | 324 ++++++++++++++++++++++++++++++ A files.c | 73 +++++++++++++++++++++++++++++++ D gramscii.c | 1057 ------------------------------- A gramscii.h | 138 ++++++++++++++++++++++++++++++ A main.c | 175 +++++++++++++++++++++++++++++++ A screen.c | 419 +++++++++++++++++++++++++++++++ 10 files changed, 1183 insertions(+), 1066 deletions(-) --- DIR diff --git a/Makefile b/Makefile @@ -3,8 +3,8 @@ include config.mk -SRC = gramscii.c -INC = config.h +SRC = main.c draw.c screen.c files.c +INC = config.h gramscii.h all: options gramscii @@ -22,7 +22,7 @@ gramscii: ${SRC} ${INC} clean: @echo cleaning - @rm -f $(SRC:.c=) + @rm -f $(SRC:.c=) gramscii install: all @echo installing executable to ${DESTDIR}${BINDIR} DIR diff --git a/TODO b/TODO @@ -5,10 +5,13 @@ - use [ENTER] to exit from text insert - maybe move "text" mode to "t" - implement ellipse +- filled box (B) +- manage fill character (as for other styles) +- implement comment (#: ignore until the end of the line) + parse control characters + parse arrows (text-mode will allow movements as well) - (?) implement CTRL+G as abort (aside ESC) -- add crop command (c) +- add crop command (C) - (?) remove extra blanks until EOL when saving to file + visual selection - crop-to @@ -21,6 +24,7 @@ - allow scrolling (both vertical and horizontal) - catch SIGWINCH and react appropriately (after scrolling is enabled) +* reorganise code * change screen management (i.e., dynamic array of lines) * add action multiplier (e.g., "7h" moves left by 7 cols) * add scripting mode option ("-s"?) DIR diff --git a/arg.h b/arg.h @@ -0,0 +1,37 @@ +#ifndef ARG_H +#define ARG_H + +#define USED(x) ((void)(x)) + +extern char *argv0; + +#define ARGBEGIN for(argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][0] == '-'\ + && argv[0][1];\ + argc--, argv++) {\ + char _argc;\ + char **_argv;\ + if(argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + int i_;\ + for(i_ = 1, _argv = argv; argv[0][i_];\ + i_++) {\ + if(_argv != argv)\ + break;\ + _argc = argv[0][i_];\ + switch(_argc) + +#define ARGEND }\ + USED(_argc);\ + }\ + USED(argv);\ + USED(argc); + +#define EARGF(x) ((argv[1] == NULL)? ((x), abort(), (char *)0) :\ + (argc--, argv++, argv[0])) + +#endif + DIR diff --git a/config.h b/config.h @@ -1,18 +1,22 @@ /* This is part of `gramscii`` -- see COPYING for details */ +#ifndef __LOCAL_CONFIG_H__ +#define __LOCAL_CONFIG_H__ /* Config options */ /** MARKERS -- the first character is the default one **/ /* markers for horizontal lines */ -char hlines[] = {"-~=#*@._ "}; +static char hlines[] = {"-~=#*@._ "}; /* markers for vertical lines */ -char vlines[] = {"|H#*@:;i "}; +static char vlines[] = {"|H#*@:;i "}; /* markers for corners */ -char corners[] = {"+'H#*@.\"`"}; +static char corners[] = {"+'H#*@.\"`"}; /* markers for arrow start points */ -char st_marks[] = {"+o-|<>^v*"}; +static char st_marks[] = {"+o-|<>^v*"}; /* markers for arrow endpoints */ -char end_marks[] = {">+o-|<^v*"}; +static char end_marks[] = {">+o-|<^v*"}; /** LONG_STEP (movements through uppercase HJKL) **/ #define LONG_STEP 5 + +#endif DIR diff --git a/draw.c b/draw.c @@ -0,0 +1,324 @@ +#include <stdlib.h> + +#include "gramscii.h" +#include "config.h" + +/*** drawing-related functions ***/ + +/*** Lines and markers ***/ + +void toggle_hline(){ + + cur_hl = (cur_hl + 1) % hlines_sz; + line_h = hlines[cur_hl]; + +} + +void toggle_corner(){ + + cur_corn = (cur_corn + 1 ) % corners_sz; + corner = corners[cur_corn]; + +} + +void toggle_vline(){ + + cur_vl = (cur_vl + 1) % vlines_sz; + line_v = vlines[cur_vl]; + +} + +void toggle_st_mark(){ + + cur_start = (cur_start + 1 ) % stmarks_sz; + mark_st = st_marks[cur_start]; +} + +void toggle_end_mark(){ + + cur_end = (cur_end+ 1 ) % endmarks_sz; + mark_end = end_marks[cur_end]; +} + +int change_style(char c){ + switch(c){ + case '-': + toggle_hline(); + break; + case '|': + toggle_vline(); + break; + case '+': + toggle_corner(); + break; + case '<': + toggle_st_mark(); + break; + case '>': + toggle_end_mark(); + break; + case '.': + reset_styles(); + break; + default: + return 0; + } + return c; +} + + + + +/***** text, box, arrows *****/ + +void get_text(FILE *fc){ + char c; + int orig_x = x; + + redraw(); + while((c=fgetc(fc))!=EOF && c != 27){ + if(c=='\n'){ + set_cur(BG); + y += 1; + x = orig_x; + } + else { + set_cur(c); + update_current(); + modified = 1; + x += 1; + if (x >= WIDTH) + x = orig_x; + } + check_bound(); + status_bar(); + show_cursor(); + } + mode=MOVE; +} + +void draw_box(int x1, int y1, int fix){ + + int xmin, ymin, xmax, ymax; + int i; + void (*f)(int, int, char); + + if (fix == FIX) + f = set_xy; + else + f = draw_xy; + + xmin = MIN(x, x1); + xmax = MAX(x, x1); + ymin = MIN(y, y1); + ymax = MAX(y, y1); + + for(i=xmin+1; i<=xmax; i++){ + f(i, ymin, line_h); + f(i, ymax, line_h); + } + for(i=ymin+1; i<=ymax; i++){ + f(xmin, i, line_v); + f(xmax, i, line_v); + } + f(xmin, ymin, corner); + f(xmin, ymax, corner); + f(xmax, ymin, corner); + f(xmax, ymax, corner); + show_cursor(); +} + +void get_box(FILE *fc){ + char c; + int orig_x=x, orig_y=y; + redraw(); + step = 1; + draw_box(x,y,NOFIX); + while((c=fgetc(fc))!=EOF && c != 27 && c!= 'b' && c != '\n'){ + if (change_style(c)) + goto update_box; + if (!move_around(c, fc)) + continue; + check_bound(); + redraw(); + step = 1; +update_box: + draw_box(orig_x, orig_y, NOFIX); + status_bar(); + show_cursor(); + } + if (c == 'b' || c == '\n'){ + draw_box(orig_x, orig_y, FIX); + modified = 1; + } + redraw(); + mode = MOVE; +} + +void draw_arrow(int x, int y, char *a, int a_len, int fix){ + + int i, j, cur_dir; + char line; + void (*f)(int, int, char); + + + if (fix == FIX) + f = set_xy; + else + f = draw_xy; + + f(x,y,mark_st); + if (!a_len){ + show_cursor(); + return; + } + cur_dir=DIR_N; + for (i=0; i<a_len; i+=2){ + if (i>0) { + /* If we are switching between horizontal and vertical, put a "corner" */ + if (((cur_dir & DIR_HOR) && (a[i] & DIR_VER)) || + ((cur_dir & DIR_VER) && (a[i] & DIR_HOR))){ + f(x,y,corner); + show_cursor(); + } + } + for(j=0; j<a[i+1]; j++){ + line = (a[i] & DIR_L) || (a[i] & DIR_R) ? line_h : line_v; + x += progr_x(a[i]); + y += progr_y(a[i]); + f(x, y, line); + } + /* f(x,y,mark_end);*/ + cur_dir = a[i]; + } + if (autoend){ + if (cur_dir != DIR_N) + f(x,y, get_mark(cur_dir)); + } + else + f(x,y,mark_end); + show_cursor(); +} + +void get_arrow(FILE *fc){ + + char c; + int orig_x=x, orig_y=y, arrow_len; + static char *arrow = NULL; + static int arrow_sz; + + if (!arrow){ + arrow_sz = 100; + arrow = malloc(arrow_sz * sizeof(char)); + } + arrow_len = 0; + dir = DIR_N; + + redraw(); + step = 1; + draw_arrow(x,y, arrow, 0, NOFIX); + while((c=fgetc(fc))!=EOF && c != 27 && c!= 'a' && c != '\n'){ + if (change_style(c)) + goto update_arrow; + if (!move_around(c, fc)) + continue; + check_bound(); + /* FIXME: if we are out of bound, do nothing? */ + if (arrow_len == arrow_sz){ + arrow_sz *=2; + arrow = realloc(arrow, arrow_sz * sizeof(char)); + } + arrow[arrow_len++] = dir; + arrow[arrow_len++] = step; + redraw(); + step = 1; +update_arrow: + draw_arrow(orig_x, orig_y, arrow, arrow_len, NOFIX); + status_bar(); + show_cursor(); + } + if (c == 'a' || c == '\n'){ + draw_arrow(orig_x, orig_y, arrow, arrow_len, FIX); + modified = 1; + } + redraw(); + mode = MOVE; +} + + +void do_erase(int x1, int y1){ + int i; + switch(dir){ + case DIR_R: + for(i=x1; i<=x; i++) set_xy(i,y,BG); + break; + case DIR_L: + for(i=x1; i>=x; i--) set_xy(i,y,BG); + break; + case DIR_U: + for(i=y1; i>=y; i--) set_xy(x,i,BG); + break; + case DIR_D: + for(i=y1; i<=y; i++) set_xy(x,i,BG); + break; + } +} + + +void erase(FILE *fc){ + char c; + int orig_x = x, orig_y = y; + status_bar(); + show_cursor(); + while((c=fgetc(fc))!=EOF && c!=27 && c!= 'x' && c != '\n'){ + if (!move_around(c, fc)) continue; + check_bound(); + do_erase(orig_x, orig_y); + step = 1; + modified = 1; + orig_x = x; + orig_y = y; + redraw(); + status_bar(); + show_cursor(); + } + mode = MOVE; +} + + +/*** Visual ***/ + + +void visual_box(FILE *fc){ + int orig_x =x, orig_y = y; + char c, f = BG; + + redraw(); + step = 1; + set_video(VIDEO_REV); + draw_box(x,y,NOFIX); + while((c=fgetc(fc))!=EOF && c != 27 && c!= 'v' && c != '\n'){ + if (!move_around(c, fc)) switch(c){ + case 'f':/* fill */ + f = get_key(fc, "fill char: "); /** FALLTHROUGH **/ + case 'x':/* erase */ + erase_box(orig_x, orig_y, f); + modified = 1; + goto vis_exit; + break; + } + check_bound(); + set_video(VIDEO_NRM); + redraw(); + step = 1; + f = BG; + set_video(VIDEO_REV); + draw_box(orig_x, orig_y, NOFIX); + status_bar(); + show_cursor(); + } +vis_exit: + set_video(VIDEO_NRM); + redraw(); + mode = MOVE; +} DIR diff --git a/files.c b/files.c @@ -0,0 +1,73 @@ +#include <stdio.h> +#include <string.h> +#include "gramscii.h" + + +/*** File management ***/ + +void write_file(FILE *fc){ + FILE *fout; + int i; + + if (!fname[0] || force_new){ + get_string(fc, "Write to: ", fname, 255); + if ((fout=fopen(fname, "r"))!=NULL){ + if (!is_yes(get_key(fc,"File exists. Overwrite [y/n]?")) ){ + fclose(fout); + return; + } + fclose(fout); + } + } + if((fout=fopen(fname, "w"))==NULL){ + get_key(fc, "Error opening file."); + return; + } + for (i=0; i<HEIGHT; i++){ + fprintf(fout, "%s\n", screen[i].s); + } + fclose(fout); + modified = 0; + get_key(fc, "File saved."); +} + +void check_modified(FILE *fc){ + + if (modified){ + if (!is_yes(get_key(fc, "Unsaved changes. Write to file [y/n]?")) ){ + return; + } + write_file(fc); + } +} + +void load_file(FILE *fc){ + + char newfname[256]; + FILE *fin; + int i; + + get_string(fc, "Load file: ", newfname, 255); + if ((fin=fopen(newfname, "r")) != NULL){ + i = 0; + while((fgets(screen[i].s, WIDTH+2, fin)) != NULL && i<HEIGHT) + screen[i++].s[WIDTH-1]='\0'; + for(;i<HEIGHT; i++){ + erase_line(screen[i].s); + } + fclose(fin); + } + strcpy(fname, newfname); + modified=0; + redraw(); +} + +void new_file(FILE *fc){ + check_modified(fc); + erase_screen(); + go_to(HOME); + redraw(); + fname[0] = '\0'; + modified=0; +} + DIR diff --git a/gramscii.c b/gramscii.c @@ -1,1057 +0,0 @@ -/* -* -* gramscii: a simple editor for ASCII box-and-arrow charts -* -* Copyright (c) 2019 Vincenzo "KatolaZ" Nicosia <katolaz@freaknet.org> -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. Please see the attached file COPYING. -* Otherwise, please visit <https://www.gnu.org/licenses/>. -* -*/ - -#include <stdio.h> -#include <stdlib.h> -#include <termios.h> -#include <unistd.h> -#include <signal.h> -#include <string.h> -#include <sys/ioctl.h> -#include <ctype.h> - -#include "config.h" - -#include "arg.h" - -typedef struct{ - int sz; - int lst; - char *s; -} line_t; - - -#define MOVE 0x00 -#define BOX 0x01 -#define ARROW 0x02 -#define TEXT 0x04 -#define DEL 0x08 -#define VIS 0x10 - -#define DIR_N 0x00 -#define DIR_R 0x01 -#define DIR_U 0x02 -#define DIR_D 0x04 -#define DIR_L 0x08 - -#define DIR_HOR (DIR_R | DIR_L) -#define DIR_VER (DIR_D | DIR_U) - - -#define NOFIX 0x0 -#define FIX 0x1 - -#define BG ' ' -#define PTR '+' -#define UND '_' -#define ARR_L '<' -#define ARR_R '>' -#define ARR_U '^' -#define ARR_D 'v' - -#define HOME 0x01 -#define END 0x02 -#define MIDDLE 0x04 - -#define VIDEO_NRM 0 -#define VIDEO_REV 7 - -#define MIN(x,y) (x) < (y) ? (x) : (y) -#define MAX(x,y) (x) > (y) ? (x) : (y) - -#define DEBUG 1 - -line_t *screen; -int num_lines; -int WIDTH, HEIGHT; - -int state; -int dir; -int x; -int y; -int step; -int mult; -int force_new; -char cursor; -char corner; - -int hlines_sz= sizeof(hlines) -1; -int vlines_sz= sizeof(vlines) -1; -int corners_sz = sizeof(corners) -1; -int stmarks_sz = sizeof(st_marks) - 1; -int endmarks_sz = sizeof(st_marks) - 1; - -int cur_hl, cur_vl, cur_corn, cur_start, cur_end; -char line_h; -char line_v; -char mark_st; -char mark_end; - -char modified; -char fname[256]; - -char visual; -char silent; -char autoend; - -char *argv0; - -struct termios t1, t2, t3; - - -void dump_lines(){ - int i; - for (i=0; i<HEIGHT; i++){ - printf("%s\n", screen[i].s); - } -} - -void cleanup(int s){ - - if (!silent) - printf("\033[;H\033[2J"); - if (silent) - dump_lines(); - tcsetattr(0, TCSANOW, &t1); - fflush(stdout); - exit(0); -} - -void exit_cleanup(void){ - cleanup(0); -} - - -/*** Status bar ***/ - -char* state_str(){ - switch(state){ - case MOVE: - return "mov"; - case TEXT: - return "txt"; - case BOX: - return "box"; - case ARROW: - return "arr"; - case DEL: - return "del"; - case VIS: - return "vis"; - default: - return "ERR"; - } - return "ERR"; -} - -char get_mark(char dir){ - switch(dir){ - case DIR_U: - return '^'; - case DIR_D: - return 'v'; - case DIR_L: - return '<'; - case DIR_R: - return '>'; - } - return '>'; -} - - -void status_bar(){ - - if (silent) - return; - printf("\033[%d;1f\033[7m", HEIGHT+1); - printf("%*s", WIDTH-1, ""); - printf("\033[%d;1f\033[7m", HEIGHT+1); - printf(" x:%3d y:%3d -- MODE:%4s HL:%c VL:%c CN:%c SP:%c EP:%c %10s", - x, y, state_str(), line_h, line_v, corner, mark_st, mark_end, ""); - if (!modified) - printf(" [%s]", fname ); - else - printf(" *%s*", fname ); -#ifdef DEBUG - printf(" '%d' ", screen[y].s[x]); -#endif - printf("\033[0m"); - fflush(stdout); -} - -char get_key(FILE *fc, char *msg){ - - if (silent) - return 0; - printf("\033[%d;1f\033[7m", HEIGHT+1); - printf("%*s", WIDTH, ""); - printf("\033[%d;1f\033[7m", HEIGHT+1); - printf("%s", msg); - fflush(stdout); - printf("\033[0m"); - fflush(stdout); - return fgetc(fc); -} - -void get_string(FILE *fc, char *msg, char *s, int sz){ - - if (!silent){ - printf("\033[%d;1f\033[7m", HEIGHT+1); - printf("%*s", WIDTH, ""); - printf("\033[%d;1f\033[7m", HEIGHT+1); - - /* We must activate echo now */ - t3 = t2; - t3.c_lflag |= (ECHO | ICANON); - tcsetattr(0, TCSANOW, &t3); - printf("%s", msg); - printf("\033[0m"); - } - fgets(s, sz, fc); - s[strlen(s)-1] = '\0'; - tcsetattr(0, TCSANOW, &t2); - if (!silent) - fflush(stdout); -} - -int is_yes(char c){ - return c=='y' ? 1 : c == 'Y'? 1 : 0; -} - -/*** Screen management ***/ - -void show_cursor(){ - if (silent) - return; - printf("\033[%d;%df", y+1, x+1); - fflush(stdout); -} - - -void set_xy(int _x, int _y, char c){ - line_t *tmp; - if (_y >= num_lines){ - tmp = realloc(screen, (_y + LONG_STEP)* sizeof(line_t)); - if (tmp == NULL){ - fprintf(stderr, "Unable to allocate memory for more lines"); - exit(1); - } - else while ( num_lines < _y + LONG_STEP){ - screen[num_lines].sz = WIDTH+1; - screen[num_lines].s = malloc((screen[num_lines].sz) * sizeof(char)); - if (screen[num_lines].s == NULL){ - perror("allocating screen[num_lines].s"); - exit(1); - } - memset(screen[num_lines].s, BG, screen[num_lines].sz); - screen[num_lines].lst = 0; - screen[num_lines].s[screen[num_lines].lst+1]='\0'; - num_lines ++; - } - } - if (screen[_y].sz < _x + 2){ - screen[_y].sz = (_x +2) * 2; - screen[_y].s = realloc(screen[_y].s, screen[_y].sz * sizeof(char)); - } - while (screen[_y].lst<_x){ - screen[_y].lst ++; - screen[_y].s[screen[_y].lst] = BG; - } - screen[_y].s[_x] = c; - if (_x == screen[_y].lst) - screen[_y].s[_x+1] = '\0'; -} - -void set_cur(char c){ - set_xy(x, y, c); -} - -void draw_xy(int x, int y, char c){ - /* FIXME: check if x and y are valid!!!! */ - if (silent) - return; - printf("\033[%d;%df",y+1,x+1); - putchar(c); - fflush(stdout); -} - -void update_current(){ - if (silent) - return; - printf("\033[%d'%df",y+1,x+1); - putchar(screen[y].s[x]); - fflush(stdout); -} - -void erase_line(char *s){ - while(*s){ - *s = BG; - s++; - } -} - -void erase_box(int x1, int y1, char c){ - int x_incr, y_incr, i; - - x_incr = x1 < x? +1: -1; - y_incr = y1 < y? +1: -1; - do{ - i = y1; - do{ - set_xy(x1, i, c); - } while(i != y && (1 | (i += y_incr))); - } while(x1 != x && (1 | (x1 += x_incr))); - -} - -void erase_screen(){ - int i; - for(i=0;i<HEIGHT; i++) - erase_line(screen[i].s); -} - -void check_bound(){ - if (x<0) x=0; - else if (x>=WIDTH) x = WIDTH-1; - if (y<0) y=0; - else if (y>=HEIGHT) y = HEIGHT -1; -} - -void reset_styles(){ - - cur_corn = 0; - corner = corners[0]; - cur_hl = cur_vl = 0; - cur_start = cur_end = 0; - line_h = hlines[cur_hl]; - line_v = vlines[cur_vl]; - mark_st = st_marks[cur_start]; - mark_end = end_marks[cur_end]; -} - -void redraw(){ - int i; - - if (silent) - return; - printf("\033[2J\033[1;1H"); - for (i=0;i<HEIGHT;i++){ - fprintf(stdout,"%s\n",screen[i].s); - } - status_bar(); - show_cursor(); -} - -void go_to(int where){ - switch(where){ - case HOME: - x = y = 0; - break; - case END: - x = WIDTH-1; - y = HEIGHT-1; - break; - case MIDDLE: - x = WIDTH/2; - y = HEIGHT/2; - break; - } - check_bound(); - show_cursor(); -} - -void handle_goto(){ - char c; - c=getchar(); - switch(c){ - case 'h': - dir = DIR_L; - x = 0; - break; - case 'l': - dir = DIR_R; - x = WIDTH - 1; - break; - case 'j': - dir = DIR_D; - y = HEIGHT - 1; - break; - case 'k': - dir = DIR_U; - y = 0; - break; - case 'g': - dir = DIR_N; - go_to(HOME); - break; - case 'G': - dir = DIR_N; - go_to(END); - break; - case 'm': - dir = DIR_N; - go_to(MIDDLE); - break; - } - check_bound(); - show_cursor(); -} - - -int get_escape(FILE *fc){ - char c[4]; - - c[0] = fgetc(fc); - if (c[0] == '['){ - c[1] = fgetc(fc); - switch(c[1]){ - case 'D': - dir = DIR_L; - x -= step; - break; - case 'B': - dir = DIR_D; - y += step; - break; - case 'A': - dir = DIR_U; - y -= step; - break; - case 'C': - dir = DIR_R; - x += step; - break; - } - return 1; - } - else{ - ungetc(c[0], fc); - return 0; - } - -} - - -int move_around(char c, FILE *fc){ - - if (isdigit(c)){ - if (mult) - mult *=10; - mult += c - '0'; - return 0; - } - switch(c){ - case 27: /* control sequence? */ - c = get_escape(fc); - break; - case 'H': step = LONG_STEP;/** FALLTHROUGH **/ - case 'h': - dir = DIR_L; - if (mult) - step *= mult; - x -= step; - break; - case 'J': step = LONG_STEP;/** FALLTHROUGH **/ - case 'j': - if (mult) - step *= mult; - dir = DIR_D; - y += step; - break; - case 'K': step = LONG_STEP;/** FALLTHROUGH **/ - case 'k': - if (mult) - step *= mult; - dir = DIR_U; - y -= step; - break; - case 'L': step = LONG_STEP;/** FALLTHROUGH **/ - case 'l': - if (mult) - step *= mult; - dir = DIR_R; - x += step; - break; - case 'g': - handle_goto(); - break; - default: - return 0; - } - mult = 0; - return c; -} - -int progr_x(int dir){ - return dir == DIR_L ? -1 : dir == DIR_R ? 1: 0; -} - - -int progr_y(int dir){ - return dir == DIR_U ? -1 : dir == DIR_D ? 1: 0; -} - -void set_video(int v){ - if (silent) - return; - printf("\033[%dm", v); - fflush(stdout); -} - -/*** Lines and markers ***/ - -void toggle_hline(){ - - cur_hl = (cur_hl + 1) % hlines_sz; - line_h = hlines[cur_hl]; - -} - -void toggle_corner(){ - - cur_corn = (cur_corn + 1 ) % corners_sz; - corner = corners[cur_corn]; - -} - -void toggle_vline(){ - - cur_vl = (cur_vl + 1) % vlines_sz; - line_v = vlines[cur_vl]; - -} - -void toggle_st_mark(){ - - cur_start = (cur_start + 1 ) % stmarks_sz; - mark_st = st_marks[cur_start]; -} - -void toggle_end_mark(){ - - cur_end = (cur_end+ 1 ) % endmarks_sz; - mark_end = end_marks[cur_end]; -} - -int change_style(char c){ - switch(c){ - case '-': - toggle_hline(); - break; - case '|': - toggle_vline(); - break; - case '+': - toggle_corner(); - break; - case '<': - toggle_st_mark(); - break; - case '>': - toggle_end_mark(); - break; - case '.': - reset_styles(); - break; - default: - return 0; - } - return c; -} - - - - -/***** text, box, arrows *****/ - -void get_text(FILE *fc){ - char c; - int orig_x = x; - - redraw(); - while((c=fgetc(fc))!=EOF && c != 27){ - if(c=='\n'){ - set_cur(BG); - y += 1; - x = orig_x; - } - else { - set_cur(c); - update_current(); - modified = 1; - x += 1; - if (x >= WIDTH) - x = orig_x; - } - check_bound(); - status_bar(); - show_cursor(); - } - state=MOVE; -} - -void draw_box(int x1, int y1, int fix){ - - int xmin, ymin, xmax, ymax; - int i; - void (*f)(int, int, char); - - if (fix == FIX) - f = set_xy; - else - f = draw_xy; - - xmin = MIN(x, x1); - xmax = MAX(x, x1); - ymin = MIN(y, y1); - ymax = MAX(y, y1); - - for(i=xmin+1; i<=xmax; i++){ - f(i, ymin, line_h); - f(i, ymax, line_h); - } - for(i=ymin+1; i<=ymax; i++){ - f(xmin, i, line_v); - f(xmax, i, line_v); - } - f(xmin, ymin, corner); - f(xmin, ymax, corner); - f(xmax, ymin, corner); - f(xmax, ymax, corner); - show_cursor(); -} - -void get_box(FILE *fc){ - char c; - int orig_x=x, orig_y=y; - redraw(); - step = 1; - draw_box(x,y,NOFIX); - while((c=fgetc(fc))!=EOF && c != 27 && c!= 'b' && c != '\n'){ - if (change_style(c)) - goto update_box; - if (!move_around(c, fc)) - continue; - check_bound(); - redraw(); - step = 1; -update_box: - draw_box(orig_x, orig_y, NOFIX); - status_bar(); - show_cursor(); - } - if (c == 'b' || c == '\n'){ - draw_box(orig_x, orig_y, FIX); - modified = 1; - } - redraw(); - state = MOVE; -} - -void draw_arrow(int x, int y, char *a, int a_len, int fix){ - - int i, j, cur_dir; - char line; - void (*f)(int, int, char); - - - if (fix == FIX) - f = set_xy; - else - f = draw_xy; - - f(x,y,mark_st); - if (!a_len){ - show_cursor(); - return; - } - cur_dir=DIR_N; - for (i=0; i<a_len; i+=2){ - if (i>0) { - /* If we are switching between horizontal and vertical, put a "corner" */ - if (((cur_dir & DIR_HOR) && (a[i] & DIR_VER)) || - ((cur_dir & DIR_VER) && (a[i] & DIR_HOR))){ - f(x,y,corner); - show_cursor(); - } - } - for(j=0; j<a[i+1]; j++){ - line = (a[i] & DIR_L) || (a[i] & DIR_R) ? line_h : line_v; - x += progr_x(a[i]); - y += progr_y(a[i]); - f(x, y, line); - } - /* f(x,y,mark_end);*/ - cur_dir = a[i]; - } - if (autoend){ - if (cur_dir != DIR_N) - f(x,y, get_mark(cur_dir)); - } - else - f(x,y,mark_end); - show_cursor(); -} - -void get_arrow(FILE *fc){ - - char c; - int orig_x=x, orig_y=y, arrow_len; - static char *arrow = NULL; - static int arrow_sz; - - if (!arrow){ - arrow_sz = 100; - arrow = malloc(arrow_sz * sizeof(char)); - } - arrow_len = 0; - dir = DIR_N; - - redraw(); - step = 1; - draw_arrow(x,y, arrow, 0, NOFIX); - while((c=fgetc(fc))!=EOF && c != 27 && c!= 'a' && c != '\n'){ - if (change_style(c)) - goto update_arrow; - if (!move_around(c, fc)) - continue; - check_bound(); - /* FIXME: if we are out of bound, do nothing? */ - if (arrow_len == arrow_sz){ - arrow_sz *=2; - arrow = realloc(arrow, arrow_sz * sizeof(char)); - } - arrow[arrow_len++] = dir; - arrow[arrow_len++] = step; - redraw(); - step = 1; -update_arrow: - draw_arrow(orig_x, orig_y, arrow, arrow_len, NOFIX); - status_bar(); - show_cursor(); - } - if (c == 'a' || c == '\n'){ - draw_arrow(orig_x, orig_y, arrow, arrow_len, FIX); - modified = 1; - } - redraw(); - state = MOVE; -} - - -void do_delete(int x1, int y1){ - int i; - switch(dir){ - case DIR_R: - for(i=x1; i<=x; i++) set_xy(i,y,BG); - break; - case DIR_L: - for(i=x1; i>=x; i--) set_xy(i,y,BG); - break; - case DIR_U: - for(i=y1; i>=y; i--) set_xy(x,i,BG); - break; - case DIR_D: - for(i=y1; i<=y; i++) set_xy(x,i,BG); - break; - } -} - - -void delete(FILE *fc){ - char c; - int orig_x = x, orig_y = y; - status_bar(); - show_cursor(); - while((c=fgetc(fc))!=EOF && c!=27 && c!= 'x' && c != '\n'){ - if (!move_around(c, fc)) continue; - check_bound(); - do_delete(orig_x, orig_y); - step = 1; - modified = 1; - orig_x = x; - orig_y = y; - redraw(); - status_bar(); - show_cursor(); - } - state = MOVE; -} - -/*** File management ***/ - -void write_file(FILE *fc){ - FILE *fout; - int i; - - if (!fname[0] || force_new){ - get_string(fc, "Write to: ", fname, 255); - if ((fout=fopen(fname, "r"))!=NULL){ - if (!is_yes(get_key(fc,"File exists. Overwrite [y/n]?")) ){ - fclose(fout); - return; - } - fclose(fout); - } - } - if((fout=fopen(fname, "w"))==NULL){ - get_key(fc, "Error opening file."); - return; - } - for (i=0; i<HEIGHT; i++){ - fprintf(fout, "%s\n", screen[i].s); - } - fclose(fout); - modified = 0; - get_key(fc, "File saved."); -} - -void check_modified(FILE *fc){ - - if (modified){ - if (!is_yes(get_key(fc, "Unsaved changes. Write to file [y/n]?")) ){ - return; - } - write_file(fc); - } -} - -void load_file(FILE *fc){ - - char newfname[256]; - FILE *fin; - int i; - - get_string(fc, "Load file: ", newfname, 255); - if ((fin=fopen(newfname, "r")) != NULL){ - i = 0; - while((fgets(screen[i].s, WIDTH+2, fin)) != NULL && i<HEIGHT) - screen[i++].s[WIDTH-1]='\0'; - for(;i<HEIGHT; i++){ - erase_line(screen[i].s); - } - fclose(fin); - } - strcpy(fname, newfname); - modified=0; - redraw(); -} - -void new_file(FILE *fc){ - check_modified(fc); - erase_screen(); - go_to(HOME); - redraw(); - fname[0] = '\0'; - modified=0; -} - -/*** Visual ***/ - - -void visual_box(FILE *fc){ - int orig_x =x, orig_y = y; - char c, f = BG; - - redraw(); - step = 1; - set_video(VIDEO_REV); - draw_box(x,y,NOFIX); - while((c=fgetc(fc))!=EOF && c != 27 && c!= 'v' && c != '\n'){ - if (!move_around(c, fc)) switch(c){ - case 'f':/* fill */ - f = get_key(fc, "fill char: "); /** FALLTHROUGH **/ - case 'x':/* erase */ - erase_box(orig_x, orig_y, f); - modified = 1; - goto vis_exit; - break; - } - check_bound(); - set_video(VIDEO_NRM); - redraw(); - step = 1; - f = BG; - set_video(VIDEO_REV); - draw_box(orig_x, orig_y, NOFIX); - status_bar(); - show_cursor(); - } -vis_exit: - set_video(VIDEO_NRM); - redraw(); - state = MOVE; -} - -/*** Initialisation ***/ - -void init_screen(){ - int i; - struct winsize wsz; - - if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &wsz)){ - WIDTH=wsz.ws_col - 2; - HEIGHT=wsz.ws_row - 1; - } - else { - WIDTH=80; - HEIGHT=24; - } - screen = malloc(HEIGHT * sizeof(line_t)); - num_lines = HEIGHT; - if (screen == NULL){ - perror("allocating screen"); - exit(1); - } - for (i=0; i<HEIGHT; i++){ - screen[i].sz = WIDTH+1; - screen[i].s = malloc((screen[i].sz) * sizeof(char)); - if (screen[i].s == NULL){ - perror("allocating screen[i].s"); - exit(1); - } - memset(screen[i].s, BG, screen[i].sz); - screen[i].lst = 0; - screen[i].s[screen[i].lst+1]='\0'; - } - reset_styles(); -} - -void init(){ - - signal(SIGHUP, cleanup); - signal(SIGINT, cleanup); - signal(SIGTERM, cleanup); - signal(SIGQUIT, cleanup); - atexit(exit_cleanup); - - tcgetattr(0, &t1); - t2 = t1; - t2.c_lflag &= ~(ICANON | ECHO); - tcsetattr(0, TCSANOW, &t2); - - init_screen(); - x = 0; - y = 0; - modified = 0; - fname[0] = '\0'; - redraw(); -} - - -/*** Commands ***/ - -void commands(FILE *fc){ - - char c; - while((c=fgetc(fc))!=EOF){ - if (!change_style(c) && !move_around(c, fc)){ - switch(c){ - case 'i': - state = TEXT; - get_text(fc); - break; - case 'R': - redraw(); - break; - case 'b': - state = BOX; - get_box(fc); - break; - case 'A': autoend=1; - case 'a': - state = ARROW; - get_arrow(fc); - autoend = 0; - break; - case 'W': - force_new = 1;/** FALLTHROUGH **/ - case 'w': - write_file(fc); - break; - case 'e': - check_modified(fc);/** FALLTHROUGH **/ - case 'E': - load_file(fc); - break; - case 'N': - new_file(fc); - break; - case 'x': - state = DEL; - delete(fc); - break; - case 'v': - state = VIS; - visual_box(fc); - break; - case 'q': - check_modified(fc);/** FALLTHROUGH **/ - case 'Q': - exit(0); - break; - } - } - check_bound(); - status_bar(); - show_cursor(); - step = 1; - force_new = 0; - } - -} - -void usage(){ - fprintf(stderr, "Usage: %s [-s] [-h] [file ...]\n", argv0); - exit(1); -} - - -int main(int argc, char *argv[]){ - FILE *fc; - - ARGBEGIN { - case 's': - silent = 1; - break; - case 'h': /* FALLTHROUGH */ - default: - usage(); - } ARGEND; - - init(); - while (argc){ - fc = fopen(argv[0], "r"); - if (fc == NULL){ - fprintf(stderr, "Error opening file %s\n", argv[0]); - } - else { - commands(fc); - fclose(fc); - redraw(); - } - argv++; - argc--; - } - commands(stdin); - return 0; -} DIR diff --git a/gramscii.h b/gramscii.h @@ -0,0 +1,138 @@ +#ifndef __GRAMSCII_H__ +#define __GRAMSCII_H__ + +#include <stdio.h> +#include <termios.h> +#include <unistd.h> + +/** types **/ + +typedef struct{ + int sz; + int lst; + char *s; +} line_t; + +/** constants **/ + +#define MOVE 0x00 +#define BOX 0x01 +#define ARROW 0x02 +#define TEXT 0x04 +#define DEL 0x08 +#define VIS 0x10 + +#define DIR_N 0x00 +#define DIR_R 0x01 +#define DIR_U 0x02 +#define DIR_D 0x04 +#define DIR_L 0x08 + +#define DIR_HOR (DIR_R | DIR_L) +#define DIR_VER (DIR_D | DIR_U) + + +#define NOFIX 0x0 +#define FIX 0x1 + +#define BG ' ' +#define PTR '+' +#define UND '_' +#define ARR_L '<' +#define ARR_R '>' +#define ARR_U '^' +#define ARR_D 'v' + +#define HOME 0x01 +#define END 0x02 +#define MIDDLE 0x04 + +#define VIDEO_NRM 0 +#define VIDEO_REV 7 + + +/** MACROS **/ + +#define MIN(x,y) (x) < (y) ? (x) : (y) +#define MAX(x,y) (x) > (y) ? (x) : (y) + +#define progr_x(d) ((d) == DIR_L ? -1 : (d) == DIR_R ? 1 : 0) +#define progr_y(d) ((d) == DIR_U ? -1 : (d) == DIR_D ? 1 : 0) + +/* #define DEBUG 1 */ + +/** global variables **/ + +line_t *screen; +int num_lines; +int WIDTH, HEIGHT; + +int mode; +int dir; +int x; +int y; +int step; +int mult; +int force_new; +char cursor; +char corner; + +int hlines_sz; +int vlines_sz; +int corners_sz; +int stmarks_sz; +int endmarks_sz; + +int cur_hl, cur_vl, cur_corn, cur_start, cur_end; +char line_h; +char line_v; +char mark_st; +char mark_end; + +char modified; +char fname[256]; + +char visual; +char silent; +char autoend; + +struct termios t1, t2, t3; + +/** screen-related functions **/ +void reset_styles(); +void redraw(); +int move_around(char c, FILE *fc); +void check_bound(); +void status_bar(); +void show_cursor(); +void set_cur(char c); +void update_current(); +void set_xy(int _x, int _y, char c); +void draw_xy(int x, int y, char c); +char get_mark(char dir); +void set_video(int v); +char get_key(FILE *fc, char *msg); +void get_string(FILE *fc, char *msg, char *s, int sz); +void erase_box(int x1, int y1, char c); +int is_yes(char c); +void init_screen(); +void erase_line(char *s); +void erase_screen(); +void go_to(int where); + +/** drawing-related functions **/ +int change_style(char c); +void get_text(FILE *fc); +void get_box(FILE *fc); +void get_arrow(FILE *fc); +void erase(FILE *fc); +void visual_box(FILE *fc); + +/** file-related functions **/ +void write_file(FILE *fc); +void check_modified(FILE *fc); +void load_file(FILE *fc); +void new_file(FILE *fc); + + +#endif DIR diff --git a/main.c b/main.c @@ -0,0 +1,175 @@ +/* +* +* gramscii: a simple editor for ASCII box-and-arrow charts +* +* Copyright (c) 2019 Vincenzo "KatolaZ" Nicosia <katolaz@freaknet.org> +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. Please see the attached file COPYING. +* Otherwise, please visit <https://www.gnu.org/licenses/>. +* +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> + +#include "arg.h" +#include "gramscii.h" + + +char *argv0; + +void dump_lines(){ + int i; + for (i=0; i<HEIGHT; i++){ + printf("%s\n", screen[i].s); + } +} + +void cleanup(int s){ + + if (!silent) + printf("\033[;H\033[2J"); + else + dump_lines(); + tcsetattr(0, TCSANOW, &t1); + fflush(stdout); + exit(0); +} + +void exit_cleanup(void){ + cleanup(0); +} + +/*** Initialisation ***/ + +void init(){ + + signal(SIGHUP, cleanup); + signal(SIGINT, cleanup); + signal(SIGTERM, cleanup); + signal(SIGQUIT, cleanup); + atexit(exit_cleanup); + + tcgetattr(0, &t1); + t2 = t1; + t2.c_lflag &= ~(ICANON | ECHO); + tcsetattr(0, TCSANOW, &t2); + + init_screen(); + x = 0; + y = 0; + modified = 0; + fname[0] = '\0'; + redraw(); +} + + +/*** Commands ***/ + +void commands(FILE *fc){ + + char c; + while((c=fgetc(fc))!=EOF){ + if (!change_style(c) && !move_around(c, fc)){ + switch(c){ + case 'i': + mode = TEXT; + get_text(fc); + break; + case 'R': + redraw(); + break; + case 'b': + mode = BOX; + get_box(fc); + break; + case 'A': autoend=1; + case 'a': + mode = ARROW; + get_arrow(fc); + autoend = 0; + break; + case 'W': + force_new = 1;/** FALLTHROUGH **/ + case 'w': + write_file(fc); + break; + case 'e': + check_modified(fc);/** FALLTHROUGH **/ + case 'E': + load_file(fc); + break; + case 'N': + new_file(fc); + break; + case 'x': + mode = DEL; + erase(fc); + break; + case 'v': + mode = VIS; + visual_box(fc); + break; + case 'q': + check_modified(fc);/** FALLTHROUGH **/ + case 'Q': + exit(0); + break; + } + } + check_bound(); + status_bar(); + show_cursor(); + step = 1; + force_new = 0; + } + +} + +void usage(){ + fprintf(stderr, "Usage: %s [-s] [-h] [file ...]\n", argv0); + exit(1); +} + + +int main(int argc, char *argv[]){ + FILE *fc; + + ARGBEGIN { + case 's': + silent = 1; + break; + case 'h': /* FALLTHROUGH */ + default: + usage(); + } ARGEND; + + init(); + while (argc){ + fc = fopen(argv[0], "r"); + if (fc == NULL){ + fprintf(stderr, "Error opening file %s\n", argv[0]); + } + else { + commands(fc); + fclose(fc); + redraw(); + } + argv++; + argc--; + } + commands(stdin); + return 0; +} DIR diff --git a/screen.c b/screen.c @@ -0,0 +1,419 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <termios.h> +#include <sys/ioctl.h> + +#include "gramscii.h" +#include "config.h" + +/*** screen management functions ***/ + + +/*** Status bar ***/ + +char* mode_str(){ + switch(mode){ + case MOVE: + return "mov"; + case TEXT: + return "txt"; + case BOX: + return "box"; + case ARROW: + return "arr"; + case DEL: + return "del"; + case VIS: + return "vis"; + default: + return "ERR"; + } + return "ERR"; +} + +char get_mark(char dir){ + switch(dir){ + case DIR_U: + return '^'; + case DIR_D: + return 'v'; + case DIR_L: + return '<'; + case DIR_R: + return '>'; + } + return '>'; +} + + +void status_bar(){ + + if (silent) + return; + printf("\033[%d;1f\033[7m", HEIGHT+1); + printf("%*s", WIDTH-1, ""); + printf("\033[%d;1f\033[7m", HEIGHT+1); + printf(" x:%3d y:%3d -- MODE:%4s HL:%c VL:%c CN:%c SP:%c EP:%c %10s", + x, y, mode_str(), line_h, line_v, corner, mark_st, mark_end, ""); + if (!modified) + printf(" [%s]", fname ); + else + printf(" *%s*", fname ); +#ifdef DEBUG + printf(" '%d' ", screen[y].s[x]); +#endif + printf("\033[0m"); + fflush(stdout); +} + +char get_key(FILE *fc, char *msg){ + + if (silent) + return 0; + printf("\033[%d;1f\033[7m", HEIGHT+1); + printf("%*s", WIDTH, ""); + printf("\033[%d;1f\033[7m", HEIGHT+1); + printf("%s", msg); + fflush(stdout); + printf("\033[0m"); + fflush(stdout); + return fgetc(fc); +} + +void get_string(FILE *fc, char *msg, char *s, int sz){ + + if (!silent){ + printf("\033[%d;1f\033[7m", HEIGHT+1); + printf("%*s", WIDTH, ""); + printf("\033[%d;1f\033[7m", HEIGHT+1); + + /* We must activate echo now */ + t3 = t2; + t3.c_lflag |= (ECHO | ICANON); + tcsetattr(0, TCSANOW, &t3); + printf("%s", msg); + printf("\033[0m"); + } + fgets(s, sz, fc); + s[strlen(s)-1] = '\0'; + tcsetattr(0, TCSANOW, &t2); + if (!silent) + fflush(stdout); +} + +int is_yes(char c){ + return c=='y' ? 1 : c == 'Y'? 1 : 0; +} + +/*** Screen management ***/ + +void show_cursor(){ + if (silent) + return; + printf("\033[%d;%df", y+1, x+1); + fflush(stdout); +} + + +void set_xy(int _x, int _y, char c){ + line_t *tmp; + if (_y >= num_lines){ + tmp = realloc(screen, (_y + LONG_STEP)* sizeof(line_t)); + if (tmp == NULL){ + fprintf(stderr, "Unable to allocate memory for more lines"); + exit(1); + } + else while ( num_lines < _y + LONG_STEP){ + screen[num_lines].sz = WIDTH+1; + screen[num_lines].s = malloc((screen[num_lines].sz) * sizeof(char)); + if (screen[num_lines].s == NULL){ + perror("allocating screen[num_lines].s"); + exit(1); + } + memset(screen[num_lines].s, BG, screen[num_lines].sz); + screen[num_lines].lst = 0; + screen[num_lines].s[screen[num_lines].lst+1]='\0'; + num_lines ++; + } + } + if (screen[_y].sz < _x + 2){ + screen[_y].sz = (_x +2) * 2; + screen[_y].s = realloc(screen[_y].s, screen[_y].sz * sizeof(char)); + } + while (screen[_y].lst<_x){ + screen[_y].lst ++; + screen[_y].s[screen[_y].lst] = BG; + } + screen[_y].s[_x] = c; + if (_x == screen[_y].lst) + screen[_y].s[_x+1] = '\0'; +} + +void set_cur(char c){ + set_xy(x, y, c); +} + +void draw_xy(int x, int y, char c){ + /* FIXME: check if x and y are valid!!!! */ + if (silent) + return; + printf("\033[%d;%df",y+1,x+1); + putchar(c); + fflush(stdout); +} + +void update_current(){ + if (silent) + return; + printf("\033[%d'%df",y+1,x+1); + putchar(screen[y].s[x]); + fflush(stdout); +} + +void erase_line(char *s){ + while(*s){ + *s = BG; + s++; + } +} + +void erase_box(int x1, int y1, char c){ + int x_incr, y_incr, i; + + x_incr = x1 < x? +1: -1; + y_incr = y1 < y? +1: -1; + do{ + i = y1; + do{ + set_xy(x1, i, c); + } while(i != y && (1 | (i += y_incr))); + } while(x1 != x && (1 | (x1 += x_incr))); + +} + +void erase_screen(){ + int i; + for(i=0;i<HEIGHT; i++) + erase_line(screen[i].s); +} + +void check_bound(){ + if (x<0) x=0; + else if (x>=WIDTH) x = WIDTH-1; + if (y<0) y=0; + else if (y>=HEIGHT) y = HEIGHT -1; +} + +void reset_styles(){ + + cur_corn = 0; + corner = corners[0]; + cur_hl = cur_vl = 0; + cur_start = cur_end = 0; + line_h = hlines[cur_hl]; + line_v = vlines[cur_vl]; + mark_st = st_marks[cur_start]; + mark_end = end_marks[cur_end]; +} + +void redraw(){ + int i; + + if (silent) + return; + printf("\033[2J\033[1;1H"); + for (i=0;i<HEIGHT;i++){ + fprintf(stdout,"%s\n",screen[i].s); + } + status_bar(); + show_cursor(); +} + +void go_to(int where){ + switch(where){ + case HOME: + x = y = 0; + break; + case END: + x = WIDTH-1; + y = HEIGHT-1; + break; + case MIDDLE: + x = WIDTH/2; + y = HEIGHT/2; + break; + } + check_bound(); + show_cursor(); +} + +void handle_goto(){ + char c; + c=getchar(); + switch(c){ + case 'h': + dir = DIR_L; + x = 0; + break; + case 'l': + dir = DIR_R; + x = WIDTH - 1; + break; + case 'j': + dir = DIR_D; + y = HEIGHT - 1; + break; + case 'k': + dir = DIR_U; + y = 0; + break; + case 'g': + dir = DIR_N; + go_to(HOME); + break; + case 'G': + dir = DIR_N; + go_to(END); + break; + case 'm': + dir = DIR_N; + go_to(MIDDLE); + break; + } + check_bound(); + show_cursor(); +} + + +int get_escape(FILE *fc){ + char c[4]; + + c[0] = fgetc(fc); + if (c[0] == '['){ + c[1] = fgetc(fc); + switch(c[1]){ + case 'D': + dir = DIR_L; + x -= step; + break; + case 'B': + dir = DIR_D; + y += step; + break; + case 'A': + dir = DIR_U; + y -= step; + break; + case 'C': + dir = DIR_R; + x += step; + break; + } + return 1; + } + else{ + ungetc(c[0], fc); + return 0; + } + +} + + +int move_around(char c, FILE *fc){ + + if (isdigit(c)){ + if (mult) + mult *=10; + mult += c - '0'; + return 0; + } + switch(c){ + case 27: /* control sequence? */ + c = get_escape(fc); + break; + case 'H': step = LONG_STEP;/** FALLTHROUGH **/ + case 'h': + dir = DIR_L; + if (mult) + step *= mult; + x -= step; + break; + case 'J': step = LONG_STEP;/** FALLTHROUGH **/ + case 'j': + if (mult) + step *= mult; + dir = DIR_D; + y += step; + break; + case 'K': step = LONG_STEP;/** FALLTHROUGH **/ + case 'k': + if (mult) + step *= mult; + dir = DIR_U; + y -= step; + break; + case 'L': step = LONG_STEP;/** FALLTHROUGH **/ + case 'l': + if (mult) + step *= mult; + dir = DIR_R; + x += step; + break; + case 'g': + handle_goto(); + break; + default: + return 0; + } + mult = 0; + return c; +} + + +void set_video(int v){ + if (silent) + return; + printf("\033[%dm", v); + fflush(stdout); +} + + +void init_screen(){ + int i; + struct winsize wsz; + + if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &wsz)){ + WIDTH=wsz.ws_col - 2; + HEIGHT=wsz.ws_row - 1; + } + else { + WIDTH=80; + HEIGHT=24; + } + screen = malloc(HEIGHT * sizeof(line_t)); + num_lines = HEIGHT; + if (screen == NULL){ + perror("allocating screen"); + exit(1); + } + for (i=0; i<HEIGHT; i++){ + screen[i].sz = WIDTH+1; + screen[i].s = malloc((screen[i].sz) * sizeof(char)); + if (screen[i].s == NULL){ + perror("allocating screen[i].s"); + exit(1); + } + memset(screen[i].s, BG, screen[i].sz); + screen[i].lst = 0; + screen[i].s[screen[i].lst+1]='\0'; + } + hlines_sz= sizeof(hlines) -1; + vlines_sz= sizeof(vlines) -1; + corners_sz = sizeof(corners) -1; + stmarks_sz = sizeof(st_marks) - 1; + endmarks_sz = sizeof(st_marks) - 1; + reset_styles(); +} +