tex.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
       ---
       tex.c (20738B)
       ---
            1 #include <ctype.h>
            2 #include <fcntl.h>
            3 #include <stdio.h>
            4 #include <stdlib.h>
            5 #include <string.h>
            6 #include <sys/stat.h>
            7 #include <unistd.h>
            8 #include "vi.h"
            9 
           10 int xrow, xoff, xtop;                /* current row, column, and top row */
           11 int xleft;                        /* the first visible column */
           12 int xquit;                        /* exit if set */
           13 int xvis;                        /* visual mode */
           14 int xai = 1;                        /* autoindent option */
           15 int xic = 1;                        /* ignorecase option */
           16 int xaw;                        /* autowrite option */
           17 int xhl = 1;                        /* syntax highlight option */
           18 int xhll;                        /* highlight current line */
           19 int xled = 1;                        /* use the line editor */
           20 int xtd = +1;                        /* current text direction */
           21 int xshape = 1;                        /* perform letter shaping */
           22 int xorder = 1;                        /* change the order of characters */
           23 int xkmap = 0;                        /* the current keymap */
           24 int xkmap_alt = 1;                /* the alternate keymap */
           25 int xlim = 256;                        /* do not process lines longer than this */
           26 static char xkwd[EXLEN];        /* the last searched keyword */
           27 static char xrep[EXLEN];        /* the last replacement */
           28 static int xkwddir;                /* the last search direction */
           29 static int xgdep;                /* global command recursion depth */
           30 
           31 static struct buf {
           32         char ft[32];                /* file type */
           33         char *path;                /* file path */
           34         struct lbuf *lb;
           35         int row, off, top;
           36         short id, td;                /* buffer id and text direction */
           37         long mtime;                /* modification time */
           38 } bufs[8];
           39 
           40 static int bufs_cnt = 0;        /* number of allocated buffers */
           41 
           42 static int bufs_find(char *path)
           43 {
           44         int i;
           45         for (i = 0; i < LEN(bufs); i++)
           46                 if (bufs[i].lb && !strcmp(bufs[i].path, path))
           47                         return i;
           48         return -1;
           49 }
           50 
           51 static void bufs_free(int idx)
           52 {
           53         if (bufs[idx].lb) {
           54                 free(bufs[idx].path);
           55                 lbuf_free(bufs[idx].lb);
           56                 memset(&bufs[idx], 0, sizeof(bufs[idx]));
           57         }
           58 }
           59 
           60 static long mtime(char *path)
           61 {
           62         struct stat st;
           63         if (!stat(path, &st))
           64                 return st.st_mtime;
           65         return -1;
           66 }
           67 
           68 static int bufs_open(char *path)
           69 {
           70         int i;
           71         for (i = 0; i < LEN(bufs) - 1; i++)
           72                 if (!bufs[i].lb)
           73                         break;
           74         bufs_free(i);
           75         bufs[i].id = ++bufs_cnt;
           76         bufs[i].path = uc_dup(path);
           77         bufs[i].lb = lbuf_make();
           78         bufs[i].row = 0;
           79         bufs[i].off = 0;
           80         bufs[i].top = 0;
           81         bufs[i].td = +1;
           82         bufs[i].mtime = -1;
           83         strcpy(bufs[i].ft, syn_filetype(path));
           84         return i;
           85 }
           86 
           87 static void bufs_switch(int idx)
           88 {
           89         struct buf tmp;
           90         bufs[0].row = xrow;
           91         bufs[0].off = xoff;
           92         bufs[0].top = xtop;
           93         bufs[0].td = xtd;
           94         memcpy(&tmp, &bufs[idx], sizeof(tmp));
           95         memmove(&bufs[1], &bufs[0], sizeof(tmp) * idx);
           96         memcpy(&bufs[0], &tmp, sizeof(tmp));
           97         xrow = bufs[0].row;
           98         xoff = bufs[0].off;
           99         xtop = bufs[0].top;
          100         xtd = bufs[0].td;
          101 }
          102 
          103 char *ex_path(void)
          104 {
          105         return bufs[0].path;
          106 }
          107 
          108 struct lbuf *ex_lbuf(void)
          109 {
          110         return bufs[0].lb;
          111 }
          112 
          113 char *ex_filetype(void)
          114 {
          115         return bufs[0].ft;
          116 }
          117 
          118 /* replace % and # with current and alternate path names; returns a static buffer */
          119 static char *ex_pathexpand(char *src, int spaceallowed)
          120 {
          121         static char buf[1024];
          122         char *dst = buf;
          123         char *end = dst + sizeof(buf);
          124         while (dst + 1 < end && *src && *src != '\n' &&
          125                         (spaceallowed || (*src != ' ' && *src != '\t'))) {
          126                 if (*src == '%' || *src == '#') {
          127                         int idx = *src == '#';
          128                         if (!bufs[idx].path || !bufs[idx].path[0]) {
          129                                 ex_show("pathname \"%\" or \"#\" is not set\n");
          130                                 return NULL;
          131                         }
          132                         dst += snprintf(dst, end - dst, "%s", bufs[idx].path);
          133                         src++;
          134                 } else {
          135                         if (*src == '\\' && src[1])
          136                                 src++;
          137                         *dst++ = *src++;
          138                 }
          139         }
          140         if (dst + 1 >= end)
          141                 dst = end - 1;
          142         *dst = '\0';
          143         return buf;
          144 }
          145 
          146 /* the previous search keyword */
          147 int ex_kwd(char **kwd, int *dir)
          148 {
          149         if (kwd)
          150                 *kwd = xkwd;
          151         if (dir)
          152                 *dir = xkwddir;
          153         return xkwddir == 0;
          154 }
          155 
          156 /* set the previous search keyword */
          157 void ex_kwdset(char *kwd, int dir)
          158 {
          159         if (kwd) {
          160                 snprintf(xkwd, sizeof(xkwd), "%s", kwd);
          161                 reg_put('/', kwd, 0);
          162         }
          163         xkwddir = dir;
          164 }
          165 
          166 static int ex_search(char **pat)
          167 {
          168         struct sbuf *kw;
          169         char *b = *pat;
          170         char *e = b;
          171         char *pat_re;
          172         struct rstr *re;
          173         int dir, row;
          174         kw = sbuf_make();
          175         while (*++e) {
          176                 if (*e == **pat)
          177                         break;
          178                 sbuf_chr(kw, (unsigned char) *e);
          179                 if (*e == '\\' && e[1])
          180                         e++;
          181         }
          182         if (sbuf_len(kw))
          183                 ex_kwdset(sbuf_buf(kw), **pat == '/' ? 1 : -1);
          184         sbuf_free(kw);
          185         *pat = *e ? e + 1 : e;
          186         if (ex_kwd(&pat_re, &dir))
          187                 return -1;
          188         re = rstr_make(pat_re, xic ? RE_ICASE : 0);
          189         if (!re)
          190                 return -1;
          191         row = xrow + dir;
          192         while (row >= 0 && row < lbuf_len(xb)) {
          193                 if (rstr_find(re, lbuf_get(xb, row), 0, NULL, 0) >= 0)
          194                         break;
          195                 row += dir;
          196         }
          197         rstr_free(re);
          198         return row >= 0 && row < lbuf_len(xb) ? row : -1;
          199 }
          200 
          201 static int ex_lineno(char **num)
          202 {
          203         int n = xrow;
          204         switch ((unsigned char) **num) {
          205         case '.':
          206                 *num += 1;
          207                 break;
          208         case '$':
          209                 n = lbuf_len(xb) - 1;
          210                 *num += 1;
          211                 break;
          212         case '\'':
          213                 if (lbuf_jump(xb, (unsigned char) *++(*num), &n, NULL))
          214                         return -1;
          215                 *num += 1;
          216                 break;
          217         case '/':
          218         case '?':
          219                 n = ex_search(num);
          220                 break;
          221         default:
          222                 if (isdigit((unsigned char) **num)) {
          223                         n = atoi(*num) - 1;
          224                         while (isdigit((unsigned char) **num))
          225                                 *num += 1;
          226                 }
          227         }
          228         while (**num == '-' || **num == '+') {
          229                 n += atoi((*num)++);
          230                 while (isdigit((unsigned char) **num))
          231                         (*num)++;
          232         }
          233         return n;
          234 }
          235 
          236 /* parse ex command addresses */
          237 static int ex_region(char *loc, int *beg, int *end)
          238 {
          239         int naddr = 0;
          240         if (!strcmp("%", loc)) {
          241                 *beg = 0;
          242                 *end = MAX(0, lbuf_len(xb));
          243                 return 0;
          244         }
          245         if (!*loc) {
          246                 *beg = xrow;
          247                 *end = xrow == lbuf_len(xb) ? xrow : xrow + 1;
          248                 return 0;
          249         }
          250         while (*loc) {
          251                 int end0 = *end;
          252                 *end = ex_lineno(&loc) + 1;
          253                 *beg = naddr++ ? end0 - 1 : *end - 1;
          254                 if (!naddr++)
          255                         *beg = *end - 1;
          256                 while (*loc && *loc != ';' && *loc != ',')
          257                         loc++;
          258                 if (!*loc)
          259                         break;
          260                 if (*loc == ';')
          261                         xrow = *end - 1;
          262                 loc++;
          263         }
          264         if (*beg < 0 && *end == 0)
          265                 *beg = 0;
          266         if (*beg < 0 || *beg >= lbuf_len(xb))
          267                 return 1;
          268         if (*end < *beg || *end > lbuf_len(xb))
          269                 return 1;
          270         return 0;
          271 }
          272 
          273 static int ec_write(char *loc, char *cmd, char *arg);
          274 
          275 static int ex_modifiedbuffer(char *msg)
          276 {
          277         if (!lbuf_modified(xb))
          278                 return 0;
          279         if (xaw && ex_path()[0])
          280                 return ec_write("", "w", "");
          281         if (msg)
          282                 ex_show(msg);
          283         return 1;
          284 }
          285 
          286 static int ec_buffer(char *loc, char *cmd, char *arg)
          287 {
          288         char ln[128];
          289         int id;
          290         int i;
          291         id = arg[0] ? atoi(arg) : 0;
          292         for (i = 0; i < LEN(bufs) && bufs[i].lb; i++) {
          293                 if (id) {
          294                         if (id == bufs[i].id)
          295                                 break;
          296                 } else {
          297                         char c = i < 2 ? "%#"[i] : ' ';
          298                         snprintf(ln, LEN(ln), "%i %c %s",
          299                                         (int) bufs[i].id, c, bufs[i].path);
          300                         ex_print(ln);
          301                 }
          302         }
          303         if (id) {
          304                 if (i < LEN(bufs) && bufs[i].lb)
          305                         bufs_switch(i);
          306                 else
          307                         ex_show("no such buffer\n");
          308         }
          309         return 0;
          310 }
          311 
          312 static int ec_quit(char *loc, char *cmd, char *arg)
          313 {
          314         if (!strchr(cmd, '!'))
          315                 if (ex_modifiedbuffer("buffer modified\n"))
          316                         return 1;
          317         xquit = 1;
          318         return 0;
          319 }
          320 
          321 static int ec_edit(char *loc, char *cmd, char *arg)
          322 {
          323         char msg[128];
          324         char *path;
          325         int fd;
          326         if (!strchr(cmd, '!'))
          327                 if (xb && ex_modifiedbuffer("buffer modified\n"))
          328                         return 1;
          329         if (!(path = ex_pathexpand(arg, 0)))
          330                 return 1;
          331         if (path[0] && bufs_find(path) >= 0) {
          332                 bufs_switch(bufs_find(path));
          333                 return 0;
          334         }
          335         if (path[0] || !bufs[0].path)
          336                 bufs_switch(bufs_open(path));
          337         fd = open(ex_path(), O_RDONLY);
          338         if (fd >= 0) {
          339                 int rd = lbuf_rd(xb, fd, 0, lbuf_len(xb));
          340                 close(fd);
          341                 snprintf(msg, sizeof(msg), "\"%s\"  %d lines  [r]\n",
          342                                 ex_path(), lbuf_len(xb));
          343                 if (rd)
          344                         ex_show("read failed\n");
          345                 else
          346                         ex_show(msg);
          347         }
          348         lbuf_saved(xb, path[0] != '\0');
          349         bufs[0].mtime = mtime(ex_path());
          350         xrow = MAX(0, MIN(xrow, lbuf_len(xb) - 1));
          351         xoff = 0;
          352         xtop = MAX(0, MIN(xtop, lbuf_len(xb) - 1));
          353         return 0;
          354 }
          355 
          356 static int ec_read(char *loc, char *cmd, char *arg)
          357 {
          358         char msg[128];
          359         int beg, end, pos;
          360         char *path;
          361         char *obuf;
          362         int n = lbuf_len(xb);
          363         path = arg[0] ? arg : ex_path();
          364         if (ex_region(loc, &beg, &end))
          365                 return 1;
          366         pos = lbuf_len(xb) ? end : 0;
          367         if (arg[0] == '!') {
          368                 char *ecmd = ex_pathexpand(arg, 1);
          369                 if (!ecmd)
          370                         return 1;
          371                 obuf = cmd_pipe(ecmd + 1, NULL, 0, 1);
          372                 if (obuf)
          373                         lbuf_edit(xb, obuf, pos, pos);
          374                 free(obuf);
          375         } else {
          376                 int fd = open(path, O_RDONLY);
          377                 if (fd < 0) {
          378                         ex_show("read failed\n");
          379                         return 1;
          380                 }
          381                 if (lbuf_rd(xb, fd, pos, pos)) {
          382                         ex_show("read failed\n");
          383                         close(fd);
          384                         return 1;
          385                 }
          386                 close(fd);
          387         }
          388         xrow = end + lbuf_len(xb) - n - 1;
          389         snprintf(msg, sizeof(msg), "\"%s\"  %d lines  [r]\n",
          390                         path, lbuf_len(xb) - n);
          391         ex_show(msg);
          392         return 0;
          393 }
          394 
          395 static int ec_write(char *loc, char *cmd, char *arg)
          396 {
          397         char msg[128];
          398         char *path;
          399         char *ibuf;
          400         int beg, end;
          401         path = arg[0] ? arg : ex_path();
          402         if (cmd[0] == 'x' && !lbuf_modified(xb))
          403                 return ec_quit("", cmd, "");
          404         if (ex_region(loc, &beg, &end))
          405                 return 1;
          406         if (!loc[0]) {
          407                 beg = 0;
          408                 end = lbuf_len(xb);
          409         }
          410         if (arg[0] == '!') {
          411                 char *ecmd = ex_pathexpand(arg, 1);
          412                 if (!ecmd)
          413                         return 1;
          414                 ibuf = lbuf_cp(xb, beg, end);
          415                 ex_print(NULL);
          416                 cmd_pipe(ecmd + 1, ibuf, 1, 0);
          417                 free(ibuf);
          418         } else {
          419                 int fd;
          420                 if (!strchr(cmd, '!') && bufs[0].path &&
          421                                 !strcmp(bufs[0].path, path) &&
          422                                 mtime(bufs[0].path) > bufs[0].mtime) {
          423                         ex_show("write failed: file changed\n");
          424                         return 1;
          425                 }
          426                 if (!strchr(cmd, '!') && arg[0] && mtime(arg) >= 0) {
          427                         ex_show("write failed: file exists\n");
          428                         return 1;
          429                 }
          430                 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, conf_mode());
          431                 if (fd < 0) {
          432                         ex_show("write failed: cannot create file\n");
          433                         return 1;
          434                 }
          435                 if (lbuf_wr(xb, fd, beg, end)) {
          436                         ex_show("write failed\n");
          437                         close(fd);
          438                         return 1;
          439                 }
          440                 close(fd);
          441         }
          442         snprintf(msg, sizeof(msg), "\"%s\"  %d lines  [w]\n",
          443                         path, end - beg);
          444         ex_show(msg);
          445         if (!ex_path()[0]) {
          446                 free(bufs[0].path);
          447                 bufs[0].path = uc_dup(path);
          448         }
          449         if (!strcmp(ex_path(), path))
          450                 lbuf_saved(xb, 0);
          451         if (!strcmp(ex_path(), path))
          452                 bufs[0].mtime = mtime(path);
          453         if (cmd[0] == 'x' || (cmd[0] == 'w' && cmd[1] == 'q'))
          454                 ec_quit("", cmd, "");
          455         return 0;
          456 }
          457 
          458 static int ec_insert(char *loc, char *cmd, char *arg)
          459 {
          460         struct sbuf *sb;
          461         char *s;
          462         int beg, end;
          463         int n;
          464         if (ex_region(loc, &beg, &end) && (beg != 0 || end != 0))
          465                 return 1;
          466         sb = sbuf_make();
          467         while ((s = ex_read(""))) {
          468                 if (!strcmp(".", s)) {
          469                         free(s);
          470                         break;
          471                 }
          472                 sbuf_str(sb, s);
          473                 sbuf_chr(sb, '\n');
          474                 free(s);
          475         }
          476         if (cmd[0] == 'a')
          477                 if (beg + 1 <= lbuf_len(xb))
          478                         beg++;
          479         if (cmd[0] != 'c')
          480                 end = beg;
          481         n = lbuf_len(xb);
          482         lbuf_edit(xb, sbuf_buf(sb), beg, end);
          483         xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
          484         sbuf_free(sb);
          485         return 0;
          486 }
          487 
          488 static int ec_print(char *loc, char *cmd, char *arg)
          489 {
          490         int beg, end;
          491         int i;
          492         if (!cmd[0] && !loc[0])
          493                 if (xrow >= lbuf_len(xb))
          494                         return 1;
          495         if (ex_region(loc, &beg, &end))
          496                 return 1;
          497         for (i = beg; i < end; i++)
          498                 ex_print(lbuf_get(xb, i));
          499         xrow = end;
          500         xoff = 0;
          501         return 0;
          502 }
          503 
          504 static int ec_null(char *loc, char *cmd, char *arg)
          505 {
          506         int beg, end;
          507         if (!xvis)
          508                 return ec_print(loc, cmd, arg);
          509         if (ex_region(loc, &beg, &end))
          510                 return 1;
          511         xrow = MAX(beg, end - 1);
          512         xoff = 0;
          513         return 0;
          514 }
          515 
          516 static void ex_yank(int reg, int beg, int end)
          517 {
          518         char *buf = lbuf_cp(xb, beg, end);
          519         reg_put(reg, buf, 1);
          520         free(buf);
          521 }
          522 
          523 static int ec_delete(char *loc, char *cmd, char *arg)
          524 {
          525         int beg, end;
          526         if (ex_region(loc, &beg, &end) || !lbuf_len(xb))
          527                 return 1;
          528         ex_yank(arg[0], beg, end);
          529         lbuf_edit(xb, NULL, beg, end);
          530         xrow = beg;
          531         return 0;
          532 }
          533 
          534 static int ec_yank(char *loc, char *cmd, char *arg)
          535 {
          536         int beg, end;
          537         if (ex_region(loc, &beg, &end) || !lbuf_len(xb))
          538                 return 1;
          539         ex_yank(arg[0], beg, end);
          540         return 0;
          541 }
          542 
          543 static int ec_put(char *loc, char *cmd, char *arg)
          544 {
          545         int beg, end;
          546         int lnmode;
          547         char *buf;
          548         int n = lbuf_len(xb);
          549         buf = reg_get(arg[0], &lnmode);
          550         if (!buf || ex_region(loc, &beg, &end))
          551                 return 1;
          552         lbuf_edit(xb, buf, end, end);
          553         xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
          554         return 0;
          555 }
          556 
          557 static int ec_lnum(char *loc, char *cmd, char *arg)
          558 {
          559         char msg[128];
          560         int beg, end;
          561         if (ex_region(loc, &beg, &end))
          562                 return 1;
          563         sprintf(msg, "%d\n", end);
          564         ex_print(msg);
          565         return 0;
          566 }
          567 
          568 static int ec_undo(char *loc, char *cmd, char *arg)
          569 {
          570         return lbuf_undo(xb);
          571 }
          572 
          573 static int ec_redo(char *loc, char *cmd, char *arg)
          574 {
          575         return lbuf_redo(xb);
          576 }
          577 
          578 static int ec_mark(char *loc, char *cmd, char *arg)
          579 {
          580         int beg, end;
          581         if (ex_region(loc, &beg, &end))
          582                 return 1;
          583         lbuf_mark(xb, arg[0], end - 1, 0);
          584         return 0;
          585 }
          586 
          587 static void replace(struct sbuf *dst, char *rep, char *ln, int *offs)
          588 {
          589         while (rep[0]) {
          590                 if (rep[0] == '\\' && rep[1]) {
          591                         if (rep[1] >= '0' && rep[1] <= '9') {
          592                                 int grp = (rep[1] - '0') * 2;
          593                                 int len = offs[grp + 1] - offs[grp];
          594                                 sbuf_mem(dst, ln + offs[grp], len);
          595                         } else {
          596                                 sbuf_chr(dst, (unsigned char) rep[1]);
          597                         }
          598                         rep++;
          599                 } else {
          600                         sbuf_chr(dst, (unsigned char) rep[0]);
          601                 }
          602                 rep++;
          603         }
          604 }
          605 
          606 static int ec_substitute(char *loc, char *cmd, char *arg)
          607 {
          608         struct rstr *re;
          609         int offs[32];
          610         int beg, end;
          611         char *pat = NULL, *rep = NULL;
          612         char *s = arg;
          613         int i;
          614         if (ex_region(loc, &beg, &end))
          615                 return 1;
          616         pat = re_read(&s);
          617         if (pat && pat[0])
          618                 ex_kwdset(pat, +1);
          619         if (pat && *s) {
          620                 s--;
          621                 rep = re_read(&s);
          622         }
          623         if (pat || rep)
          624                 snprintf(xrep, sizeof(xrep), "%s", rep ? rep : "");
          625         free(pat);
          626         free(rep);
          627         if (ex_kwd(&pat, NULL))
          628                 return 1;
          629         re = rstr_make(pat, xic ? RE_ICASE : 0);
          630         if (!re)
          631                 return 1;
          632         for (i = beg; i < end; i++) {
          633                 char *ln = lbuf_get(xb, i);
          634                 struct sbuf *r = NULL;
          635                 while (rstr_find(re, ln, LEN(offs) / 2, offs, 0) >= 0) {
          636                         if (!r)
          637                                 r = sbuf_make();
          638                         sbuf_mem(r, ln, offs[0]);
          639                         replace(r, xrep, ln, offs);
          640                         ln += offs[1];
          641                         if (offs[1] <= 0)        /* zero-length match */
          642                                 sbuf_chr(r, (unsigned char) *ln++);
          643                         if (!*ln || *ln == '\n' || !strchr(s, 'g'))
          644                                 break;
          645                 }
          646                 if (r) {
          647                         sbuf_str(r, ln);
          648                         lbuf_edit(xb, sbuf_buf(r), i, i + 1);
          649                         sbuf_free(r);
          650                 }
          651         }
          652         rstr_free(re);
          653         return 0;
          654 }
          655 
          656 static int ec_exec(char *loc, char *cmd, char *arg)
          657 {
          658         int beg, end;
          659         char *text;
          660         char *rep;
          661         char *ecmd;
          662         ex_modifiedbuffer(NULL);
          663         if (!(ecmd = ex_pathexpand(arg, 1)))
          664                 return 1;
          665         if (!loc[0]) {
          666                 ex_print(NULL);
          667                 return cmd_exec(ecmd);
          668         }
          669         if (ex_region(loc, &beg, &end))
          670                 return 1;
          671         text = lbuf_cp(xb, beg, end);
          672         rep = cmd_pipe(ecmd, text, 1, 1);
          673         if (rep)
          674                 lbuf_edit(xb, rep, beg, end);
          675         free(text);
          676         free(rep);
          677         return 0;
          678 }
          679 
          680 static int ec_make(char *loc, char *cmd, char *arg)
          681 {
          682         char make[EXLEN];
          683         char *target;
          684         ex_modifiedbuffer(NULL);
          685         if (!(target = ex_pathexpand(arg, 0)))
          686                 return 1;
          687         sprintf(make, "make %s", target);
          688         ex_print(NULL);
          689         if (cmd_exec(make))
          690                 return 1;
          691         return 0;
          692 }
          693 
          694 static int ec_ft(char *loc, char *cmd, char *arg)
          695 {
          696         if (arg[0])
          697                 snprintf(bufs[0].ft, sizeof(bufs[0].ft), "%s", arg);
          698         else
          699                 ex_print(ex_filetype());
          700         return 0;
          701 }
          702 
          703 static int ec_cmap(char *loc, char *cmd, char *arg)
          704 {
          705         if (arg[0])
          706                 xkmap_alt = conf_kmapfind(arg);
          707         else
          708                 ex_print(conf_kmap(xkmap)[0]);
          709         if (arg[0] && !strchr(cmd, '!'))
          710                 xkmap = xkmap_alt;
          711         return 0;
          712 }
          713 
          714 static int ex_exec(char *ln);
          715 
          716 static int ec_glob(char *loc, char *cmd, char *arg)
          717 {
          718         struct rstr *re;
          719         int offs[32];
          720         int beg, end, not;
          721         char *pat;
          722         char *s = arg;
          723         int i;
          724         if (!loc[0] && !xgdep)
          725                 strcpy(loc, "%");
          726         if (ex_region(loc, &beg, &end))
          727                 return 1;
          728         not = strchr(cmd, '!') || cmd[0] == 'v';
          729         pat = re_read(&s);
          730         if (pat && pat[0])
          731                 ex_kwdset(pat, +1);
          732         free(pat);
          733         if (ex_kwd(&pat, NULL))
          734                 return 1;
          735         if (!(re = rstr_make(pat, xic ? RE_ICASE : 0)))
          736                 return 1;
          737         xgdep++;
          738         for (i = beg + 1; i < end; i++)
          739                 lbuf_globset(xb, i, xgdep);
          740         i = beg;
          741         while (i < lbuf_len(xb)) {
          742                 char *ln = lbuf_get(xb, i);
          743                 if ((rstr_find(re, ln, LEN(offs) / 2, offs, 0) < 0) == not) {
          744                         xrow = i;
          745                         if (ex_exec(s))
          746                                 break;
          747                         i = MIN(i, xrow);
          748                 }
          749                 while (i < lbuf_len(xb) && !lbuf_globget(xb, i, xgdep))
          750                         i++;
          751         }
          752         for (i = 0; i < lbuf_len(xb); i++)
          753                 lbuf_globget(xb, i, xgdep);
          754         xgdep--;
          755         rstr_free(re);
          756         return 0;
          757 }
          758 
          759 static struct option {
          760         char *abbr;
          761         char *name;
          762         int *var;
          763 } options[] = {
          764         {"ai", "autoindent", &xai},
          765         {"aw", "autowrite", &xaw},
          766         {"ic", "ignorecase", &xic},
          767         {"td", "textdirection", &xtd},
          768         {"shape", "shape", &xshape},
          769         {"order", "xorder", &xorder},
          770         {"hl", "highlight", &xhl},
          771         {"hll", "highlightline", &xhll},
          772         {"lim", "linelimit", &xlim},
          773 };
          774 
          775 static char *cutword(char *s, char *d)
          776 {
          777         while (isspace(*s))
          778                 s++;
          779         while (*s && !isspace(*s))
          780                 *d++ = *s++;
          781         while (isspace(*s))
          782                 s++;
          783         *d = '\0';
          784         return s;
          785 }
          786 
          787 static int ec_set(char *loc, char *cmd, char *arg)
          788 {
          789         char tok[EXLEN];
          790         char opt[EXLEN];
          791         char *s = arg;
          792         int val = 0;
          793         int i;
          794         if (*s) {
          795                 s = cutword(s, tok);
          796                 if (tok[0] == 'n' && tok[1] == 'o') {
          797                         strcpy(opt, tok + 2);
          798                         val = 0;
          799                 } else {
          800                         char *r = strchr(tok, '=');
          801                         if (r) {
          802                                 *r = '\0';
          803                                 strcpy(opt, tok);
          804                                 val = atoi(r + 1);
          805                         } else {
          806                                 strcpy(opt, tok);
          807                                 val = 1;
          808                         }
          809                 }
          810                 for (i = 0; i < LEN(options); i++) {
          811                         struct option *o = &options[i];
          812                         if (!strcmp(o->abbr, opt) || !strcmp(o->name, opt)) {
          813                                 *o->var = val;
          814                                 return 0;
          815                         }
          816                 }
          817                 ex_show("unknown option");
          818                 return 1;
          819         }
          820         return 0;
          821 }
          822 
          823 static struct excmd {
          824         char *abbr;
          825         char *name;
          826         int (*ec)(char *loc, char *cmd, char *arg);
          827 } excmds[] = {
          828         {"b", "buffer", ec_buffer},
          829         {"p", "print", ec_print},
          830         {"a", "append", ec_insert},
          831         {"i", "insert", ec_insert},
          832         {"d", "delete", ec_delete},
          833         {"c", "change", ec_insert},
          834         {"e", "edit", ec_edit},
          835         {"e!", "edit!", ec_edit},
          836         {"g", "global", ec_glob},
          837         {"g!", "global!", ec_glob},
          838         {"=", "=", ec_lnum},
          839         {"k", "mark", ec_mark},
          840         {"pu", "put", ec_put},
          841         {"q", "quit", ec_quit},
          842         {"q!", "quit!", ec_quit},
          843         {"r", "read", ec_read},
          844         {"v", "vglobal", ec_glob},
          845         {"w", "write", ec_write},
          846         {"w!", "write!", ec_write},
          847         {"wq", "wq", ec_write},
          848         {"wq!", "wq!", ec_write},
          849         {"u", "undo", ec_undo},
          850         {"redo", "redo", ec_redo},
          851         {"se", "set", ec_set},
          852         {"s", "substitute", ec_substitute},
          853         {"x", "xit", ec_write},
          854         {"x!", "xit!", ec_write},
          855         {"ya", "yank", ec_yank},
          856         {"!", "!", ec_exec},
          857         {"make", "make", ec_make},
          858         {"ft", "filetype", ec_ft},
          859         {"cm", "cmap", ec_cmap},
          860         {"cm!", "cmap!", ec_cmap},
          861         {"", "", ec_null},
          862 };
          863 
          864 static int ex_idx(char *cmd)
          865 {
          866         int i;
          867         for (i = 0; i < LEN(excmds); i++)
          868                 if (!strcmp(excmds[i].abbr, cmd) || !strcmp(excmds[i].name, cmd))
          869                         return i;
          870         return -1;
          871 }
          872 
          873 /* read ex command addresses */
          874 static char *ex_loc(char *src, char *loc)
          875 {
          876         while (*src == ':' || *src == ' ' || *src == '\t')
          877                 src++;
          878         while (*src && !isalpha((unsigned char) *src) && *src != '=' && *src != '!') {
          879                 if (*src == '\'')
          880                         *loc++ = *src++;
          881                 if (*src == '/' || *src == '?') {
          882                         int d = *src;
          883                         *loc++ = *src++;
          884                         while (*src && *src != d) {
          885                                 if (*src == '\\' && src[1])
          886                                         *loc++ = *src++;
          887                                 *loc++ = *src++;
          888                         }
          889                 }
          890                 if (*src)
          891                         *loc++ = *src++;
          892         }
          893         *loc = '\0';
          894         return src;
          895 }
          896 
          897 /* read ex command name */
          898 static char *ex_cmd(char *src, char *cmd)
          899 {
          900         char *cmd0 = cmd;
          901         while (*src == ' ' || *src == '\t')
          902                 src++;
          903         while (isalpha((unsigned char) *src) && cmd < cmd0 + 16)
          904                 if ((*cmd++ = *src++) == 'k' && cmd == cmd0 + 1)
          905                         break;
          906         if (*src == '!' || *src == '=')
          907                 *cmd++ = *src++;
          908         *cmd = '\0';
          909         return src;
          910 }
          911 
          912 /* read ex command argument for excmd command */
          913 static char *ex_arg(char *src, char *dst, char *excmd)
          914 {
          915         int c0 = excmd[0];
          916         int c1 = excmd[1];
          917         while (*src == ' ' || *src == '\t')
          918                 src++;
          919         if (c0 == '!' || c0 == 'g' || c0 == 'v' ||
          920                         ((c0 == 'r' || c0 == 'w') && src[0] == '!')) {
          921                 while (*src && *src != '\n') {
          922                         if (*src == '\\' && src[1])
          923                                 *dst++ = *src++;
          924                         *dst++ = *src++;
          925                 }
          926         } else if ((c0 == 's' && c1 != 'e') || c0 == '&' || c0 == '~') {
          927                 int delim = *src;
          928                 int cnt = 2;
          929                 *dst++ = *src++;
          930                 while (*src && *src != '\n' && cnt > 0) {
          931                         if (*src == delim)
          932                                 cnt--;
          933                         if (*src == '\\' && src[1])
          934                                 *dst++ = *src++;
          935                         *dst++ = *src++;
          936                 }
          937         }
          938         while (*src && *src != '\n' && *src != '|' && *src != '"') {
          939                 if (*src == '\\' && src[1])
          940                         *dst++ = *src++;
          941                 *dst++ = *src++;
          942         }
          943         if (*src == '"') {
          944                 while (*src != '\n')
          945                         src++;
          946         }
          947         if (*src == '\n' || *src == '|')
          948                 src++;
          949         *dst = '\0';
          950         return src;
          951 }
          952 
          953 /* execute a single ex command */
          954 static int ex_exec(char *ln)
          955 {
          956         char loc[EXLEN], cmd[EXLEN], arg[EXLEN];
          957         int ret = 0;
          958         if (strlen(ln) >= EXLEN) {
          959                 ex_show("command too long");
          960                 return 1;
          961         }
          962         while (*ln) {
          963                 int idx;
          964                 ln = ex_loc(ln, loc);
          965                 ln = ex_cmd(ln, cmd);
          966                 idx = ex_idx(cmd);
          967                 ln = ex_arg(ln, arg, idx >= 0 ? excmds[idx].abbr : "unknown");
          968                 if (idx >= 0)
          969                         ret = excmds[idx].ec(loc, cmd, arg);
          970         }
          971         return ret;
          972 }
          973 
          974 /* execute a single ex command */
          975 void ex_command(char *ln)
          976 {
          977         ex_exec(ln);
          978         lbuf_modified(xb);
          979         reg_put(':', ln, 0);
          980 }
          981 
          982 /* ex main loop */
          983 void ex(void)
          984 {
          985         while (!xquit) {
          986                 char *ln = ex_read(":");
          987                 if (ln)
          988                         ex_command(ln);
          989                 free(ln);
          990         }
          991 }
          992 
          993 int ex_init(char **files)
          994 {
          995         char arg[EXLEN];
          996         char *s = arg;
          997         char *r = files[0] ? files[0] : "";
          998         while (*r && s + 2 < arg + sizeof(arg)) {
          999                 if (*r == ' ' || *r == '%' || *r == '#')
         1000                         *s++ = '\\';
         1001                 *s++ = *r++;
         1002         }
         1003         *s = '\0';
         1004         if (ec_edit("", "e", arg))
         1005                 return 1;
         1006         if (getenv("EXINIT"))
         1007                 ex_command(getenv("EXINIT"));
         1008         return 0;
         1009 }
         1010 
         1011 void ex_done(void)
         1012 {
         1013         int i;
         1014         for (i = 0; i < LEN(bufs); i++)
         1015                 bufs_free(i);
         1016 }