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
 (HTM) 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();
       +}
       +