tlbuf.c - neatvi - [fork] simple vi-type editor with UTF-8 support
git clone git://src.adamsgaard.dk/neatvi
Log
Files
Refs
README
---
tlbuf.c (8526B)
---
     1 #include 
     2 #include 
     3 #include 
     4 #include 
     5 #include 
     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 }