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 }