tlbuf.c - neatvi - [fork] simple vi-type editor with UTF-8 support
 (HTM) git clone git://src.adamsgaard.dk/neatvi
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
       ---
       tlbuf.c (8526B)
       ---
            1 #include <ctype.h>
            2 #include <stdlib.h>
            3 #include <stdio.h>
            4 #include <string.h>
            5 #include <unistd.h>
            6 #include "vi.h"
            7 
            8 #define NMARKS_BASE                ('z' - 'a' + 2)
            9 #define NMARKS                        32
           10 
           11 /* line operations */
           12 struct lopt {
           13         char *ins;                /* inserted text */
           14         char *del;                /* deleted text */
           15         int pos, n_ins, n_del;        /* modification location */
           16         int pos_off;                /* cursor line offset */
           17         int seq;                /* operation number */
           18         int *mark, *mark_off;        /* saved marks */
           19 };
           20 
           21 /* line buffers */
           22 struct lbuf {
           23         int mark[NMARKS];        /* mark lines */
           24         int mark_off[NMARKS];        /* mark line offsets */
           25         char **ln;                /* buffer lines */
           26         char *ln_glob;                /* line global mark */
           27         int ln_n;                /* number of lines in ln[] */
           28         int ln_sz;                /* size of ln[] */
           29         int useq;                /* current operation sequence */
           30         struct lopt *hist;        /* buffer history */
           31         int hist_sz;                /* size of hist[] */
           32         int hist_n;                /* current history head in hist[] */
           33         int hist_u;                /* current undo head in hist[] */
           34         int useq_zero;                /* useq for lbuf_saved() */
           35         int useq_last;                /* useq before hist[] */
           36 };
           37 
           38 struct lbuf *lbuf_make(void)
           39 {
           40         struct lbuf *lb = malloc(sizeof(*lb));
           41         int i;
           42         memset(lb, 0, sizeof(*lb));
           43         for (i = 0; i < LEN(lb->mark); i++)
           44                 lb->mark[i] = -1;
           45         lb->useq = 1;
           46         return lb;
           47 }
           48 
           49 static void lopt_done(struct lopt *lo)
           50 {
           51         free(lo->ins);
           52         free(lo->del);
           53         free(lo->mark);
           54         free(lo->mark_off);
           55 }
           56 
           57 static void lbuf_savemark(struct lbuf *lb, struct lopt *lo, int m)
           58 {
           59         if (lb->mark[m] >= 0) {
           60                 if (!lo->mark) {
           61                         lo->mark = malloc(sizeof(lb->mark));
           62                         lo->mark_off = malloc(sizeof(lb->mark_off));
           63                         memset(lo->mark, 0xff, sizeof(lb->mark));
           64                 }
           65                 lo->mark[m] = lb->mark[m];
           66                 lo->mark_off[m] = lb->mark_off[m];
           67         }
           68 }
           69 
           70 static void lbuf_loadmark(struct lbuf *lb, struct lopt *lo, int m)
           71 {
           72         if (lo->mark && lo->mark[m] >= 0) {
           73                 lb->mark[m] = lo->mark[m];
           74                 lb->mark_off[m] = lo->mark_off[m];
           75         }
           76 }
           77 
           78 static int markidx(int mark)
           79 {
           80         if (islower(mark))
           81                 return mark - 'a';
           82         if (mark == '\'' || mark == '`')
           83                 return 'z' - 'a' + 1;
           84         if (mark == '*')
           85                 return 'z' - 'a' + 2;
           86         if (mark == '[')
           87                 return 'z' - 'a' + 3;
           88         if (mark == ']')
           89                 return 'z' - 'a' + 4;
           90         return -1;
           91 }
           92 
           93 static void lbuf_savepos(struct lbuf *lb, struct lopt *lo)
           94 {
           95         if (lb->mark[markidx('*')] >= 0)
           96                 lo->pos_off = lb->mark_off[markidx('*')];
           97 }
           98 
           99 static void lbuf_loadpos(struct lbuf *lb, struct lopt *lo)
          100 {
          101         lb->mark[markidx('*')] = lo->pos;
          102         lb->mark_off[markidx('*')] = lo->pos_off;
          103 }
          104 
          105 void lbuf_free(struct lbuf *lb)
          106 {
          107         int i;
          108         for (i = 0; i < lb->ln_n; i++)
          109                 free(lb->ln[i]);
          110         for (i = 0; i < lb->hist_n; i++)
          111                 lopt_done(&lb->hist[i]);
          112         free(lb->hist);
          113         free(lb->ln);
          114         free(lb->ln_glob);
          115         free(lb);
          116 }
          117 
          118 static int linelength(char *s)
          119 {
          120         char *r = strchr(s, '\n');
          121         return r ? r - s + 1 : strlen(s);
          122 }
          123 
          124 static int linecount(char *s)
          125 {
          126         int n;
          127         for (n = 0; s && *s; n++)
          128                 s += linelength(s);
          129         return n;
          130 }
          131 
          132 
          133 /* low-level line replacement */
          134 static void lbuf_replace(struct lbuf *lb, char *s, int pos, int n_del)
          135 {
          136         int n_ins = linecount(s);
          137         int i;
          138         while (lb->ln_n + n_ins - n_del >= lb->ln_sz) {
          139                 int nsz = lb->ln_sz + (lb->ln_sz ? lb->ln_sz : 512);
          140                 char **nln = malloc(nsz * sizeof(nln[0]));
          141                 char *nln_glob = malloc(nsz * sizeof(nln_glob[0]));
          142                 memcpy(nln, lb->ln, lb->ln_n * sizeof(lb->ln[0]));
          143                 memcpy(nln_glob, lb->ln_glob, lb->ln_n * sizeof(lb->ln_glob[0]));
          144                 free(lb->ln);
          145                 free(lb->ln_glob);
          146                 lb->ln = nln;
          147                 lb->ln_glob = nln_glob;
          148                 lb->ln_sz = nsz;
          149         }
          150         for (i = 0; i < n_del; i++)
          151                 free(lb->ln[pos + i]);
          152         if (n_ins != n_del) {
          153                 memmove(lb->ln + pos + n_ins, lb->ln + pos + n_del,
          154                         (lb->ln_n - pos - n_del) * sizeof(lb->ln[0]));
          155                 memmove(lb->ln_glob + pos + n_ins, lb->ln_glob + pos + n_del,
          156                         (lb->ln_n - pos - n_del) * sizeof(lb->ln_glob[0]));
          157         }
          158         lb->ln_n += n_ins - n_del;
          159         for (i = 0; i < n_ins; i++) {
          160                 int l = s ? linelength(s) : 0;
          161                 int l_nonl = l - (s[l - 1] == '\n');
          162                 char *n = malloc(l_nonl + 2);
          163                 memcpy(n, s, l_nonl);
          164                 n[l_nonl + 0] = '\n';
          165                 n[l_nonl + 1] = '\0';
          166                 lb->ln[pos + i] = n;
          167                 s += l;
          168         }
          169         for (i = n_del; i < n_ins; i++)
          170                 lb->ln_glob[pos + i] = 0;
          171         for (i = 0; i < LEN(lb->mark); i++) {        /* updating marks */
          172                 if (!s && lb->mark[i] >= pos && lb->mark[i] < pos + n_del)
          173                         lb->mark[i] = -1;
          174                 else if (lb->mark[i] >= pos + n_del)
          175                         lb->mark[i] += n_ins - n_del;
          176                 else if (lb->mark[i] >= pos + n_ins)
          177                         lb->mark[i] = pos + n_ins - 1;
          178         }
          179         lbuf_mark(lb, '[', pos, 0);
          180         lbuf_mark(lb, ']', pos + (n_ins ? n_ins - 1 : 0), 0);
          181 }
          182 
          183 /* append undo/redo history */
          184 static void lbuf_opt(struct lbuf *lb, char *buf, int pos, int n_del)
          185 {
          186         struct lopt *lo;
          187         int i;
          188         for (i = lb->hist_u; i < lb->hist_n; i++)
          189                 lopt_done(&lb->hist[i]);
          190         lb->hist_n = lb->hist_u;
          191         if (lb->hist_n == lb->hist_sz) {
          192                 int sz = lb->hist_sz + (lb->hist_sz ? lb->hist_sz : 128);
          193                 struct lopt *hist = malloc(sz * sizeof(hist[0]));
          194                 memcpy(hist, lb->hist, lb->hist_n * sizeof(hist[0]));
          195                 free(lb->hist);
          196                 lb->hist = hist;
          197                 lb->hist_sz = sz;
          198         }
          199         lo = &lb->hist[lb->hist_n];
          200         lb->hist_n++;
          201         lb->hist_u = lb->hist_n;
          202         memset(lo, 0, sizeof(*lo));
          203         lo->pos = pos;
          204         lo->n_del = n_del;
          205         lo->del = n_del ? lbuf_cp(lb, pos, pos + n_del) : NULL;
          206         lo->n_ins = buf ? linecount(buf) : 0;
          207         lo->ins = buf ? uc_dup(buf) : NULL;
          208         lo->seq = lb->useq;
          209         lbuf_savepos(lb, lo);
          210         for (i = 0; i < NMARKS_BASE; i++)
          211                 if (lb->mark[i] >= pos && lb->mark[i] < pos + n_del)
          212                         lbuf_savemark(lb, lo, i);
          213 }
          214 
          215 int lbuf_rd(struct lbuf *lbuf, int fd, int beg, int end)
          216 {
          217         char buf[1 << 10];
          218         struct sbuf *sb;
          219         long nr;
          220         sb = sbuf_make();
          221         while ((nr = read(fd, buf, sizeof(buf))) > 0)
          222                 sbuf_mem(sb, buf, nr);
          223         if (!nr)
          224                 lbuf_edit(lbuf, sbuf_buf(sb), beg, end);
          225         sbuf_free(sb);
          226         return nr != 0;
          227 }
          228 
          229 int lbuf_wr(struct lbuf *lbuf, int fd, int beg, int end)
          230 {
          231         int i;
          232         for (i = beg; i < end; i++) {
          233                 char *ln = lbuf->ln[i];
          234                 long nw = 0;
          235                 long nl = strlen(ln);
          236                 while (nw < nl) {
          237                         long nc = write(fd, ln + nw, nl - nw);
          238                         if (nc < 0)
          239                                 return 1;
          240                         nw += nc;
          241                 }
          242         }
          243         return 0;
          244 }
          245 
          246 /* replace lines beg through end with buf */
          247 void lbuf_edit(struct lbuf *lb, char *buf, int beg, int end)
          248 {
          249         if (beg > lb->ln_n)
          250                 beg = lb->ln_n;
          251         if (end > lb->ln_n)
          252                 end = lb->ln_n;
          253         if (beg == end && !buf)
          254                 return;
          255         lbuf_opt(lb, buf, beg, end - beg);
          256         lbuf_replace(lb, buf, beg, end - beg);
          257 }
          258 
          259 char *lbuf_cp(struct lbuf *lb, int beg, int end)
          260 {
          261         struct sbuf *sb;
          262         int i;
          263         sb = sbuf_make();
          264         for (i = beg; i < end; i++)
          265                 if (i < lb->ln_n)
          266                         sbuf_str(sb, lb->ln[i]);
          267         return sbuf_done(sb);
          268 }
          269 
          270 char *lbuf_get(struct lbuf *lb, int pos)
          271 {
          272         return pos >= 0 && pos < lb->ln_n ? lb->ln[pos] : NULL;
          273 }
          274 
          275 int lbuf_len(struct lbuf *lb)
          276 {
          277         return lb->ln_n;
          278 }
          279 
          280 void lbuf_mark(struct lbuf *lbuf, int mark, int pos, int off)
          281 {
          282         if (markidx(mark) >= 0) {
          283                 lbuf->mark[markidx(mark)] = pos;
          284                 lbuf->mark_off[markidx(mark)] = off;
          285         }
          286 }
          287 
          288 int lbuf_jump(struct lbuf *lbuf, int mark, int *pos, int *off)
          289 {
          290         int mk = markidx(mark);
          291         if (mk < 0 || lbuf->mark[mk] < 0)
          292                 return 1;
          293         *pos = lbuf->mark[mk];
          294         if (off)
          295                 *off = lbuf->mark_off[mk];
          296         return 0;
          297 }
          298 
          299 int lbuf_undo(struct lbuf *lb)
          300 {
          301         int useq, i;
          302         if (!lb->hist_u)
          303                 return 1;
          304         useq = lb->hist[lb->hist_u - 1].seq;
          305         while (lb->hist_u && lb->hist[lb->hist_u - 1].seq == useq) {
          306                 struct lopt *lo = &lb->hist[--(lb->hist_u)];
          307                 lbuf_replace(lb, lo->del, lo->pos, lo->n_ins);
          308                 lbuf_loadpos(lb, lo);
          309                 for (i = 0; i < LEN(lb->mark); i++)
          310                         lbuf_loadmark(lb, lo, i);
          311         }
          312         return 0;
          313 }
          314 
          315 int lbuf_redo(struct lbuf *lb)
          316 {
          317         int useq;
          318         if (lb->hist_u == lb->hist_n)
          319                 return 1;
          320         useq = lb->hist[lb->hist_u].seq;
          321         while (lb->hist_u < lb->hist_n && lb->hist[lb->hist_u].seq == useq) {
          322                 struct lopt *lo = &lb->hist[lb->hist_u++];
          323                 lbuf_replace(lb, lo->ins, lo->pos, lo->n_del);
          324                 lbuf_loadpos(lb, lo);
          325         }
          326         return 0;
          327 }
          328 
          329 static int lbuf_seq(struct lbuf *lb)
          330 {
          331         return lb->hist_u ? lb->hist[lb->hist_u - 1].seq : lb->useq_last;
          332 }
          333 
          334 /* mark buffer as saved and, if clear, clear the undo history */
          335 void lbuf_saved(struct lbuf *lb, int clear)
          336 {
          337         int i;
          338         if (clear) {
          339                 for (i = 0; i < lb->hist_n; i++)
          340                         lopt_done(&lb->hist[i]);
          341                 lb->hist_n = 0;
          342                 lb->hist_u = 0;
          343                 lb->useq_last = lb->useq;
          344         }
          345         lb->useq_zero = lbuf_seq(lb);
          346         lbuf_modified(xb);
          347 }
          348 
          349 /* was the file modified since the last lbuf_modreset() */
          350 int lbuf_modified(struct lbuf *lb)
          351 {
          352         lb->useq++;
          353         return lbuf_seq(lb) != lb->useq_zero;
          354 }
          355 
          356 /* mark the line for ex global command */
          357 void lbuf_globset(struct lbuf *lb, int pos, int dep)
          358 {
          359         lb->ln_glob[pos] |= 1 << dep;
          360 }
          361 
          362 /* return and clear ex global command mark */
          363 int lbuf_globget(struct lbuf *lb, int pos, int dep)
          364 {
          365         int o = lb->ln_glob[pos] & (1 << dep);
          366         lb->ln_glob[pos] &= ~(1 << dep);
          367         return o > 0;
          368 }