tvi.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
       ---
       tvi.c (29670B)
       ---
            1 /*
            2  * NEATVI Editor
            3  *
            4  * Copyright (C) 2015-2019 Ali Gholami Rudi <ali at rudi dot ir>
            5  *
            6  * Permission to use, copy, modify, and/or distribute this software for any
            7  * purpose with or without fee is hereby granted, provided that the above
            8  * copyright notice and this permission notice appear in all copies.
            9  *
           10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
           11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
           12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
           13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
           14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
           15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
           16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
           17  */
           18 #include <ctype.h>
           19 #include <fcntl.h>
           20 #include <stdio.h>
           21 #include <stdlib.h>
           22 #include <string.h>
           23 #include <signal.h>
           24 #include "vi.h"
           25 
           26 static char vi_msg[EXLEN];        /* current message */
           27 static char vi_charlast[8];        /* the last character searched via f, t, F, or T */
           28 static int vi_charcmd;                /* the character finding command */
           29 static int vi_arg1, vi_arg2;        /* the first and second arguments */
           30 static int vi_ybuf;                /* current yank buffer */
           31 static int vi_pcol;                /* the column requested by | command */
           32 static int vi_printed;                /* ex_print() calls since the last command */
           33 static int vi_scroll;                /* scroll amount for ^f and ^d*/
           34 static int vi_soset, vi_so;        /* search offset; 1 in "/kw/1" */
           35 
           36 static void vi_wait(void)
           37 {
           38         if (vi_printed > 1) {
           39                 free(ex_read("[enter to continue]"));
           40                 vi_msg[0] = '\0';
           41         }
           42         vi_printed = 0;
           43 }
           44 
           45 static void vi_drawmsg(void)
           46 {
           47         int oleft = xleft;
           48         xleft = 0;
           49         led_printmsg(vi_msg, xrows, "---");
           50         vi_msg[0] = '\0';
           51         xleft = oleft;
           52 }
           53 
           54 static void vi_drawrow(int row)
           55 {
           56         char *s = lbuf_get(xb, row);
           57         if (xhll && row == xrow)
           58                 syn_context(conf_hlline());
           59         led_print(s ? s : (row ? "~" : ""), row - xtop, ex_filetype());
           60         syn_context(0);
           61 }
           62 
           63 /* redraw the screen */
           64 static void vi_drawagain(int xcol, int lineonly)
           65 {
           66         int i;
           67         term_record();
           68         for (i = xtop; i < xtop + xrows; i++)
           69                 if (!lineonly || i == xrow)
           70                         vi_drawrow(i);
           71         vi_drawmsg();
           72         term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
           73         term_commit();
           74 }
           75 
           76 /* update the screen */
           77 static void vi_drawupdate(int xcol, int otop)
           78 {
           79         int i = 0;
           80         if (otop != xtop) {
           81                 term_record();
           82                 term_pos(0, 0);
           83                 term_room(otop - xtop);
           84                 if (xtop > otop) {
           85                         int n = MIN(xtop - otop, xrows);
           86                         for (i = 0; i < n; i++)
           87                                 vi_drawrow(xtop + xrows - n + i);
           88                 } else {
           89                         int n = MIN(otop - xtop, xrows);
           90                         for (i = 0; i < n; i++)
           91                                 vi_drawrow(xtop + i);
           92                 }
           93                 term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
           94                 term_commit();
           95         }
           96         vi_drawmsg();
           97         term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
           98 }
           99 
          100 /* update the screen by removing lines r1 to r2 before an input command */
          101 static void vi_drawrm(int r1, int r2, int newln)
          102 {
          103         r1 = MIN(MAX(r1, xtop), xtop + xrows);
          104         r2 = MIN(MAX(r2, xtop), xtop + xrows);
          105         term_pos(r1 - xtop, 0);
          106         term_room(r1 - r2 + newln);
          107 }
          108 
          109 static int vi_buf[128];
          110 static int vi_buflen;
          111 
          112 static int vi_read(void)
          113 {
          114         return vi_buflen ? vi_buf[--vi_buflen] : term_read();
          115 }
          116 
          117 static void vi_back(int c)
          118 {
          119         if (vi_buflen < sizeof(vi_buf))
          120                 vi_buf[vi_buflen++] = c;
          121 }
          122 
          123 static char *vi_char(void)
          124 {
          125         return led_read(&xkmap);
          126 }
          127 
          128 static char *vi_prompt(char *msg, int *kmap)
          129 {
          130         char *r, *s;
          131         term_pos(xrows, led_pos(msg, 0));
          132         term_kill();
          133         s = led_prompt(msg, "", kmap, "---");
          134         if (!s)
          135                 return NULL;
          136         r = uc_dup(strlen(s) >= strlen(msg) ? s + strlen(msg) : s);
          137         free(s);
          138         return r;
          139 }
          140 
          141 /* read an ex input line */
          142 char *ex_read(char *msg)
          143 {
          144         struct sbuf *sb;
          145         int c;
          146         if (xled) {
          147                 int oleft = xleft;
          148                 char *s = led_prompt(msg, "", &xkmap, "---");
          149                 xleft = oleft;
          150                 if (s)
          151                         term_chr('\n');
          152                 return s;
          153         }
          154         sb = sbuf_make();
          155         while ((c = getchar()) != EOF && c != '\n')
          156                 sbuf_chr(sb, c);
          157         if (c == EOF) {
          158                 sbuf_free(sb);
          159                 return NULL;
          160         }
          161         return sbuf_done(sb);
          162 }
          163 
          164 /* show an ex message */
          165 void ex_show(char *msg)
          166 {
          167         if (xvis) {
          168                 snprintf(vi_msg, sizeof(vi_msg), "%s", msg);
          169         } else if (xled) {
          170                 led_print(msg, -1, "---");
          171                 term_chr('\n');
          172         } else {
          173                 printf("%s", msg);
          174         }
          175 }
          176 
          177 /* print an ex output line */
          178 void ex_print(char *line)
          179 {
          180         if (xvis) {
          181                 vi_printed += line ? 1 : 2;
          182                 if (line)
          183                         snprintf(vi_msg, sizeof(vi_msg), "%s", line);
          184                 if (line)
          185                         led_print(line, -1, "");
          186                 term_chr('\n');
          187         } else {
          188                 if (line)
          189                         ex_show(line);
          190         }
          191 }
          192 
          193 static int vi_yankbuf(void)
          194 {
          195         int c = vi_read();
          196         if (c == '"')
          197                 return vi_read();
          198         vi_back(c);
          199         return 0;
          200 }
          201 
          202 static int vi_prefix(void)
          203 {
          204         int n = 0;
          205         int c = vi_read();
          206         if ((c >= '1' && c <= '9')) {
          207                 while (isdigit(c)) {
          208                         n = n * 10 + c - '0';
          209                         c = vi_read();
          210                 }
          211         }
          212         vi_back(c);
          213         return n;
          214 }
          215 
          216 static int vi_col2off(struct lbuf *lb, int row, int col)
          217 {
          218         char *ln = lbuf_get(lb, row);
          219         return ln ? ren_off(ln, col) : 0;
          220 }
          221 
          222 static int vi_off2col(struct lbuf *lb, int row, int off)
          223 {
          224         char *ln = lbuf_get(lb, row);
          225         return ln ? ren_pos(ln, off) : 0;
          226 }
          227 
          228 static int vi_nextoff(struct lbuf *lb, int dir, int *row, int *off)
          229 {
          230         int o = *off + dir;
          231         if (o < 0 || !lbuf_get(lb, *row) || o >= uc_slen(lbuf_get(lb, *row)))
          232                 return 1;
          233         *off = o;
          234         return 0;
          235 }
          236 
          237 static int vi_nextcol(struct lbuf *lb, int dir, int *row, int *off)
          238 {
          239         char *ln = lbuf_get(lb, *row);
          240         int col = ln ? ren_pos(ln, *off) : 0;
          241         int o = ln ? ren_next(ln, col, dir) : -1;
          242         if (o < 0)
          243                 return -1;
          244         *off = ren_off(ln, o);
          245         return 0;
          246 }
          247 
          248 static int vi_findchar(struct lbuf *lb, char *cs, int cmd, int n, int *row, int *off)
          249 {
          250         if (cs != vi_charlast)
          251                 strcpy(vi_charlast, cs);
          252         vi_charcmd = cmd;
          253         return lbuf_findchar(lb, cs, cmd, n, row, off);
          254 }
          255 
          256 static int vi_search(int cmd, int cnt, int *row, int *off)
          257 {
          258         char *kwd;
          259         int r = *row;
          260         int o = *off;
          261         char *failed = NULL;
          262         int len = 0;
          263         int i, dir;
          264         if (cmd == '/' || cmd == '?') {
          265                 char sign[4] = {cmd};
          266                 struct sbuf *sb;
          267                 char *kw = vi_prompt(sign, &xkmap);
          268                 char *re;
          269                 if (!kw)
          270                         return 1;
          271                 sb = sbuf_make();
          272                 sbuf_chr(sb, cmd);
          273                 sbuf_str(sb, kw);
          274                 free(kw);
          275                 kw = sbuf_buf(sb);
          276                 if ((re = re_read(&kw))) {
          277                         ex_kwdset(re[0] ? re : NULL, cmd == '/' ? +1 : -1);
          278                         while (isspace(*kw))
          279                                 kw++;
          280                         vi_soset = !!kw[0];
          281                         vi_so = atoi(kw);
          282                         free(re);
          283                 }
          284                 sbuf_free(sb);
          285         }
          286         if (!lbuf_len(xb) || ex_kwd(&kwd, &dir))
          287                 return 1;
          288         dir = cmd == 'N' ? -dir : dir;
          289         o = *off;
          290         for (i = 0; i < cnt; i++) {
          291                 if (lbuf_search(xb, kwd, dir, &r, &o, &len)) {
          292                         failed = " not found";
          293                         break;
          294                 }
          295                 if (i + 1 < cnt && cmd == '/')
          296                         o += len;
          297         }
          298         if (!failed) {
          299                 *row = r;
          300                 *off = o;
          301                 if (vi_soset) {
          302                         *off = -1;
          303                         if (*row + vi_so < 0 || *row + vi_so >= lbuf_len(xb))
          304                                 failed = " bad offset";
          305                         else
          306                                 *row += vi_so;
          307                 }
          308         }
          309         if (failed != NULL)
          310                 snprintf(vi_msg, sizeof(vi_msg), "/%s/%s\n", kwd, failed ? failed : "");
          311         return failed != NULL;
          312 }
          313 
          314 /* read a line motion */
          315 static int vi_motionln(int *row, int cmd)
          316 {
          317         int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
          318         int c = vi_read();
          319         int mark, mark_row, mark_off;
          320         switch (c) {
          321         case '\n':
          322         case '+':
          323                 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
          324                 break;
          325         case '-':
          326                 *row = MAX(*row - cnt, 0);
          327                 break;
          328         case '_':
          329                 *row = MIN(*row + cnt - 1, lbuf_len(xb) - 1);
          330                 break;
          331         case '\'':
          332                 if ((mark = vi_read()) <= 0)
          333                         return -1;
          334                 if (lbuf_jump(xb, mark, &mark_row, &mark_off))
          335                         return -1;
          336                 *row = mark_row;
          337                 break;
          338         case 'j':
          339                 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
          340                 break;
          341         case 'k':
          342                 *row = MAX(*row - cnt, 0);
          343                 break;
          344         case 'G':
          345                 *row = (vi_arg1 || vi_arg2) ? cnt - 1 : lbuf_len(xb) - 1;
          346                 break;
          347         case 'H':
          348                 *row = MIN(xtop + cnt - 1, lbuf_len(xb) - 1);
          349                 break;
          350         case 'L':
          351                 *row = MIN(xtop + xrows - 1 - cnt + 1, lbuf_len(xb) - 1);
          352                 break;
          353         case 'M':
          354                 *row = MIN(xtop + xrows / 2, lbuf_len(xb) - 1);
          355                 break;
          356         default:
          357                 if (c == cmd) {
          358                         *row = MIN(*row + cnt - 1, lbuf_len(xb) - 1);
          359                         break;
          360                 }
          361                 if (c == '%' && (vi_arg1 || vi_arg2)) {
          362                         if (cnt > 100)
          363                                 return -1;
          364                         *row = MAX(0, lbuf_len(xb) - 1) * cnt / 100;
          365                         break;
          366                 }
          367                 vi_back(c);
          368                 return 0;
          369         }
          370         if (*row < 0)
          371                 *row = 0;
          372         return c;
          373 }
          374 
          375 static char *vi_curword(struct lbuf *lb, int row, int off)
          376 {
          377         struct sbuf *sb;
          378         char *ln = lbuf_get(lb, row);
          379         char *beg, *end;
          380         if (!ln)
          381                 return NULL;
          382         beg = uc_chr(ln, ren_noeol(ln, off));
          383         end = beg;
          384         while (*end && uc_kind(end) == 1)
          385                 end = uc_next(end);
          386         while (beg > ln && uc_kind(uc_beg(ln, beg - 1)) == 1)
          387                 beg = uc_beg(ln, beg - 1);
          388         if (beg >= end)
          389                 return NULL;
          390         sb = sbuf_make();
          391         sbuf_str(sb, "\\<");
          392         sbuf_mem(sb, beg, end - beg);
          393         sbuf_str(sb, "\\>");
          394         return sbuf_done(sb);
          395 }
          396 
          397 /* read a motion */
          398 static int vi_motion(int *row, int *off)
          399 {
          400         int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
          401         char *ln = lbuf_get(xb, *row);
          402         int dir = dir_context(ln ? ln : "");
          403         int mark, mark_row, mark_off;
          404         char *cs;
          405         int mv;
          406         int i;
          407         if ((mv = vi_motionln(row, 0))) {
          408                 *off = -1;
          409                 return mv;
          410         }
          411         mv = vi_read();
          412         switch (mv) {
          413         case 'f':
          414                 if (!(cs = vi_char()))
          415                         return -1;
          416                 if (vi_findchar(xb, cs, mv, cnt, row, off))
          417                         return -1;
          418                 break;
          419         case 'F':
          420                 if (!(cs = vi_char()))
          421                         return -1;
          422                 if (vi_findchar(xb, cs, mv, cnt, row, off))
          423                         return -1;
          424                 break;
          425         case ';':
          426                 if (!vi_charlast[0])
          427                         return -1;
          428                 if (vi_findchar(xb, vi_charlast, vi_charcmd, cnt, row, off))
          429                         return -1;
          430                 break;
          431         case ',':
          432                 if (!vi_charlast[0])
          433                         return -1;
          434                 if (vi_findchar(xb, vi_charlast, vi_charcmd, -cnt, row, off))
          435                         return -1;
          436                 break;
          437         case 'h':
          438                 for (i = 0; i < cnt; i++)
          439                         if (vi_nextcol(xb, -1 * dir, row, off))
          440                                 break;
          441                 break;
          442         case 'l':
          443                 for (i = 0; i < cnt; i++)
          444                         if (vi_nextcol(xb, +1 * dir, row, off))
          445                                 break;
          446                 break;
          447         case 't':
          448                 if (!(cs = vi_char()))
          449                         return -1;
          450                 if (vi_findchar(xb, cs, mv, cnt, row, off))
          451                         return -1;
          452                 break;
          453         case 'T':
          454                 if (!(cs = vi_char()))
          455                         return -1;
          456                 if (vi_findchar(xb, cs, mv, cnt, row, off))
          457                         return -1;
          458                 break;
          459         case 'B':
          460                 for (i = 0; i < cnt; i++)
          461                         if (lbuf_wordend(xb, 1, -1, row, off))
          462                                 break;
          463                 break;
          464         case 'E':
          465                 for (i = 0; i < cnt; i++)
          466                         if (lbuf_wordend(xb, 1, +1, row, off))
          467                                 break;
          468                 break;
          469         case 'W':
          470                 for (i = 0; i < cnt; i++)
          471                         if (lbuf_wordbeg(xb, 1, +1, row, off))
          472                                 break;
          473                 break;
          474         case 'b':
          475                 for (i = 0; i < cnt; i++)
          476                         if (lbuf_wordend(xb, 0, -1, row, off))
          477                                 break;
          478                 break;
          479         case 'e':
          480                 for (i = 0; i < cnt; i++)
          481                         if (lbuf_wordend(xb, 0, +1, row, off))
          482                                 break;
          483                 break;
          484         case 'w':
          485                 for (i = 0; i < cnt; i++)
          486                         if (lbuf_wordbeg(xb, 0, +1, row, off))
          487                                 break;
          488                 break;
          489         case '{':
          490                 for (i = 0; i < cnt; i++)
          491                         if (lbuf_paragraphbeg(xb, -1, row, off))
          492                                 break;
          493                 break;
          494         case '}':
          495                 for (i = 0; i < cnt; i++)
          496                         if (lbuf_paragraphbeg(xb, +1, row, off))
          497                                 break;
          498                 break;
          499         case '[':
          500                 if (vi_read() != '[')
          501                         return -1;
          502                 for (i = 0; i < cnt; i++)
          503                         if (lbuf_sectionbeg(xb, -1, row, off))
          504                                 break;
          505                 break;
          506         case ']':
          507                 if (vi_read() != ']')
          508                         return -1;
          509                 for (i = 0; i < cnt; i++)
          510                         if (lbuf_sectionbeg(xb, +1, row, off))
          511                                 break;
          512                 break;
          513         case '0':
          514                 *off = 0;
          515                 break;
          516         case '^':
          517                 *off = lbuf_indents(xb, *row);
          518                 break;
          519         case '$':
          520                 *off = lbuf_eol(xb, *row);
          521                 break;
          522         case '|':
          523                 *off = vi_col2off(xb, *row, cnt - 1);
          524                 vi_pcol = cnt - 1;
          525                 break;
          526         case '/':
          527                 if (vi_search(mv, cnt, row, off))
          528                         return -1;
          529                 break;
          530         case '?':
          531                 if (vi_search(mv, cnt, row, off))
          532                         return -1;
          533                 break;
          534         case 'n':
          535                 if (vi_search(mv, cnt, row, off))
          536                         return -1;
          537                 break;
          538         case 'N':
          539                 if (vi_search(mv, cnt, row, off))
          540                         return -1;
          541                 break;
          542         case TK_CTL('a'):
          543                 if (!(cs = vi_curword(xb, *row, *off)))
          544                         return -1;
          545                 ex_kwdset(cs, +1);
          546                 vi_soset = 0;
          547                 free(cs);
          548                 if (vi_search('n', cnt, row, off))
          549                         return -1;
          550                 break;
          551         case ' ':
          552                 for (i = 0; i < cnt; i++)
          553                         if (vi_nextoff(xb, +1, row, off))
          554                                 break;
          555                 break;
          556         case 127:
          557         case TK_CTL('h'):
          558                 for (i = 0; i < cnt; i++)
          559                         if (vi_nextoff(xb, -1, row, off))
          560                                 break;
          561                 break;
          562         case '`':
          563                 if ((mark = vi_read()) <= 0)
          564                         return -1;
          565                 if (lbuf_jump(xb, mark, &mark_row, &mark_off))
          566                         return -1;
          567                 *row = mark_row;
          568                 *off = mark_off;
          569                 break;
          570         case '%':
          571                 if (lbuf_pair(xb, row, off))
          572                         return -1;
          573                 break;
          574         default:
          575                 vi_back(mv);
          576                 return 0;
          577         }
          578         return mv;
          579 }
          580 
          581 static void swap(int *a, int *b)
          582 {
          583         int t = *a;
          584         *a = *b;
          585         *b = t;
          586 }
          587 
          588 static char *lbuf_region(struct lbuf *lb, int r1, int o1, int r2, int o2)
          589 {
          590         struct sbuf *sb;
          591         char *s1, *s2, *s3;
          592         if (r1 == r2)
          593                 return uc_sub(lbuf_get(lb, r1), o1, o2);
          594         sb = sbuf_make();
          595         s1 = uc_sub(lbuf_get(lb, r1), o1, -1);
          596         s3 = uc_sub(lbuf_get(lb, r2), 0, o2);
          597         s2 = lbuf_cp(lb, r1 + 1, r2);
          598         sbuf_str(sb, s1);
          599         sbuf_str(sb, s2);
          600         sbuf_str(sb, s3);
          601         free(s1);
          602         free(s2);
          603         free(s3);
          604         return sbuf_done(sb);
          605 }
          606 
          607 static void vi_yank(int r1, int o1, int r2, int o2, int lnmode)
          608 {
          609         char *region;
          610         region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
          611         reg_put(vi_ybuf, region, lnmode);
          612         free(region);
          613         xrow = r1;
          614         xoff = lnmode ? xoff : o1;
          615 }
          616 
          617 static void vi_delete(int r1, int o1, int r2, int o2, int lnmode)
          618 {
          619         char *pref, *post;
          620         char *region;
          621         region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
          622         reg_put(vi_ybuf, region, lnmode);
          623         free(region);
          624         pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
          625         post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
          626         if (!lnmode) {
          627                 struct sbuf *sb = sbuf_make();
          628                 sbuf_str(sb, pref);
          629                 sbuf_str(sb, post);
          630                 lbuf_edit(xb, sbuf_buf(sb), r1, r2 + 1);
          631                 sbuf_free(sb);
          632         } else {
          633                 lbuf_edit(xb, NULL, r1, r2 + 1);
          634         }
          635         xrow = r1;
          636         xoff = lnmode ? lbuf_indents(xb, xrow) : o1;
          637         free(pref);
          638         free(post);
          639 }
          640 
          641 static int linecount(char *s)
          642 {
          643         int n;
          644         for (n = 0; s; n++)
          645                 if ((s = strchr(s, '\n')))
          646                         s++;
          647         return n;
          648 }
          649 
          650 static int charcount(char *text, char *post)
          651 {
          652         int tlen = strlen(text);
          653         int plen = strlen(post);
          654         char *nl = text;
          655         int i;
          656         if (tlen < plen)
          657                 return 0;
          658         for (i = 0; i < tlen - plen; i++)
          659                 if (text[i] == '\n')
          660                         nl = text + i + 1;
          661         return uc_slen(nl) - uc_slen(post);
          662 }
          663 
          664 static char *vi_input(char *pref, char *post, int *row, int *off)
          665 {
          666         char *rep = led_input(pref, post, &xkmap, ex_filetype());
          667         if (!rep)
          668                 return NULL;
          669         *row = linecount(rep) - 1;
          670         *off = charcount(rep, post) - 1;
          671         if (*off < 0)
          672                 *off = 0;
          673         return rep;
          674 }
          675 
          676 static char *vi_indents(char *ln)
          677 {
          678         struct sbuf *sb = sbuf_make();
          679         while (xai && ln && (*ln == ' ' || *ln == '\t'))
          680                 sbuf_chr(sb, *ln++);
          681         return sbuf_done(sb);
          682 }
          683 
          684 static void vi_change(int r1, int o1, int r2, int o2, int lnmode)
          685 {
          686         char *region;
          687         int row, off;
          688         char *rep;
          689         char *pref, *post;
          690         region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
          691         reg_put(vi_ybuf, region, lnmode);
          692         free(region);
          693         pref = lnmode ? vi_indents(lbuf_get(xb, r1)) : uc_sub(lbuf_get(xb, r1), 0, o1);
          694         post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
          695         vi_drawrm(r1, r2, 0);
          696         rep = vi_input(pref, post, &row, &off);
          697         if (rep) {
          698                 lbuf_edit(xb, rep, r1, r2 + 1);
          699                 xrow = r1 + row - 1;
          700                 xoff = off;
          701                 free(rep);
          702         }
          703         free(pref);
          704         free(post);
          705 }
          706 
          707 static void vi_case(int r1, int o1, int r2, int o2, int lnmode, int cmd)
          708 {
          709         char *pref, *post;
          710         char *region, *s;
          711         region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
          712         s = region;
          713         while (*s) {
          714                 int c = (unsigned char) s[0];
          715                 if (c <= 0x7f) {
          716                         if (cmd == 'u')
          717                                 s[0] = tolower(c);
          718                         if (cmd == 'U')
          719                                 s[0] = toupper(c);
          720                         if (cmd == '~')
          721                                 s[0] = islower(c) ? toupper(c) : tolower(c);
          722                 }
          723                 s = uc_next(s);
          724         }
          725         pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
          726         post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
          727         if (!lnmode) {
          728                 struct sbuf *sb = sbuf_make();
          729                 sbuf_str(sb, pref);
          730                 sbuf_str(sb, region);
          731                 sbuf_str(sb, post);
          732                 lbuf_edit(xb, sbuf_buf(sb), r1, r2 + 1);
          733                 sbuf_free(sb);
          734         } else {
          735                 lbuf_edit(xb, region, r1, r2 + 1);
          736         }
          737         xrow = r2;
          738         xoff = lnmode ? lbuf_indents(xb, r2) : o2;
          739         free(region);
          740         free(pref);
          741         free(post);
          742 }
          743 
          744 static void vi_pipe(int r1, int r2)
          745 {
          746         char *text;
          747         char *rep;
          748         int kmap = 0;
          749         char *cmd = vi_prompt("!", &kmap);
          750         if (!cmd)
          751                 return;
          752         text = lbuf_cp(xb, r1, r2 + 1);
          753         rep = cmd_pipe(cmd, text, 1, 1);
          754         if (rep)
          755                 lbuf_edit(xb, rep, r1, r2 + 1);
          756         free(cmd);
          757         free(text);
          758         free(rep);
          759 }
          760 
          761 static void vi_shift(int r1, int r2, int dir)
          762 {
          763         struct sbuf *sb;
          764         char *ln;
          765         int i;
          766         for (i = r1; i <= r2; i++) {
          767                 if (!(ln = lbuf_get(xb, i)))
          768                         continue;
          769                 sb = sbuf_make();
          770                 if (dir > 0)
          771                         sbuf_chr(sb, '\t');
          772                 else
          773                         ln = ln[0] == ' ' || ln[0] == '\t' ? ln + 1 : ln;
          774                 sbuf_str(sb, ln);
          775                 lbuf_edit(xb, sbuf_buf(sb), i, i + 1);
          776                 sbuf_free(sb);
          777         }
          778         xrow = r1;
          779         xoff = lbuf_indents(xb, xrow);
          780 }
          781 
          782 static int vc_motion(int cmd)
          783 {
          784         int r1 = xrow, r2 = xrow;        /* region rows */
          785         int o1 = xoff, o2 = xoff;        /* visual region columns */
          786         int lnmode = 0;                        /* line-based region */
          787         int mv;
          788         vi_arg2 = vi_prefix();
          789         if (vi_arg2 < 0)
          790                 return 1;
          791         o1 = ren_noeol(lbuf_get(xb, r1), o1);
          792         o2 = o1;
          793         if ((mv = vi_motionln(&r2, cmd))) {
          794                 o2 = -1;
          795         } else if (!(mv = vi_motion(&r2, &o2))) {
          796                 vi_read();
          797                 return 1;
          798         }
          799         if (mv < 0)
          800                 return 1;
          801         lnmode = o2 < 0;
          802         if (lnmode) {
          803                 o1 = 0;
          804                 o2 = lbuf_eol(xb, r2);
          805         }
          806         if (r1 > r2) {
          807                 swap(&r1, &r2);
          808                 swap(&o1, &o2);
          809         }
          810         if (r1 == r2 && o1 > o2)
          811                 swap(&o1, &o2);
          812         o1 = ren_noeol(lbuf_get(xb, r1), o1);
          813         if (!lnmode && strchr("fFtTeE%", mv))
          814                 if (o2 < lbuf_eol(xb, r2))
          815                         o2 = ren_noeol(lbuf_get(xb, r2), o2) + 1;
          816         if (cmd == 'y')
          817                 vi_yank(r1, o1, r2, o2, lnmode);
          818         if (cmd == 'd')
          819                 vi_delete(r1, o1, r2, o2, lnmode);
          820         if (cmd == 'c')
          821                 vi_change(r1, o1, r2, o2, lnmode);
          822         if (cmd == '~' || cmd == 'u' || cmd == 'U')
          823                 vi_case(r1, o1, r2, o2, lnmode, cmd);
          824         if (cmd == '!')
          825                 vi_pipe(r1, r2);
          826         if (cmd == '>' || cmd == '<')
          827                 vi_shift(r1, r2, cmd == '>' ? +1 : -1);
          828         return 0;
          829 }
          830 
          831 static int vc_insert(int cmd)
          832 {
          833         char *pref, *post;
          834         char *ln = lbuf_get(xb, xrow);
          835         int row, off = 0;
          836         char *rep;
          837         if (cmd == 'I')
          838                 xoff = lbuf_indents(xb, xrow);
          839         if (cmd == 'A')
          840                 xoff = lbuf_eol(xb, xrow);
          841         xoff = ren_noeol(ln, xoff);
          842         if (cmd == 'o')
          843                 xrow += 1;
          844         if (cmd == 'i' || cmd == 'I')
          845                 off = xoff;
          846         if (cmd == 'a' || cmd == 'A')
          847                 off = xoff + 1;
          848         if (ln && ln[0] == '\n')
          849                 off = 0;
          850         pref = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, 0, off) : vi_indents(ln);
          851         post = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, off, -1) : uc_dup("\n");
          852         vi_drawrm(xrow, xrow, cmd == 'o' || cmd == 'O');
          853         rep = vi_input(pref, post, &row, &off);
          854         if ((cmd == 'o' || cmd == 'O') && !lbuf_len(xb))
          855                 lbuf_edit(xb, "\n", 0, 0);
          856         if (rep) {
          857                 lbuf_edit(xb, rep, xrow, xrow + (cmd != 'o' && cmd != 'O'));
          858                 xrow += row - 1;
          859                 xoff = off;
          860                 free(rep);
          861         }
          862         free(pref);
          863         free(post);
          864         return !rep;
          865 }
          866 
          867 static int vc_put(int cmd)
          868 {
          869         int cnt = MAX(1, vi_arg1);
          870         int lnmode;
          871         char *buf = reg_get(vi_ybuf, &lnmode);
          872         int i;
          873         if (!buf)
          874                 snprintf(vi_msg, sizeof(vi_msg), "yank buffer empty\n");
          875         if (!buf || !buf[0])
          876                 return 1;
          877         if (lnmode) {
          878                 struct sbuf *sb = sbuf_make();
          879                 for (i = 0; i < cnt; i++)
          880                         sbuf_str(sb, buf);
          881                 if (!lbuf_len(xb))
          882                         lbuf_edit(xb, "\n", 0, 0);
          883                 if (cmd == 'p')
          884                         xrow++;
          885                 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow);
          886                 xoff = lbuf_indents(xb, xrow);
          887                 sbuf_free(sb);
          888         } else {
          889                 struct sbuf *sb = sbuf_make();
          890                 char *ln = xrow < lbuf_len(xb) ? lbuf_get(xb, xrow) : "\n";
          891                 int off = ren_noeol(ln, xoff) + (ln[0] != '\n' && cmd == 'p');
          892                 char *s = uc_sub(ln, 0, off);
          893                 sbuf_str(sb, s);
          894                 free(s);
          895                 for (i = 0; i < cnt; i++)
          896                         sbuf_str(sb, buf);
          897                 s = uc_sub(ln, off, -1);
          898                 sbuf_str(sb, s);
          899                 free(s);
          900                 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
          901                 xoff = off + uc_slen(buf) * cnt - 1;
          902                 sbuf_free(sb);
          903         }
          904         return 0;
          905 }
          906 
          907 static int join_spaces(char *prev, char *next)
          908 {
          909         int prevlen = strlen(prev);
          910         if (!prev[0])
          911                 return 0;
          912         if (prev[prevlen - 1] == ' ' || next[0] == ')')
          913                 return 0;
          914         return prev[prevlen - 1] == '.' ? 2 : 1;
          915 }
          916 
          917 static int vc_join(void)
          918 {
          919         struct sbuf *sb;
          920         int cnt = vi_arg1 <= 1 ? 2 : vi_arg1;
          921         int beg = xrow;
          922         int end = xrow + cnt;
          923         int off = 0;
          924         int i;
          925         if (!lbuf_get(xb, beg) || !lbuf_get(xb, end - 1))
          926                 return 1;
          927         sb = sbuf_make();
          928         for (i = beg; i < end; i++) {
          929                 char *ln = lbuf_get(xb, i);
          930                 char *lnend = strchr(ln, '\n');
          931                 int spaces;
          932                 if (i > beg)
          933                         while (ln[0] == ' ' || ln[0] == '\t')
          934                                 ln++;
          935                 spaces = i > beg ? join_spaces(sbuf_buf(sb), ln) : 0;
          936                 off = uc_slen(sbuf_buf(sb));
          937                 while (spaces--)
          938                         sbuf_chr(sb, ' ');
          939                 sbuf_mem(sb, ln, lnend - ln);
          940         }
          941         sbuf_chr(sb, '\n');
          942         lbuf_edit(xb, sbuf_buf(sb), beg, end);
          943         xoff = off;
          944         sbuf_free(sb);
          945         return 0;
          946 }
          947 
          948 static int vi_scrollforward(int cnt)
          949 {
          950         if (xtop >= lbuf_len(xb) - 1)
          951                 return 1;
          952         xtop = MIN(lbuf_len(xb) - 1, xtop + cnt);
          953         xrow = MAX(xrow, xtop);
          954         return 0;
          955 }
          956 
          957 static int vi_scrollbackward(int cnt)
          958 {
          959         if (xtop == 0)
          960                 return 1;
          961         xtop = MAX(0, xtop - cnt);
          962         xrow = MIN(xrow, xtop + xrows - 1);
          963         return 0;
          964 }
          965 
          966 static void vc_status(void)
          967 {
          968         int col = vi_off2col(xb, xrow, xoff);
          969         snprintf(vi_msg, sizeof(vi_msg),
          970                 "\"%s\"%c %d lines  L%d C%d\n",
          971                 ex_path()[0] ? ex_path() : "unnamed",
          972                 lbuf_modified(xb) ? '*' : ' ',
          973                 lbuf_len(xb), xrow + 1,
          974                 ren_cursor(lbuf_get(xb, xrow), col) + 1);
          975 }
          976 
          977 static void vc_charinfo(void)
          978 {
          979         char *c = uc_chr(lbuf_get(xb, xrow), xoff);
          980         if (c) {
          981                 char cbuf[8] = "";
          982                 memcpy(cbuf, c, uc_len(c));
          983                 snprintf(vi_msg, sizeof(vi_msg), "<%s> %04x\n", cbuf, uc_code(c));
          984         }
          985 }
          986 
          987 static int vc_replace(void)
          988 {
          989         int cnt = MAX(1, vi_arg1);
          990         char *cs = vi_char();
          991         char *ln = lbuf_get(xb, xrow);
          992         struct sbuf *sb;
          993         char *pref, *post;
          994         char *s;
          995         int off, i;
          996         if (!ln || !cs)
          997                 return 1;
          998         off = ren_noeol(ln, xoff);
          999         s = uc_chr(ln, off);
         1000         for (i = 0; s[0] != '\n' && i < cnt; i++)
         1001                 s = uc_next(s);
         1002         if (i < cnt)
         1003                 return 1;
         1004         pref = uc_sub(ln, 0, off);
         1005         post = uc_sub(ln, off + cnt, -1);
         1006         sb = sbuf_make();
         1007         sbuf_str(sb, pref);
         1008         for (i = 0; i < cnt; i++)
         1009                 sbuf_str(sb, cs);
         1010         sbuf_str(sb, post);
         1011         lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
         1012         off += cnt - 1;
         1013         xoff = off;
         1014         sbuf_free(sb);
         1015         free(pref);
         1016         free(post);
         1017         return 0;
         1018 }
         1019 
         1020 static char rep_cmd[4096];        /* the last command */
         1021 static int rep_len;
         1022 
         1023 static void vc_repeat(void)
         1024 {
         1025         int i;
         1026         for (i = 0; i < MAX(1, vi_arg1); i++)
         1027                 term_push(rep_cmd, rep_len);
         1028 }
         1029 
         1030 static void vc_execute(void)
         1031 {
         1032         static int exec_buf = -1;
         1033         int lnmode;
         1034         int c = vi_read();
         1035         char *buf = NULL;
         1036         int i;
         1037         if (TK_INT(c))
         1038                 return;
         1039         if (c == '@')
         1040                 c = exec_buf;
         1041         exec_buf = c;
         1042         if (exec_buf >= 0)
         1043                 buf = reg_get(exec_buf, &lnmode);
         1044         if (buf)
         1045                 for (i = 0; i < MAX(1, vi_arg1); i++)
         1046                         term_push(buf, strlen(buf));
         1047 }
         1048 
         1049 static void sigwinch(int signo)
         1050 {
         1051         vi_back(TK_CTL('l'));
         1052         vi_back(TK_CTL('c'));
         1053 }
         1054 
         1055 static void vi(void)
         1056 {
         1057         int xcol;
         1058         int mark;
         1059         char *ln;
         1060         int kmap = 0;
         1061         signal(SIGWINCH, sigwinch);
         1062         xtop = MAX(0, xrow - xrows / 2);
         1063         xoff = 0;
         1064         xcol = vi_off2col(xb, xrow, xoff);
         1065         vi_drawagain(xcol, 0);
         1066         term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow), xcol));
         1067         while (!xquit) {
         1068                 int mod = 0;        /* screen should be redrawn (1: the whole screen, 2: the current line) */
         1069                 int nrow = xrow;
         1070                 int noff = ren_noeol(lbuf_get(xb, xrow), xoff);
         1071                 int otop = xtop;
         1072                 int oleft = xleft;
         1073                 int orow = xrow;
         1074                 int mv, n;
         1075                 term_cmd(&n);
         1076                 vi_arg2 = 0;
         1077                 vi_ybuf = vi_yankbuf();
         1078                 vi_arg1 = vi_prefix();
         1079                 if (!vi_ybuf)
         1080                         vi_ybuf = vi_yankbuf();
         1081                 mv = vi_motion(&nrow, &noff);
         1082                 if (mv > 0) {
         1083                         if (strchr("\'`GHML/?{}[]nN", mv) ||
         1084                                         (mv == '%' && noff < 0)) {
         1085                                 lbuf_mark(xb, '\'', xrow, xoff);
         1086                                 lbuf_mark(xb, '`', xrow, xoff);
         1087                         }
         1088                         xrow = nrow;
         1089                         if (noff < 0 && !strchr("jk", mv))
         1090                                 noff = lbuf_indents(xb, xrow);
         1091                         if (strchr("jk", mv))
         1092                                 noff = vi_col2off(xb, xrow, xcol);
         1093                         xoff = ren_noeol(lbuf_get(xb, xrow), noff);
         1094                         if (!strchr("|jk", mv))
         1095                                 xcol = vi_off2col(xb, xrow, xoff);
         1096                         if (mv == '|')
         1097                                 xcol = vi_pcol;
         1098                 } else if (mv == 0) {
         1099                         char *cmd;
         1100                         int c = vi_read();
         1101                         int k = 0;
         1102                         if (c <= 0)
         1103                                 continue;
         1104                         lbuf_mark(xb, '*', xrow, xoff);
         1105                         switch (c) {
         1106                         case TK_CTL('b'):
         1107                                 if (vi_scrollbackward(MAX(1, vi_arg1) * (xrows - 1)))
         1108                                         break;
         1109                                 xoff = lbuf_indents(xb, xrow);
         1110                                 mod = 1;
         1111                                 break;
         1112                         case TK_CTL('f'):
         1113                                 if (vi_scrollforward(MAX(1, vi_arg1) * (xrows - 1)))
         1114                                         break;
         1115                                 xoff = lbuf_indents(xb, xrow);
         1116                                 mod = 1;
         1117                                 break;
         1118                         case TK_CTL('e'):
         1119                                 if (vi_scrollforward(MAX(1, vi_arg1)))
         1120                                         break;
         1121                                 xoff = vi_col2off(xb, xrow, xcol);
         1122                                 break;
         1123                         case TK_CTL('y'):
         1124                                 if (vi_scrollbackward(MAX(1, vi_arg1)))
         1125                                         break;
         1126                                 xoff = vi_col2off(xb, xrow, xcol);
         1127                                 break;
         1128                         case TK_CTL('u'):
         1129                                 if (xrow == 0)
         1130                                         break;
         1131                                 if (vi_arg1)
         1132                                         vi_scroll = vi_arg1;
         1133                                 n = vi_scroll ? vi_scroll : xrows / 2;
         1134                                 xrow = MAX(0, xrow - n);
         1135                                 if (xtop > 0)
         1136                                         xtop = MAX(0, xtop - n);
         1137                                 xoff = lbuf_indents(xb, xrow);
         1138                                 mod = 1;
         1139                                 break;
         1140                         case TK_CTL('d'):
         1141                                 if (xrow == lbuf_len(xb) - 1)
         1142                                         break;
         1143                                 if (vi_arg1)
         1144                                         vi_scroll = vi_arg1;
         1145                                 n = vi_scroll ? vi_scroll : xrows / 2;
         1146                                 xrow = MIN(MAX(0, lbuf_len(xb) - 1), xrow + n);
         1147                                 if (xtop < lbuf_len(xb) - xrows)
         1148                                         xtop = MIN(lbuf_len(xb) - xrows, xtop + n);
         1149                                 xoff = lbuf_indents(xb, xrow);
         1150                                 mod = 1;
         1151                                 break;
         1152                         case TK_CTL('z'):
         1153                                 term_pos(xrows, 0);
         1154                                 term_suspend();
         1155                                 mod = 1;
         1156                                 break;
         1157                         case 'u':
         1158                                 if (!lbuf_undo(xb)) {
         1159                                         lbuf_jump(xb, '*', &xrow, &xoff);
         1160                                         mod = 1;
         1161                                 } else {
         1162                                         snprintf(vi_msg, sizeof(vi_msg), "undo failed\n");
         1163                                 }
         1164                                 break;
         1165                         case TK_CTL('r'):
         1166                                 if (!lbuf_redo(xb)) {
         1167                                         lbuf_jump(xb, '*', &xrow, &xoff);
         1168                                         mod = 1;
         1169                                 } else {
         1170                                         snprintf(vi_msg, sizeof(vi_msg), "redo failed\n");
         1171                                 }
         1172                                 break;
         1173                         case TK_CTL('g'):
         1174                                 vc_status();
         1175                                 break;
         1176                         case TK_CTL('^'):
         1177                                 ex_command("e #");
         1178                                 mod = 1;
         1179                                 break;
         1180                         case ':':
         1181                                 ln = vi_prompt(":", &kmap);
         1182                                 if (ln && ln[0]) {
         1183                                         ex_command(ln);
         1184                                         mod = 1;
         1185                                 }
         1186                                 free(ln);
         1187                                 if (xquit)
         1188                                         continue;
         1189                                 break;
         1190                         case 'c':
         1191                         case 'd':
         1192                         case 'y':
         1193                         case '!':
         1194                         case '>':
         1195                         case '<':
         1196                                 if (!vc_motion(c))
         1197                                         mod = 1;
         1198                                 break;
         1199                         case 'i':
         1200                         case 'I':
         1201                         case 'a':
         1202                         case 'A':
         1203                         case 'o':
         1204                         case 'O':
         1205                                 if (!vc_insert(c))
         1206                                         mod = 1;
         1207                                 break;
         1208                         case 'J':
         1209                                 if (!vc_join())
         1210                                         mod = 1;
         1211                                 break;
         1212                         case TK_CTL('l'):
         1213                                 term_done();
         1214                                 term_init();
         1215                                 mod = 1;
         1216                                 break;
         1217                         case 'm':
         1218                                 if ((mark = vi_read()) > 0 && islower(mark))
         1219                                         lbuf_mark(xb, mark, xrow, xoff);
         1220                                 break;
         1221                         case 'p':
         1222                         case 'P':
         1223                                 if (!vc_put(c))
         1224                                         mod = 1;
         1225                                 break;
         1226                         case 'z':
         1227                                 k = vi_read();
         1228                                 switch (k) {
         1229                                 case '\n':
         1230                                         xtop = vi_arg1 ? vi_arg1 : xrow;
         1231                                         break;
         1232                                 case '.':
         1233                                         n = vi_arg1 ? vi_arg1 : xrow;
         1234                                         xtop = MAX(0, n - xrows / 2);
         1235                                         break;
         1236                                 case '-':
         1237                                         n = vi_arg1 ? vi_arg1 : xrow;
         1238                                         xtop = MAX(0, n - xrows + 1);
         1239                                         break;
         1240                                 case 'l':
         1241                                 case 'r':
         1242                                         xtd = k == 'r' ? -1 : +1;
         1243                                         break;
         1244                                 case 'L':
         1245                                 case 'R':
         1246                                         xtd = k == 'R' ? -2 : +2;
         1247                                         break;
         1248                                 case 'e':
         1249                                 case 'f':
         1250                                         xkmap = k == 'e' ? 0 : xkmap_alt;
         1251                                         break;
         1252                                 }
         1253                                 mod = 1;
         1254                                 break;
         1255                         case 'g':
         1256                                 k = vi_read();
         1257                                 if (k == '~' || k == 'u' || k == 'U')
         1258                                         if (!vc_motion(k))
         1259                                                 mod = 2;
         1260                                 if (k == 'a')
         1261                                         vc_charinfo();
         1262                                 break;
         1263                         case 'x':
         1264                                 vi_back(' ');
         1265                                 if (!vc_motion('d'))
         1266                                         mod = 2;
         1267                                 break;
         1268                         case 'X':
         1269                                 vi_back(TK_CTL('h'));
         1270                                 if (!vc_motion('d'))
         1271                                         mod = 2;
         1272                                 break;
         1273                         case 'C':
         1274                                 vi_back('$');
         1275                                 if (!vc_motion('c'))
         1276                                         mod = 1;
         1277                                 break;
         1278                         case 'D':
         1279                                 vi_back('$');
         1280                                 if (!vc_motion('d'))
         1281                                         mod = 2;
         1282                                 break;
         1283                         case 'r':
         1284                                 if (!vc_replace())
         1285                                         mod = 2;
         1286                                 break;
         1287                         case 's':
         1288                                 vi_back(' ');
         1289                                 if (!vc_motion('c'))
         1290                                         mod = 1;
         1291                                 break;
         1292                         case 'S':
         1293                                 vi_back('c');
         1294                                 if (!vc_motion('c'))
         1295                                         mod = 1;
         1296                                 break;
         1297                         case 'Y':
         1298                                 vi_back('y');
         1299                                 vc_motion('y');
         1300                                 break;
         1301                         case 'q':
         1302                                 k = vi_read();
         1303                                 if (k == 'q')
         1304                                         ex_command("q");
         1305                                 break;
         1306                         case 'Z':
         1307                                 k = vi_read();
         1308                                 if (k == 'Z')
         1309                                         ex_command("x");
         1310                                 break;
         1311                         case '~':
         1312                                 vi_back(' ');
         1313                                 if (!vc_motion('~'))
         1314                                         mod = 2;
         1315                                 break;
         1316                         case '.':
         1317                                 vc_repeat();
         1318                                 break;
         1319                         case '@':
         1320                                 vc_execute();
         1321                                 break;
         1322                         default:
         1323                                 continue;
         1324                         }
         1325                         cmd = term_cmd(&n);
         1326                         if (strchr("!<>ACDIJOPRSXYacdioprsxy~", c) ||
         1327                                         (c == 'g' && strchr("uU~", k))) {
         1328                                 if (n < sizeof(rep_cmd)) {
         1329                                         memcpy(rep_cmd, cmd, n);
         1330                                         rep_len = n;
         1331                                 }
         1332                         }
         1333                 }
         1334                 if (xrow < 0 || xrow >= lbuf_len(xb))
         1335                         xrow = lbuf_len(xb) ? lbuf_len(xb) - 1 : 0;
         1336                 if (xtop > xrow)
         1337                         xtop = xtop - xrows / 2 > xrow ?
         1338                                         MAX(0, xrow - xrows / 2) : xrow;
         1339                 if (xtop + xrows <= xrow)
         1340                         xtop = xtop + xrows + xrows / 2 <= xrow ?
         1341                                         xrow - xrows / 2 : xrow - xrows + 1;
         1342                 xoff = ren_noeol(lbuf_get(xb, xrow), xoff);
         1343                 if (mod)
         1344                         xcol = vi_off2col(xb, xrow, xoff);
         1345                 if (xcol >= xleft + xcols)
         1346                         xleft = xcol - xcols / 2;
         1347                 if (xcol < xleft)
         1348                         xleft = xcol < xcols ? 0 : xcol - xcols / 2;
         1349                 vi_wait();
         1350                 if (mod || xleft != oleft) {
         1351                         vi_drawagain(xcol, mod == 2 && xleft == oleft && xrow == orow);
         1352                 } else {
         1353                         if (xtop != otop)
         1354                                 vi_drawupdate(xcol, otop);
         1355                         if (xhll && xrow != orow && orow >= xtop && orow < xtop + xrows)
         1356                                 vi_drawrow(orow);
         1357                         if (xhll && xrow != orow)
         1358                                 vi_drawrow(xrow);
         1359                 }
         1360                 if (vi_msg[0])
         1361                         vi_drawmsg();
         1362                 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow),
         1363                                 ren_cursor(lbuf_get(xb, xrow), xcol)));
         1364                 lbuf_modified(xb);
         1365         }
         1366         term_pos(xrows, 0);
         1367         term_kill();
         1368 }
         1369 
         1370 int main(int argc, char *argv[])
         1371 {
         1372         int i;
         1373         char *prog = strchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
         1374         xvis = strcmp("ex", prog) && strcmp("neatex", prog);
         1375         for (i = 1; i < argc && argv[i][0] == '-'; i++) {
         1376                 if (argv[i][1] == 's')
         1377                         xled = 0;
         1378                 if (argv[i][1] == 'e')
         1379                         xvis = 0;
         1380                 if (argv[i][1] == 'v')
         1381                         xvis = 1;
         1382         }
         1383         dir_init();
         1384         syn_init();
         1385         if (xled || xvis)
         1386                 term_init();
         1387         if (!ex_init(argv + i)) {
         1388                 if (xvis)
         1389                         vi();
         1390                 else
         1391                         ex();
         1392                 ex_done();
         1393         }
         1394         if (xled || xvis)
         1395                 term_done();
         1396         reg_done();
         1397         syn_done();
         1398         dir_done();
         1399         return 0;
         1400 }