diff options
author | KatolaZ <katolaz@freaknet.org> | 2019-07-27 06:57:29 +0100 |
---|---|---|
committer | KatolaZ <katolaz@freaknet.org> | 2019-07-27 06:57:29 +0100 |
commit | f678684b470f02d9fed4818eb92c2a925380c428 (patch) | |
tree | fc26ae9768e6c72827b03e973dbf0479b0a0431d /gramscii.c | |
parent | 8b67bd6c5f4e175721b44d4733d1a5a6fc1a2561 (diff) | |
parent | 8e3d23b921d9bbcb7c53017bacff8a3050a34b55 (diff) |
Merge branch 'devel' into master
Diffstat (limited to 'gramscii.c')
-rw-r--r-- | gramscii.c | 1057 |
1 files changed, 0 insertions, 1057 deletions
diff --git a/gramscii.c b/gramscii.c deleted file mode 100644 index 4342da9..0000000 --- a/gramscii.c +++ /dev/null @@ -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; -} |