URI: 
       yank buffer and initial copy/cut/paste support - gramscii - A simple editor for ASCII box-and-arrow charts
   DIR Log
   DIR Files
   DIR Refs
   DIR Tags
   DIR README
   DIR LICENSE
       ---
   DIR commit eebc645dee0d15871d6cc46f156d424cd916b191
   DIR parent a99759398841d86928c7ad4d8248f907765cbeb2
  HTML Author: KatolaZ <katolaz@freaknet.org>
       Date:   Tue, 30 Jul 2019 12:15:54 +0100
       
       yank buffer and initial copy/cut/paste support
       
       Diffstat:
         M Makefile                            |       2 +-
         M TODO                                |      11 +++++++----
         M draw.c                              |      11 +++++++++++
         M files.c                             |       6 +++---
         M gramscii.1                          |      20 ++++++++++++++++++--
         M gramscii.h                          |      36 ++++++++++++++++++++++++-------
         A lineset.c                           |     117 +++++++++++++++++++++++++++++++
         M main.c                              |      11 ++++-------
         M screen.c                            |     117 ++++++++++---------------------
       
       9 files changed, 227 insertions(+), 104 deletions(-)
       ---
   DIR diff --git a/Makefile b/Makefile
       @@ -3,7 +3,7 @@
        
        include config.mk
        
       -SRC = main.c draw.c screen.c files.c
       +SRC = main.c draw.c screen.c files.c lineset.c
        INC = config.h gramscii.h
        
        all: options gramscii
   DIR diff --git a/TODO b/TODO
       @@ -6,8 +6,8 @@
        - 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)
       +- (?) filled box (B)
       +- (?) manage filled box 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)
       @@ -15,15 +15,18 @@
        - (?) remove extra blanks until EOL when saving to file
        + visual selection
          - crop-to
       -  - yank/put
       +  * yank
          * fill
       -  * delete
       +  * cut 
        - undo (by storing lines changed across insert/remove operations)
        - manage special chars (DEL/CANC) during text insert
          (also do not print unmanaged chars!)
        - allow scrolling (both vertical and horizontal)
        - catch SIGWINCH and react appropriately (after scrolling is 
          enabled)
       +* put yanked content (p)
       +* turn screen into a lineset
       +* change alloc/ensure functions to work on line_t* and lineset_t*
        * add crop command (C)
        * reorganise code
        * change screen management (i.e., dynamic array of lines)
   DIR diff --git a/draw.c b/draw.c
       @@ -299,9 +299,15 @@ void visual_box(FILE *fc){
                draw_box(x,y,NOFIX);
                while((c=fgetc(fc))!=EOF && c != 27 && c!= 'v' && c != '\n'){
                        if (!move_around(c, fc)) switch(c){
       +                        case 'y': /* yank (copy) */
       +                                yank_region(MIN(orig_x,x), MIN(orig_y,y), MAX(orig_x, x), MAX(orig_y, y));
       +                                goto vis_exit;
       +                                break;
                                case 'f':/* fill */
                                        f = get_key(fc, "fill char: "); /** FALLTHROUGH **/
                                case 'x':/* erase */
       +                                if (c == 'x')
       +                                        yank_region(MIN(orig_x,x), MIN(orig_y,y), MAX(orig_x, x), MAX(orig_y, y));
                                        erase_box(orig_x, orig_y, f);
                                        erase_blank_lines(MIN(y,orig_y), MAX(y, orig_y));
                                        modified = 1;
       @@ -323,3 +329,8 @@ vis_exit:
                redraw();
                mode = MOVE;
        }
       +
       +void paste(){
       +        paste_region(x, y);
       +        redraw();
       +}
   DIR diff --git a/files.c b/files.c
       @@ -24,7 +24,7 @@ void write_file(FILE *fc){
                        return;
                }
                for (i=0; i<HEIGHT; i++){
       -                fprintf(fout, "%s\n", screen[i].s);
       +                fprintf(fout, "%s\n", screen.l[i].s);
                }
                fclose(fout);
                modified = 0;
       @@ -50,8 +50,8 @@ void load_file(FILE *fc){
                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';
       +                while((fgets(screen.l[i].s, WIDTH+2, fin)) != NULL && i<HEIGHT)
       +                        screen.l[i++].s[WIDTH-1]='\0';
                        for(;i<HEIGHT; i++){
                                erase_line(i);
                        }
   DIR diff --git a/gramscii.1 b/gramscii.1
       @@ -57,6 +57,12 @@ Crop chart to the largest non-blank region. The first line and the first
        column of the cropped chart will contain the first non-blank line and
        the first non-blank column of the original chart, respectively.
        .TP 5m
       +.BI p
       +Paste the content of the yank buffer at the cursor position. The yank
       +buffer contains the rectangle yanked/cut in
       +.B visual
       +mode. 
       +.TP 5m
        .BI q
        Quit gramscii, and prompt for a filename if the current screen contains
        unsaved changes.
       @@ -340,9 +346,19 @@ commands to highlight a rectangle. Then, you can use one of the
        following command on the highlighted region:
        .RS
        .TP 5m
       +.BI y
       +Yank (copy) the highlighted rectangle to the yank buffer. The content of
       +the yank buffer can be retrieved by using the
       +.B p
       +command while in 
       +.B move
       +mode. The yank buffer is overwritten by subsequent yank/cut commands.
       +.TP 5m
        .BI x
       -Erase region. All the characters in the region are set to the default
       -background character (space).
       +Cut region. The content of the highlighted rectangle will be put in the
       +yank buffer and all the characters in the region are set to the default
       +background character (space). The yank buffer is overwritten by
       +subsequent yank/cut commands.
        .TP 5m
        .BI f
        Fill region. gramscii will wait for a character on input and then will
   DIR diff --git a/gramscii.h b/gramscii.h
       @@ -5,13 +5,7 @@
        #include <termios.h>
        #include <unistd.h>
        
       -/** types **/
        
       -typedef struct{
       -        int sz;
       -        int lst;
       -        char *s;
       -} line_t;
        
        /** constants **/
        
       @@ -50,6 +44,20 @@ typedef struct{
        #define VIDEO_NRM 0
        #define VIDEO_REV 7 
        
       +/** types **/
       +
       +typedef struct{
       +        int sz;/* allocated size*/
       +        int n;/* line number */
       +        int lst;/* last visible char (before the first \0) */
       +        char *s;
       +} line_t;
       +
       +typedef struct{
       +        int sz;/* allocated size */
       +        int num;/* number of lines stored */
       +        line_t *l;
       +} lineset_t;
        
        /** MACROS **/
        
       @@ -63,8 +71,9 @@ typedef struct{
        
        /** global variables **/ 
        
       -line_t *screen;
       -int num_lines;
       +lineset_t screen;
       +lineset_t cutbuf;
       +
        int WIDTH, HEIGHT;
        
        int mode;
       @@ -96,6 +105,7 @@ char visual;
        char silent;
        char autoend;
        
       +
        struct termios t1, t2, t3;
        
        /** screen-related functions **/
       @@ -129,6 +139,7 @@ void get_box(FILE *fc);
        void get_arrow(FILE *fc);
        void erase(FILE *fc);
        void visual_box(FILE *fc);
       +void paste();
        
        /** file-related functions **/
        void write_file(FILE *fc);
       @@ -136,5 +147,14 @@ void check_modified(FILE *fc);
        void load_file(FILE *fc);
        void new_file(FILE *fc);
        
       +/** line-related functions **/
       +
       +void dump_lines(lineset_t ls, FILE *f);
       +void alloc_line(line_t *l);
       +void ensure_line_length(line_t *l, int len);
       +void ensure_num_lines(lineset_t *ls, int n);
       +void yank_region(int x1, int y1, int x2, int y2);
       +void paste_region(int x1, int y1);
       +
        
        #endif
   DIR diff --git a/lineset.c b/lineset.c
       @@ -0,0 +1,117 @@
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <string.h>
       +#include "gramscii.h"
       +
       +static int LONG_STEP;
       +
       +/* line_t and lineset_t management */
       +
       +void ensure_line_length(line_t *l, int len){
       +        char *tmp;
       +
       +        if (l->sz < len + 1){
       +                tmp = realloc(l->s, (len+1) * 2 * sizeof(char));
       +                if (!tmp){
       +                        fprintf(stderr, "Unable to allocate string\n");
       +                        exit(1);
       +                }
       +                l->s = tmp;
       +                l->sz = (len + 1) * 2;
       +        }
       +}
       +
       +
       +void alloc_line(line_t *l){
       +        char *tmp;
       +
       +        l->sz = WIDTH+1;
       +        tmp = malloc((l->sz) * sizeof(char));
       +        if (tmp == NULL){
       +                fprintf(stderr, "unable to allocate line\n");
       +                exit(1);
       +        }
       +        l->s = tmp;
       +        memset(l->s, BG, l->sz);
       +        l->lst = -1;
       +        l->s[0]='\0';
       +}
       +
       +void ensure_num_lines(lineset_t *ls, int n){
       +        line_t *tmp;
       +
       +        if (n > ls->sz){
       +                if (ls->sz == 0)
       +                        ls->l=NULL;
       +                tmp = realloc(ls->l, (n + LONG_STEP) * sizeof(line_t));
       +                if (tmp == NULL){
       +                        fprintf(stderr, "Unable to allocate memory for more lines");
       +                        exit(1);
       +                }
       +                else {
       +                        ls->l = tmp;
       +                        while ( ls->sz < n + LONG_STEP){
       +                                alloc_line(&(ls->l[ls->sz]));
       +                                ls->sz ++;
       +                        }
       +                }
       +        }
       +}
       +
       +void dump_lines(lineset_t ls, FILE *f){
       +        int i;
       +        for (i=0; i<ls.num ;i++){
       +                fprintf(f, "%d:%s\n", i, ls.l[i].s);
       +        }
       +        fflush(f);
       +}
       +
       +void pad_line_to_length(char *s, int W){
       +
       +        int i;
       +
       +        for (i=strlen(s); i<W; i++){
       +                s[i] = BG;
       +        }
       +}
       +
       +/* cut/yank/paste/undo management */
       +
       +void yank_region(int x1, int y1, int x2, int y2){
       +
       +        int N, W, i;
       +
       +        N = y2 - y1 + 1;
       +        W = x2 - x1 + 1;
       +        ensure_num_lines(&cutbuf, N);
       +        
       +        for (i=y1; i<=y2; i++){
       +                ensure_line_length(&(cutbuf.l[i-y1]), W);
       +                memcpy(cutbuf.l[i-y1].s, screen.l[i].s + x1, x2-x1+1);
       +                if (strlen(cutbuf.l[i-y1].s) < W)
       +                        pad_line_to_length(cutbuf.l[i-y1].s, W);
       +                cutbuf.l[i-y1].s[W] = '\0';
       +                cutbuf.l[i-y1].n = i;
       +        }
       +        cutbuf.num = N;
       +#ifdef DEBUG
       +        dump_lines(cutbuf, stderr);
       +#endif
       +        
       +}
       +
       +
       +void paste_region(int x1, int y1){
       +        int i, curlen;
       +
       +        i = y1;
       +        while( i < HEIGHT && i < y1 + cutbuf.num){
       +                memcpy(screen.l[i].s + x1, cutbuf.l[i-y1].s, strlen(cutbuf.l[i-y1].s));
       +                curlen = strlen(screen.l[i].s); 
       +                if (curlen <= x1)
       +                        /* double-check this line below */
       +                        pad_line_to_length(screen.l[i].s+curlen, x1 - curlen);
       +                i += 1;
       +                modified = 1;
       +        }
       +}
   DIR diff --git a/main.c b/main.c
       @@ -30,19 +30,13 @@
        
        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();
       +                dump_lines(screen, stdout);
                tcsetattr(0, TCSANOW, &t1);
                fflush(stdout);
                exit(0);
       @@ -125,6 +119,9 @@ void commands(FILE *fc){
                                        case 'C':
                                                crop_to_nonblank();
                                                break;
       +                                case 'p':
       +                                        paste();
       +                                        break;
                                        case 'q':
                                                check_modified(fc);/** FALLTHROUGH **/
                                        case 'Q':
   DIR diff --git a/screen.c b/screen.c
       @@ -1,9 +1,9 @@
        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
       -#include <ctype.h>
        #include <termios.h>
        #include <sys/ioctl.h>
       +#include <ctype.h>
        
        #include "gramscii.h"
        #include "config.h"
       @@ -62,7 +62,7 @@ void status_bar(){
                else
                        printf(" *%s*", fname );
        #ifdef DEBUG
       -        printf("  '%d'  ", screen[y].s[x]);
       +        printf("  '%d'  ", screen.l[y].s[x]);
        #endif
                printf("\033[0m");
                fflush(stdout);
       @@ -109,52 +109,6 @@ int is_yes(char c){
        
        /*** Screen management ***/
        
       -void ensure_line_length(int i, int len){
       -        char *tmp;
       -
       -        if (screen[i].sz < len + 1){
       -                tmp = realloc(screen[i].s, (len+1) * 2 * sizeof(char));
       -                if (!tmp){
       -                        fprintf(stderr, "Unable to allocate string\n");
       -                        exit(1);
       -                }
       -                screen[i].s = tmp;
       -                screen[i].sz = (len + 1) * 2;
       -        }
       -}
       -
       -
       -void alloc_line(int i){
       -        char *tmp;
       -
       -        screen[i].sz = WIDTH+1;
       -        tmp = malloc((screen[i].sz) * sizeof(char));
       -        if (tmp == NULL){
       -                fprintf(stderr, "unable to allocate line %d\n", i+1);
       -                exit(1);
       -        }
       -        screen[i].s = tmp;
       -        memset(screen[i].s, BG, screen[i].sz);
       -        screen[i].lst = -1;
       -        screen[i].s[0]='\0';
       -}
       -
       -void ensure_num_lines(int n){
       -        line_t *tmp;
       -
       -        if (n > num_lines){
       -                tmp = realloc(screen, (n + LONG_STEP) * sizeof(line_t));
       -                if (tmp == NULL){
       -                        fprintf(stderr, "Unable to allocate memory for more lines");
       -                        exit(1);
       -                }
       -                else while ( num_lines < n + LONG_STEP){
       -                        alloc_line(num_lines);
       -                        num_lines ++;
       -                }
       -        }
       -}
       -
        
        void show_cursor(){
                if (silent)
       @@ -165,15 +119,15 @@ void show_cursor(){
        
        
        void set_xy(int _x, int _y, char c){
       -        ensure_num_lines(_y + 1);
       -        ensure_line_length(_y, _x + 1);
       -        while (screen[_y].lst<_x){
       -                screen[_y].lst ++;
       -                screen[_y].s[screen[_y].lst] = BG;
       +        ensure_num_lines(&screen, _y + 1);
       +        ensure_line_length(&(screen.l[_y]), _x + 1);
       +        while (screen.l[_y].lst<_x){
       +                screen.l[_y].lst ++;
       +                screen.l[_y].s[screen.l[_y].lst] = BG;
                }
       -        screen[_y].s[_x] = c;
       -        if (_x == screen[_y].lst)
       -                screen[_y].s[_x+1] = '\0';
       +        screen.l[_y].s[_x] = c;
       +        if (_x == screen.l[_y].lst)
       +                screen.l[_y].s[_x+1] = '\0';
        }
        
        void set_cur(char c){
       @@ -193,7 +147,7 @@ void update_current(){
                if (silent)
                        return;
                printf("\033[%d'%df",y+1,x+1);
       -        putchar(screen[y].s[x]);
       +        putchar(screen.l[y].s[x]);
                fflush(stdout);
        }
        
       @@ -206,20 +160,20 @@ void erase_blank_lines(int y1, int y2){
                }
                
                for (; y1 <= y2; y1++){
       -                j = screen[y1].lst; 
       -                while (j>=0 && isblank(screen[y1].s[j]))
       +                j = screen.l[y1].lst; 
       +                while (j>=0 && isblank(screen.l[y1].s[j]))
                                j--;
                        if (j<0){
       -                        screen[y1].lst = -1;
       -                        screen[y1].s[0] = '\0';
       +                        screen.l[y1].lst = -1;
       +                        screen.l[y1].s[0] = '\0';
                        }
                }
        }
        
        
        void erase_line(int i){
       -        screen[i].lst = -1;
       -        screen[i].s[0] = '\0';
       +        screen.l[i].lst = -1;
       +        screen.l[i].s[0] = '\0';
        }
        
        void erase_box(int x1, int y1, char c){
       @@ -268,7 +222,7 @@ void redraw(){
                        return;
                printf("\033[2J\033[1;1H");
                for (i=0;i<HEIGHT;i++){
       -                fprintf(stdout,"%s\n",screen[i].s);
       +                fprintf(stdout,"%s\n",screen.l[i].s);
                }
                status_bar();
                show_cursor();
       @@ -435,14 +389,15 @@ void init_screen(){
                        WIDTH=80;
                        HEIGHT=24;
                }
       -        screen = malloc(HEIGHT * sizeof(line_t));
       -        num_lines = HEIGHT;
       -        if (screen == NULL){
       +        screen.l = malloc(HEIGHT * sizeof(line_t));
       +        screen.sz = HEIGHT;
       +        screen.num = HEIGHT;
       +        if (screen.l == NULL){
                        perror("allocating screen");
                        exit(1);
                }
                for (i=0; i<HEIGHT; i++){
       -                alloc_line(i);
       +                alloc_line(&(screen.l[i]));
                }
                hlines_sz= sizeof(hlines) -1;
                vlines_sz= sizeof(vlines) -1;
       @@ -450,6 +405,9 @@ void init_screen(){
                stmarks_sz = sizeof(st_marks) - 1;
                endmarks_sz = sizeof(st_marks) - 1;
                reset_styles();
       +        cutbuf.sz = 0;
       +        cutbuf.l = NULL;
       +        cutbuf.num = 0;
        }
        
        void find_nonblank_rect(int *x1, int *y1, int *x2, int *y2){
       @@ -457,22 +415,22 @@ void find_nonblank_rect(int *x1, int *y1, int *x2, int *y2){
                int i, j;
                int first;
                *x1= WIDTH; /** FIXME: replace with num_cols **/
       -        *y1 = num_lines;
       +        *y1 = screen.num;
                *x2 = *y2 = 0; 
        
       -        for (i=0; i<num_lines; i++){
       -                if (screen[i].lst < 0)
       +        for (i=0; i<screen.num; i++){
       +                if (screen.l[i].lst < 0)
                                continue;
                        *y2 = i;
                        if (i < *y1)
                                *y1 = i;
                        j = 0;
       -                while((j <= screen[i].lst)  && isblank(first=screen[i].s[j]))
       +                while((j <= screen.l[i].lst)  && isblank(first=screen.l[i].s[j]))
                                j++;
                        if (j < *x1)
                                *x1 = j;
       -                j = screen[i].lst;
       -                while(isblank(screen[i].s[j]))
       +                j = screen.l[i].lst;
       +                while(isblank(screen.l[i].s[j]))
                                j--;
                        if (j > *x2)
                                *x2 = j;
       @@ -483,13 +441,13 @@ void crop_to_rect(int x1, int y1, int x2, int y2){
                int i;
        
                for (i=0; i<= y2-y1; i ++){
       -                ensure_line_length(i, screen[i+y1].lst);
       -                sprintf(screen[i].s, "%s", screen[i+y1].s + x1);
       -                screen[i].lst = screen[i+y1].lst - x1;
       +                ensure_line_length(&(screen.l[i]), screen.l[i+y1].lst);
       +                sprintf(screen.l[i].s, "%s", screen.l[i+y1].s + x1);
       +                screen.l[i].lst = screen.l[i+y1].lst - x1;
                } 
                while (i<=y2){
       -                screen[i].lst = -1;
       -                screen[i].s[0]= '\0';
       +                screen.l[i].lst = -1;
       +                screen.l[i].s[0]= '\0';
                        i ++;
                }
        }
       @@ -505,3 +463,4 @@ void crop_to_nonblank(){
                redraw();
        }
        
       +