vi: update the dimensions of a single window when the terminal is resized
[neatvi.git] / ex.c
blob27c6e3534ea61b0e5a6e052cc989eae3a1104a1a
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"
10 #define REG(s) ((s)[0] != '\\' ? (unsigned char) (s)[0] : 0x80 | (unsigned char) (s)[1])
12 int xrow, xoff, xtop; /* current row, column, and top row */
13 int xleft; /* the first visible column */
14 int xquit; /* exit if set */
15 int xvis; /* visual mode */
16 int xai = 1; /* autoindent option */
17 int xic = 1; /* ignorecase option */
18 int xaw; /* autowrite option */
19 int xwa; /* writeany option */
20 int xhl = 1; /* syntax highlight option */
21 int xhll; /* highlight current line */
22 int xled = 1; /* use the line editor */
23 int xtd = +1; /* current text direction */
24 int xshape = 1; /* perform letter shaping */
25 int xorder = 1; /* change the order of characters */
26 int xkmap = 0; /* the current keymap */
27 int xkmap_alt = 1; /* the alternate keymap */
28 int xlim = 256; /* do not process lines longer than this */
29 int xru = 1; /* show line number */
30 int xhist = 0; /* number of history lines */
31 static char xkwd[EXLEN]; /* the last searched keyword */
32 static char xrep[EXLEN]; /* the last replacement */
33 static int xkwddir; /* the last search direction */
34 static int xgdep; /* global command recursion depth */
35 static char **next; /* argument list */
36 static int next_pos; /* position in argument list */
38 static struct buf {
39 char ft[32]; /* file type */
40 char *path; /* file path */
41 struct lbuf *lb;
42 int row, off, top, left;
43 short id; /* buffer number */
44 short td; /* text direction */
45 long mtime; /* modification time */
46 } bufs[8];
48 static int bufs_cnt = 0; /* number of allocated buffers */
50 static void bufs_free(int idx)
52 if (bufs[idx].lb) {
53 free(bufs[idx].path);
54 lbuf_free(bufs[idx].lb);
55 memset(&bufs[idx], 0, sizeof(bufs[idx]));
59 static int bufs_find(char *path)
61 int i;
62 path = path[0] == '/' && path[1] == '\0' ? "" : path;
63 for (i = 0; i < LEN(bufs); i++)
64 if (bufs[i].path && !strcmp(bufs[i].path, path))
65 return i;
66 return -1;
69 static int bufs_findroom(void)
71 int i;
72 for (i = 0; i < LEN(bufs) - 1; i++)
73 if (!bufs[i].lb)
74 break;
75 return i;
78 static void bufs_init(int idx, char *path)
80 bufs_free(idx);
81 bufs[idx].id = ++bufs_cnt;
82 bufs[idx].path = uc_dup(path);
83 bufs[idx].lb = lbuf_make();
84 bufs[idx].row = 0;
85 bufs[idx].off = 0;
86 bufs[idx].top = 0;
87 bufs[idx].left = 0;
88 bufs[idx].td = +1;
89 bufs[idx].mtime = -1;
90 strcpy(bufs[idx].ft, syn_filetype(path));
93 static int bufs_open(char *path)
95 int idx = bufs_findroom();
96 bufs_init(idx, path);
97 return idx;
100 static void bufs_save(void)
102 bufs[0].row = xrow;
103 bufs[0].off = xoff;
104 bufs[0].top = xtop;
105 bufs[0].left = xleft;
106 bufs[0].td = xtd;
109 static void bufs_load(void)
111 xrow = bufs[0].row;
112 xoff = bufs[0].off;
113 xtop = bufs[0].top;
114 xleft = bufs[0].left;
115 xtd = bufs[0].td;
116 reg_put('%', bufs[0].path ? bufs[0].path : "", 0);
119 static void bufs_shift(void)
121 bufs_free(0);
122 memmove(&bufs[0], &bufs[1], sizeof(bufs) - sizeof(bufs[0]));
123 memset(&bufs[LEN(bufs) - 1], 0, sizeof(bufs[0]));
124 bufs_load();
127 static void bufs_switch(int idx)
129 struct buf tmp;
130 bufs_save();
131 memcpy(&tmp, &bufs[idx], sizeof(tmp));
132 memmove(&bufs[1], &bufs[0], sizeof(tmp) * idx);
133 memcpy(&bufs[0], &tmp, sizeof(tmp));
134 bufs_load();
137 static void bufs_number(void)
139 int n = 0;
140 int i;
141 for (i = 0; i < LEN(bufs); i++)
142 if (bufs[i].lb != NULL)
143 n++;
144 bufs_cnt = n;
145 for (i = 0; i < LEN(bufs); i++)
146 if (bufs[i].lb != NULL)
147 bufs[i].id = n--;
150 static long mtime(char *path)
152 struct stat st;
153 if (!stat(path, &st))
154 return st.st_mtime;
155 return -1;
158 char *ex_path(void)
160 return bufs[0].path;
163 struct lbuf *ex_lbuf(void)
165 return bufs[0].lb;
168 char *ex_filetype(void)
170 return bufs[0].ft;
173 /* replace % and # with current and alternate path names; returns a static buffer */
174 static char *ex_pathexpand(char *src, int spaceallowed)
176 static char buf[1024];
177 char *dst = buf;
178 char *end = dst + sizeof(buf);
179 while (dst + 1 < end && *src && *src != '\n' &&
180 (spaceallowed || (*src != ' ' && *src != '\t'))) {
181 if (*src == '%' || *src == '#') {
182 int idx = *src == '#';
183 if (!bufs[idx].path || !bufs[idx].path[0]) {
184 ex_show("pathname \"%\" or \"#\" is not set");
185 return NULL;
187 dst += snprintf(dst, end - dst, "%s", bufs[idx].path);
188 src++;
189 } else if (dst == buf && *src == '=') {
190 char *cur = bufs[0].path;
191 char *dir = cur != NULL ? strrchr(cur, '/') : NULL;
192 if (cur != NULL && dir != NULL) {
193 int len = MIN(dir - cur, end - dst - 2);
194 memcpy(dst, cur, len);
195 dst += len;
196 *dst++ = '/';
198 src++;
199 } else {
200 if (*src == '\\' && src[1])
201 src++;
202 *dst++ = *src++;
205 if (dst + 1 >= end)
206 dst = end - 1;
207 *dst = '\0';
208 return buf;
211 /* read :e +cmd arguments */
212 static char *ex_plus(char *src, char *dst)
214 while (*src == ' ')
215 src++;
216 *dst = '\0';
217 if (*src != '+')
218 return src;
219 while (*src && *src != ' ') {
220 if (src[0] == '\\' && src[1])
221 src++;
222 *dst++ = *src++;
224 *dst = '\0';
225 while (*src == ' ' || *src == '\t')
226 src++;
227 return src;
230 /* the previous search keyword */
231 int ex_kwd(char **kwd, int *dir)
233 if (kwd)
234 *kwd = xkwd;
235 if (dir)
236 *dir = xkwddir;
237 return xkwddir == 0;
240 /* set the previous search keyword */
241 void ex_kwdset(char *kwd, int dir)
243 if (kwd)
244 snprintf(xkwd, sizeof(xkwd), "%s", kwd);
245 xkwddir = dir;
248 static int ex_search(char **pat)
250 char *pat_re;
251 struct rstr *re;
252 int dir, row;
253 int delim = **pat;
254 char *kw = re_read(pat);
255 if (kw != NULL && *kw)
256 ex_kwdset(kw, delim == '/' ? 1 : -1);
257 free(kw);
258 if (ex_kwd(&pat_re, &dir))
259 return -1;
260 re = rstr_make(pat_re, xic ? RE_ICASE : 0);
261 if (!re)
262 return -1;
263 row = xrow + dir;
264 while (row >= 0 && row < lbuf_len(xb)) {
265 if (rstr_find(re, lbuf_get(xb, row), 0, NULL, 0) >= 0)
266 break;
267 row += dir;
269 rstr_free(re);
270 return row >= 0 && row < lbuf_len(xb) ? row : -1;
273 static int ex_lineno(char **num)
275 int n = xrow;
276 switch ((unsigned char) **num) {
277 case '.':
278 ++*num;
279 break;
280 case '$':
281 n = lbuf_len(xb) - 1;
282 ++*num;
283 break;
284 case '\'':
285 if (lbuf_jump(xb, (unsigned char) *++(*num), &n, NULL))
286 return -1;
287 ++*num;
288 break;
289 case '/':
290 case '?':
291 n = ex_search(num);
292 break;
293 default:
294 if (isdigit((unsigned char) **num)) {
295 n = atoi(*num) - 1;
296 while (isdigit((unsigned char) **num))
297 ++*num;
300 while (**num == '-' || **num == '+') {
301 n += atoi((*num)++);
302 while (isdigit((unsigned char) **num))
303 (*num)++;
305 return n;
308 /* parse ex command addresses */
309 static int ex_region(char *loc, int *beg, int *end)
311 int naddr = 0;
312 if (!strcmp("%", loc)) {
313 *beg = 0;
314 *end = MAX(0, lbuf_len(xb));
315 return 0;
317 if (!*loc) {
318 *beg = xrow;
319 *end = xrow == lbuf_len(xb) ? xrow : xrow + 1;
320 return 0;
322 while (*loc) {
323 int end0 = *end;
324 *end = ex_lineno(&loc) + 1;
325 *beg = naddr++ ? end0 - 1 : *end - 1;
326 if (!naddr++)
327 *beg = *end - 1;
328 while (*loc && *loc != ';' && *loc != ',')
329 loc++;
330 if (!*loc)
331 break;
332 if (*loc == ';')
333 xrow = *end - 1;
334 loc++;
336 if (*beg < 0 && *end == 0)
337 *beg = 0;
338 if (*beg < 0 || *beg >= lbuf_len(xb))
339 return 1;
340 if (*end < *beg || *end > lbuf_len(xb))
341 return 1;
342 return 0;
345 static int ec_write(char *loc, char *cmd, char *arg, char *txt);
347 static int ex_modifiedbuffer(char *msg)
349 if (!lbuf_modified(xb))
350 return 0;
351 if (xaw && ex_path()[0])
352 return ec_write("", "w", "", NULL);
353 if (msg)
354 ex_show(msg);
355 return 1;
358 static int ec_buffer(char *loc, char *cmd, char *arg, char *txt)
360 char *aliases = "%#^";
361 char ln[128];
362 int i;
363 if (!arg[0]) {
364 /* print buffer list */
365 for (i = 0; i < LEN(bufs) && bufs[i].lb; i++) {
366 char c = i < strlen(aliases) ? aliases[i] : ' ';
367 char m = lbuf_modified(bufs[i].lb) ? '*' : ' ';
368 snprintf(ln, LEN(ln), "%2i %c %s %c",
369 (int) bufs[i].id, c, bufs[i].path, m);
370 ex_print(ln);
372 } else if (arg[0] == '!') {
373 /* delete buffer */
374 bufs_shift();
375 if (bufs[0].lb == NULL)
376 bufs_init(0, "");
377 } else if (arg[0] == '~') {
378 /* reassign buffer ids */
379 bufs_number();
380 } else {
381 int id = arg[0] ? atoi(arg) : 0;
382 int idx = -1;
383 /* switch to the given buffer */
384 if (isdigit((unsigned char) arg[0])) { /* buffer id given */
385 for (idx = 0; idx < LEN(bufs); idx++)
386 if (bufs[idx].lb && id == bufs[idx].id)
387 break;
388 } else if (arg[0] == '-') { /* previous buffer */
389 for (i = 0; i < LEN(bufs); i++)
390 if (bufs[i].lb && bufs[i].id < bufs[0].id)
391 if (idx < 0 || bufs[i].id > bufs[idx].id)
392 idx = i;
393 } else if (arg[0] == '+') { /* next buffer */
394 for (i = 0; i < LEN(bufs); i++)
395 if (bufs[i].lb && bufs[i].id > bufs[0].id)
396 if (idx < 0 || bufs[i].id < bufs[idx].id)
397 idx = i;
398 } else { /* buffer alias given */
399 char *r = strchr(aliases, (unsigned char) arg[0]);
400 idx = r ? r - aliases : -1;
402 if (idx >= 0 && idx < LEN(bufs) && bufs[idx].lb) {
403 if (xb && !xwa && strchr(cmd, '!') == NULL)
404 if (ex_modifiedbuffer("buffer modified"))
405 return 1;
406 bufs_switch(idx);
407 } else {
408 ex_show("no such buffer");
409 return 1;
412 return 0;
415 static int ec_quit(char *loc, char *cmd, char *arg, char *txt)
417 if (!strchr(cmd, '!'))
418 if (ex_modifiedbuffer("buffer modified"))
419 return 1;
420 xquit = 1;
421 return 0;
424 static int ec_edit(char *loc, char *cmd, char *arg, char *txt)
426 char pls[EXLEN];
427 char msg[128];
428 char *path;
429 int fd;
430 if (!strchr(cmd, '!'))
431 if (xb && !xwa && ex_modifiedbuffer("buffer modified"))
432 return 1;
433 arg = ex_plus(arg, pls);
434 if (!(path = ex_pathexpand(arg, 0)))
435 return 1;
436 /* ew: switch buffer without changing # */
437 if (path[0] && cmd[0] == 'e' && cmd[1] == 'w' && bufs_find(path) > 1)
438 bufs_switch(1);
439 /* check if the buffer is already available */
440 if (path[0] && bufs_find(path) >= 0) {
441 bufs_switch(bufs_find(path));
442 if (pls[0] == '+')
443 return ex_command(pls + 1);
444 return 0;
446 if (path[0] || !bufs[0].path)
447 bufs_switch(bufs_open(path));
448 fd = open(ex_path(), O_RDONLY);
449 if (fd >= 0) {
450 int rd = lbuf_rd(xb, fd, 0, lbuf_len(xb));
451 close(fd);
452 snprintf(msg, sizeof(msg), "\"%s\" [=%d] [r]",
453 ex_path(), lbuf_len(xb));
454 if (rd)
455 ex_show("read failed");
456 else
457 ex_show(msg);
459 lbuf_saved(xb, path[0] != '\0');
460 bufs[0].mtime = mtime(ex_path());
461 xrow = MAX(0, MIN(xrow, lbuf_len(xb) - 1));
462 xoff = 0;
463 xtop = MAX(0, MIN(xtop, lbuf_len(xb) - 1));
464 if (pls[0] == '+')
465 return ex_command(pls + 1);
466 return 0;
469 static int ex_next(char *cmd, int dis)
471 char arg[EXLEN];
472 int old = next_pos;
473 int idx = next != NULL && next[old] != NULL ? next_pos + dis : -1;
474 char *path = idx >= 0 && next[idx] != NULL ? next[idx] : NULL;
475 char *s = arg;
476 char *r = path != NULL ? path : "";
477 if (dis && path == NULL) {
478 ex_show("no more files");
479 return 1;
481 while (*r && s + 2 < arg + sizeof(arg)) {
482 if (*r == ' ' || *r == '%' || *r == '#' || *r == '=')
483 *s++ = '\\';
484 *s++ = *r++;
486 *s = '\0';
487 if (ec_edit("", cmd, arg, NULL))
488 return 1;
489 next_pos = idx;
490 return 0;
493 static int ec_next(char *loc, char *cmd, char *arg, char *txt)
495 return ex_next(cmd, +1);
498 static int ec_prev(char *loc, char *cmd, char *arg, char *txt)
500 return ex_next(cmd, -1);
503 static int ec_read(char *loc, char *cmd, char *arg, char *txt)
505 char msg[128];
506 int beg, end, pos;
507 char *path;
508 char *obuf;
509 int n = lbuf_len(xb);
510 path = arg[0] ? ex_pathexpand(arg, 1) : ex_path();
511 if (ex_region(loc, &beg, &end) || path == NULL)
512 return 1;
513 pos = lbuf_len(xb) ? end : 0;
514 if (path[0] == '!') {
515 if (!path[1])
516 return 1;
517 obuf = cmd_pipe(path + 1, NULL, 1);
518 if (obuf)
519 lbuf_edit(xb, obuf, pos, pos);
520 free(obuf);
521 } else {
522 int fd = open(path, O_RDONLY);
523 if (fd < 0) {
524 ex_show("read failed");
525 return 1;
527 if (lbuf_rd(xb, fd, pos, pos)) {
528 ex_show("read failed");
529 close(fd);
530 return 1;
532 close(fd);
534 xrow = end + lbuf_len(xb) - n - 1;
535 snprintf(msg, sizeof(msg), "\"%s\" [=%d] [r]",
536 path, lbuf_len(xb) - n);
537 ex_show(msg);
538 return 0;
541 static int ec_write(char *loc, char *cmd, char *arg, char *txt)
543 char msg[128];
544 char *path;
545 char *ibuf;
546 int beg, end;
547 path = arg[0] ? ex_pathexpand(arg, 1) : ex_path();
548 if (cmd[0] == 'x' && !lbuf_modified(xb))
549 return ec_quit("", cmd, "", NULL);
550 if (ex_region(loc, &beg, &end) || path == NULL)
551 return 1;
552 if (!loc[0]) {
553 beg = 0;
554 end = lbuf_len(xb);
556 if (path[0] == '!') {
557 if (!path[1])
558 return 1;
559 ibuf = lbuf_cp(xb, beg, end);
560 ex_print(NULL);
561 cmd_pipe(path + 1, ibuf, 0);
562 free(ibuf);
563 } else {
564 int fd;
565 if (!strchr(cmd, '!') && bufs[0].path &&
566 !strcmp(bufs[0].path, path) &&
567 mtime(bufs[0].path) > bufs[0].mtime) {
568 ex_show("write failed: file changed");
569 return 1;
571 if (!strchr(cmd, '!') && arg[0] && mtime(arg) >= 0) {
572 ex_show("write failed: file exists");
573 return 1;
575 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, conf_mode());
576 if (fd < 0) {
577 ex_show("write failed: cannot create file");
578 return 1;
580 if (lbuf_wr(xb, fd, beg, end)) {
581 ex_show("write failed");
582 close(fd);
583 return 1;
585 close(fd);
587 snprintf(msg, sizeof(msg), "\"%s\" [=%d] [w]", path, end - beg);
588 ex_show(msg);
589 if (!ex_path()[0]) {
590 free(bufs[0].path);
591 bufs[0].path = uc_dup(path);
592 reg_put('%', path, 0);
594 if (!strcmp(ex_path(), path))
595 lbuf_saved(xb, 0);
596 if (!strcmp(ex_path(), path))
597 bufs[0].mtime = mtime(path);
598 if (cmd[0] == 'x' || (cmd[0] == 'w' && cmd[1] == 'q'))
599 ec_quit("", cmd, "", NULL);
600 return 0;
603 static int ec_insert(char *loc, char *cmd, char *arg, char *txt)
605 int beg, end;
606 int n;
607 if (ex_region(loc, &beg, &end) && (beg != 0 || end != 0))
608 return 1;
609 if (cmd[0] == 'a')
610 if (beg + 1 <= lbuf_len(xb))
611 beg++;
612 if (cmd[0] != 'c')
613 end = beg;
614 n = lbuf_len(xb);
615 lbuf_edit(xb, txt, beg, end);
616 xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
617 return 0;
620 static int ec_print(char *loc, char *cmd, char *arg, char *txt)
622 int beg, end;
623 int i;
624 if (!cmd[0] && !loc[0])
625 if (xrow >= lbuf_len(xb))
626 return 1;
627 if (ex_region(loc, &beg, &end))
628 return 1;
629 for (i = beg; i < end; i++)
630 ex_print(lbuf_get(xb, i));
631 xrow = MAX(beg, end - 1);
632 xoff = 0;
633 return 0;
636 static int ec_null(char *loc, char *cmd, char *arg, char *txt)
638 int beg, end;
639 if (!xvis) {
640 xrow = xrow + 1 < lbuf_len(xb) ? xrow + 1 : xrow;
641 return ec_print(loc, cmd, arg, txt);
643 if (ex_region(loc, &beg, &end))
644 return 1;
645 xrow = MAX(beg, end - 1);
646 xoff = 0;
647 return 0;
650 static void ex_yank(int reg, int beg, int end)
652 char *buf = lbuf_cp(xb, beg, end);
653 reg_put(reg, buf, 1);
654 free(buf);
657 static int ec_ye(char *loc, char *cmd, char *arg, char *txt)
659 reg_put(REG(arg), txt, 1);
660 return 0;
663 static int ec_delete(char *loc, char *cmd, char *arg, char *txt)
665 int beg, end;
666 if (ex_region(loc, &beg, &end) || !lbuf_len(xb))
667 return 1;
668 ex_yank(REG(arg), beg, end);
669 lbuf_edit(xb, NULL, beg, end);
670 xrow = beg;
671 return 0;
674 static int ec_yank(char *loc, char *cmd, char *arg, char *txt)
676 int beg, end;
677 if (ex_region(loc, &beg, &end) || !lbuf_len(xb))
678 return 1;
679 ex_yank(REG(arg), beg, end);
680 return 0;
683 static int ec_put(char *loc, char *cmd, char *arg, char *txt)
685 int beg, end;
686 int lnmode;
687 char *buf;
688 int n = lbuf_len(xb);
689 buf = reg_get(REG(arg), &lnmode);
690 if (!buf || ex_region(loc, &beg, &end))
691 return 1;
692 lbuf_edit(xb, buf, end, end);
693 xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
694 return 0;
697 static int ec_lnum(char *loc, char *cmd, char *arg, char *txt)
699 char msg[128];
700 int beg, end;
701 if (ex_region(loc, &beg, &end))
702 return 1;
703 sprintf(msg, "%d\n", end);
704 ex_print(msg);
705 return 0;
708 static int ec_undo(char *loc, char *cmd, char *arg, char *txt)
710 return lbuf_undo(xb);
713 static int ec_redo(char *loc, char *cmd, char *arg, char *txt)
715 return lbuf_redo(xb);
718 static int ec_mark(char *loc, char *cmd, char *arg, char *txt)
720 int beg, end;
721 if (ex_region(loc, &beg, &end))
722 return 1;
723 lbuf_mark(xb, (unsigned char) arg[0], end - 1, 0);
724 return 0;
727 static void replace(struct sbuf *dst, char *rep, char *ln, int *offs)
729 while (rep[0]) {
730 if (rep[0] == '\\' && rep[1]) {
731 if (rep[1] >= '0' && rep[1] <= '9') {
732 int grp = (rep[1] - '0') * 2;
733 int len = offs[grp + 1] - offs[grp];
734 sbuf_mem(dst, ln + offs[grp], len);
735 } else {
736 sbuf_chr(dst, (unsigned char) rep[1]);
738 rep++;
739 } else {
740 sbuf_chr(dst, (unsigned char) rep[0]);
742 rep++;
746 static int ec_substitute(char *loc, char *cmd, char *arg, char *txt)
748 struct rstr *re;
749 int offs[32];
750 int beg, end;
751 char *pat = NULL, *rep = NULL;
752 char *s = arg;
753 int i;
754 if (ex_region(loc, &beg, &end))
755 return 1;
756 pat = re_read(&s);
757 if (pat && pat[0])
758 ex_kwdset(pat, +1);
759 if (pat && *s) {
760 s--;
761 rep = re_read(&s);
763 if (pat || rep)
764 snprintf(xrep, sizeof(xrep), "%s", rep ? rep : "");
765 free(pat);
766 free(rep);
767 if (ex_kwd(&pat, NULL))
768 return 1;
769 re = rstr_make(pat, xic ? RE_ICASE : 0);
770 if (!re)
771 return 1;
772 for (i = beg; i < end; i++) {
773 char *ln = lbuf_get(xb, i);
774 struct sbuf *r = NULL;
775 while (rstr_find(re, ln, LEN(offs) / 2, offs, 0) >= 0) {
776 if (!r)
777 r = sbuf_make();
778 sbuf_mem(r, ln, offs[0]);
779 replace(r, xrep, ln, offs);
780 ln += offs[1];
781 if (offs[1] <= 0) /* zero-length match */
782 sbuf_chr(r, (unsigned char) *ln++);
783 if (!*ln || *ln == '\n' || !strchr(s, 'g'))
784 break;
786 if (r) {
787 sbuf_str(r, ln);
788 lbuf_edit(xb, sbuf_buf(r), i, i + 1);
789 sbuf_free(r);
792 rstr_free(re);
793 return 0;
796 static int ec_exec(char *loc, char *cmd, char *arg, char *txt)
798 int beg, end;
799 char *text;
800 char *rep;
801 char *ecmd;
802 if (!xwa)
803 ex_modifiedbuffer(NULL);
804 if (!(ecmd = ex_pathexpand(arg, 1)))
805 return 1;
806 if (!loc[0]) {
807 ex_print(NULL);
808 return cmd_exec(ecmd);
810 if (ex_region(loc, &beg, &end))
811 return 1;
812 text = lbuf_cp(xb, beg, end);
813 rep = cmd_pipe(ecmd, text, 1);
814 if (rep)
815 lbuf_edit(xb, rep, beg, end);
816 free(text);
817 free(rep);
818 return 0;
821 static int ec_make(char *loc, char *cmd, char *arg, char *txt)
823 char make[EXLEN];
824 char *target;
825 if (!xwa)
826 ex_modifiedbuffer(NULL);
827 if (!(target = ex_pathexpand(arg, 0)))
828 return 1;
829 sprintf(make, "make %s", target);
830 ex_print(NULL);
831 if (cmd_exec(make))
832 return 1;
833 return 0;
836 static int ec_ft(char *loc, char *cmd, char *arg, char *txt)
838 if (arg[0])
839 snprintf(bufs[0].ft, sizeof(bufs[0].ft), "%s", arg);
840 else
841 ex_print(ex_filetype());
842 return 0;
845 static int ec_cmap(char *loc, char *cmd, char *arg, char *txt)
847 if (arg[0])
848 xkmap_alt = conf_kmapfind(arg);
849 else
850 ex_print(conf_kmap(xkmap)[0]);
851 if (arg[0] && !strchr(cmd, '!'))
852 xkmap = xkmap_alt;
853 return 0;
856 static int ex_exec(char *ln);
858 static int ec_glob(char *loc, char *cmd, char *arg, char *txt)
860 struct rstr *re;
861 int offs[32];
862 int beg, end, not;
863 char *pat;
864 char *s = arg;
865 int i;
866 if (!loc[0] && !xgdep)
867 strcpy(loc, "%");
868 if (ex_region(loc, &beg, &end))
869 return 1;
870 not = strchr(cmd, '!') || cmd[0] == 'v';
871 pat = re_read(&s);
872 if (pat && pat[0])
873 ex_kwdset(pat, +1);
874 free(pat);
875 if (ex_kwd(&pat, NULL))
876 return 1;
877 if (!(re = rstr_make(pat, xic ? RE_ICASE : 0)))
878 return 1;
879 xgdep++;
880 for (i = beg + 1; i < end; i++)
881 lbuf_globset(xb, i, xgdep);
882 i = beg;
883 while (i < lbuf_len(xb)) {
884 char *ln = lbuf_get(xb, i);
885 if ((rstr_find(re, ln, LEN(offs) / 2, offs, 0) < 0) == not) {
886 xrow = i;
887 if (ex_exec(s))
888 break;
889 i = MIN(i, xrow);
891 while (i < lbuf_len(xb) && !lbuf_globget(xb, i, xgdep))
892 i++;
894 for (i = 0; i < lbuf_len(xb); i++)
895 lbuf_globget(xb, i, xgdep);
896 xgdep--;
897 rstr_free(re);
898 return 0;
901 #define TAGCNT 32
902 #define TAGLEN 128
904 static int tag_row[TAGCNT];
905 static int tag_off[TAGCNT];
906 static int tag_pos[TAGCNT];
907 static char tag_path[TAGCNT][TAGLEN];
908 static char tag_name[TAGCNT][TAGLEN];
909 static int tag_cnt = 0;
911 static void ex_tagput(char *name)
913 if (tag_cnt < TAGCNT) {
914 tag_row[tag_cnt] = xrow;
915 tag_off[tag_cnt] = xoff;
916 snprintf(tag_path[tag_cnt], TAGLEN, "%s", ex_path());
917 snprintf(tag_name[tag_cnt], TAGLEN, "%s", name);
918 tag_cnt++;
922 /* go to definition (dir=+1 next, dir=-1 prev, dir=0 first) */
923 static int tag_goto(char *cw, int dir)
925 char path[120], cmd[120];
926 char *s, *ln;
927 int pos = dir == 0 || tag_cnt == 0 ? 0 : tag_pos[tag_cnt - 1];
928 if (tag_find(cw, &pos, dir, path, sizeof(path), cmd, sizeof(cmd))) {
929 ex_show("not found");
930 return 1;
932 if (dir == 0)
933 ex_tagput(cw);
934 tag_pos[tag_cnt - 1] = pos;
935 if (strcmp(path, ex_path()) != 0) {
936 if (access(path, R_OK) != 0) {
937 ex_show("cannot open");
938 return 1;
940 if (ec_edit("", "e", path, NULL) != 0)
941 return 1;
943 xrow = 0;
944 xoff = 0;
945 ex_command(cmd);
946 ln = lbuf_get(xb, xrow);
947 if (ln && (s = strstr(ln, cw)) != NULL)
948 xoff = s - ln;
949 return 0;
952 static int ec_tag(char *loc, char *cmd, char *arg, char *txt)
954 return tag_goto(arg, 0);
957 static int ec_tfree(char *loc, char *cmd, char *arg, char *txt)
959 tag_done();
960 return 0;
963 static int ec_pop(char *loc, char *cmd, char *arg, char *txt)
965 if (tag_cnt > 0) {
966 tag_cnt--;
967 if (ex_path() == NULL || strcmp(tag_path[tag_cnt], ex_path()) != 0)
968 ec_edit("", "e", tag_path[tag_cnt], txt);
969 xrow = tag_row[tag_cnt];
970 xoff = tag_off[tag_cnt];
971 return 0;
972 } else {
973 ex_show("not found");
975 return 1;
978 static int ec_tnext(char *loc, char *cmd, char *arg, char *txt)
980 if (tag_cnt > 0)
981 return tag_goto(tag_name[tag_cnt - 1], +1);
982 return 1;
985 static int ec_tprev(char *loc, char *cmd, char *arg, char *txt)
987 if (tag_cnt > 0)
988 return tag_goto(tag_name[tag_cnt - 1], -1);
989 return 1;
992 static int ec_at(char *loc, char *cmd, char *arg, char *txt)
994 int beg, end;
995 int lnmode;
996 char *buf = reg_get(REG(arg), &lnmode);
997 if (!buf || ex_region(loc, &beg, &end))
998 return 1;
999 xrow = beg;
1000 return ex_command(buf);
1003 static int ec_source(char *loc, char *cmd, char *arg, char *txt)
1005 char *path = arg[0] ? ex_pathexpand(arg, 1) : ex_path();
1006 char buf[1 << 10];
1007 struct sbuf *sb;
1008 int fd = path[0] ? open(path, O_RDONLY) : -1;
1009 long nr;
1010 if (fd < 0)
1011 return 1;
1012 sb = sbuf_make();
1013 while ((nr = read(fd, buf, sizeof(buf))) > 0)
1014 sbuf_mem(sb, buf, nr);
1015 ex_command(sbuf_buf(sb));
1016 sbuf_free(sb);
1017 return 0;
1020 static struct option {
1021 char *abbr;
1022 char *name;
1023 int *var;
1024 } options[] = {
1025 {"ai", "autoindent", &xai},
1026 {"aw", "autowrite", &xaw},
1027 {"wa", "writeany", &xwa},
1028 {"ic", "ignorecase", &xic},
1029 {"td", "textdirection", &xtd},
1030 {"shape", "shape", &xshape},
1031 {"order", "xorder", &xorder},
1032 {"hl", "highlight", &xhl},
1033 {"hll", "highlightline", &xhll},
1034 {"lim", "linelimit", &xlim},
1035 {"ru", "ruler", &xru},
1036 {"hist", "history", &xhist},
1039 static char *cutword(char *s, char *d)
1041 while (isspace(*s))
1042 s++;
1043 while (*s && !isspace(*s))
1044 *d++ = *s++;
1045 while (isspace(*s))
1046 s++;
1047 *d = '\0';
1048 return s;
1051 static int ec_set(char *loc, char *cmd, char *arg, char *txt)
1053 char tok[EXLEN];
1054 char opt[EXLEN];
1055 char *s = arg;
1056 int val = 0;
1057 int i;
1058 if (*s) {
1059 s = cutword(s, tok);
1060 if (tok[0] == 'n' && tok[1] == 'o') {
1061 strcpy(opt, tok + 2);
1062 val = 0;
1063 } else {
1064 char *r = strchr(tok, '=');
1065 if (r) {
1066 *r = '\0';
1067 strcpy(opt, tok);
1068 val = atoi(r + 1);
1069 } else {
1070 strcpy(opt, tok);
1071 val = 1;
1074 for (i = 0; i < LEN(options); i++) {
1075 struct option *o = &options[i];
1076 if (!strcmp(o->abbr, opt) || !strcmp(o->name, opt)) {
1077 *o->var = val;
1078 return 0;
1081 ex_show("unknown option");
1082 return 1;
1084 return 0;
1087 static struct excmd {
1088 char *abbr;
1089 char *name;
1090 int (*ec)(char *loc, char *cmd, char *arg, char *txt);
1091 } excmds[] = {
1092 {"b", "buffer", ec_buffer},
1093 {"p", "print", ec_print},
1094 {"a", "append", ec_insert},
1095 {"i", "insert", ec_insert},
1096 {"d", "delete", ec_delete},
1097 {"c", "change", ec_insert},
1098 {"e", "edit", ec_edit},
1099 {"e!", "edit!", ec_edit},
1100 {"ew", "ew", ec_edit},
1101 {"ew!", "ew!", ec_edit},
1102 {"g", "global", ec_glob},
1103 {"g!", "global!", ec_glob},
1104 {"=", "=", ec_lnum},
1105 {"k", "mark", ec_mark},
1106 {"po", "pop", ec_pop},
1107 {"pu", "put", ec_put},
1108 {"n", "next", ec_next},
1109 {"prev", "prev", ec_prev},
1110 {"q", "quit", ec_quit},
1111 {"q!", "quit!", ec_quit},
1112 {"r", "read", ec_read},
1113 {"ta", "tag", ec_tag},
1114 {"tn", "tnext", ec_tnext},
1115 {"tp", "tprev", ec_tprev},
1116 {"tf", "tfree", ec_tfree},
1117 {"v", "vglobal", ec_glob},
1118 {"w", "write", ec_write},
1119 {"w!", "write!", ec_write},
1120 {"wq", "wq", ec_write},
1121 {"wq!", "wq!", ec_write},
1122 {"u", "undo", ec_undo},
1123 {"redo", "redo", ec_redo},
1124 {"se", "set", ec_set},
1125 {"s", "substitute", ec_substitute},
1126 {"x", "xit", ec_write},
1127 {"x!", "xit!", ec_write},
1128 {"y", "yank", ec_yank},
1129 {"ye", "yankex", ec_ye},
1130 {"so", "source", ec_source},
1131 {"!", "!", ec_exec},
1132 {"make", "make", ec_make},
1133 {"ft", "filetype", ec_ft},
1134 {"cm", "cmap", ec_cmap},
1135 {"cm!", "cmap!", ec_cmap},
1136 {"@", "@", ec_at},
1137 {"", "", ec_null},
1140 static int ex_idx(char *cmd)
1142 int i;
1143 for (i = 0; i < LEN(excmds); i++)
1144 if (!strcmp(excmds[i].abbr, cmd) || !strcmp(excmds[i].name, cmd))
1145 return i;
1146 return -1;
1149 /* read ex command addresses */
1150 static char *ex_loc(char *src, char *loc)
1152 while (*src == ':' || *src == ' ' || *src == '\t')
1153 src++;
1154 while (*src && strchr(".$0123456789'/?+-,;%", (unsigned char) *src) != NULL) {
1155 if (*src == '\'')
1156 *loc++ = *src++;
1157 if (*src == '/' || *src == '?') {
1158 int d = *src;
1159 *loc++ = *src++;
1160 while (*src && *src != d) {
1161 if (*src == '\\' && src[1])
1162 *loc++ = *src++;
1163 *loc++ = *src++;
1166 if (*src)
1167 *loc++ = *src++;
1169 *loc = '\0';
1170 return src;
1173 /* read ex command name */
1174 static char *ex_cmd(char *src, char *cmd)
1176 char *cmd0 = cmd;
1177 while (*src == ' ' || *src == '\t')
1178 src++;
1179 while (isalpha((unsigned char) *src) && cmd < cmd0 + 16)
1180 if ((*cmd++ = *src++) == 'k' && cmd == cmd0 + 1)
1181 break;
1182 if (*src == '!' || *src == '=' || *src == '@')
1183 *cmd++ = *src++;
1184 *cmd = '\0';
1185 return src;
1188 /* read ex command argument for excmd command */
1189 static char *ex_arg(char *src, char *dst, char *excmd)
1191 int c0 = excmd[0];
1192 int c1 = c0 ? excmd[1] : 0;
1193 while (*src == ' ' || *src == '\t')
1194 src++;
1195 if (c0 == '!' || c0 == 'g' || c0 == 'v' ||
1196 ((c0 == 'r' || c0 == 'w') && src[0] == '!')) {
1197 while (*src && *src != '\n') {
1198 if (*src == '\\' && src[1])
1199 *dst++ = *src++;
1200 *dst++ = *src++;
1202 } else if ((c0 == 's' && c1 != 'e') || c0 == '&' || c0 == '~') {
1203 int delim = *src;
1204 int cnt = 2;
1205 *dst++ = *src++;
1206 while (*src && *src != '\n' && cnt > 0) {
1207 if (*src == delim)
1208 cnt--;
1209 if (*src == '\\' && src[1])
1210 *dst++ = *src++;
1211 *dst++ = *src++;
1214 while (*src && *src != '\n' && *src != '|' && *src != '"') {
1215 if (*src == '\\' && src[1])
1216 *dst++ = *src++;
1217 *dst++ = *src++;
1219 if (*src == '"') {
1220 while (*src && *src != '\n')
1221 src++;
1223 if (*src == '\n' || *src == '|')
1224 src++;
1225 *dst = '\0';
1226 return src;
1229 /* read ex text input for excmd command */
1230 static char *ex_txt(char *src, char **dst, char *excmd)
1232 int c0 = excmd[0];
1233 int c1 = c0 ? excmd[1] : 0;
1234 *dst = NULL;
1235 if (c0 == 'y' && c1 == 'e' && src[0]) {
1236 char *beg = src;
1237 char *res;
1238 while (src[0] && (src[0] != '\n' || src[1] != '.' || src[2] != '\n'))
1239 src++;
1240 res = malloc((src - beg) + 1 + 1);
1241 memcpy(res, beg, src - beg);
1242 res[src - beg] = '\n';
1243 res[src - beg + 1] = '\0';
1244 *dst = res;
1245 return src[0] ? src + 3 : src;
1247 if ((c0 == 'y' && c1 == 'e') || (c1 == 0 && (c0 == 'i' || c0 == 'a' || c0 == 'c'))) {
1248 struct sbuf *sb = sbuf_make();
1249 char *s;
1250 while ((s = ex_read(""))) {
1251 if (!strcmp(".", s)) {
1252 free(s);
1253 break;
1255 sbuf_str(sb, s);
1256 sbuf_chr(sb, '\n');
1257 free(s);
1259 *dst = sbuf_done(sb);
1260 return src;
1262 return src;
1265 /* execute a single ex command */
1266 static int ex_exec(char *ln)
1268 char loc[EXLEN], cmd[EXLEN], arg[EXLEN];
1269 int ret = 0;
1270 if (strlen(ln) >= EXLEN) {
1271 ex_show("command too long");
1272 return 1;
1274 while (*ln) {
1275 char *txt = NULL;
1276 int idx;
1277 ln = ex_loc(ln, loc);
1278 ln = ex_cmd(ln, cmd);
1279 idx = ex_idx(cmd);
1280 ln = ex_arg(ln, arg, idx >= 0 ? excmds[idx].abbr : "unknown");
1281 ln = ex_txt(ln, &txt, idx >= 0 ? excmds[idx].abbr : "unknown");
1282 if (idx >= 0)
1283 ret = excmds[idx].ec(loc, cmd, arg, txt);
1284 else
1285 ex_show("unknown command");
1286 free(txt);
1288 return ret;
1291 /* execute a single ex command */
1292 int ex_command(char *ln)
1294 int ret = ex_exec(ln);
1295 lbuf_modified(xb);
1296 return ret;
1299 /* ex main loop */
1300 void ex(void)
1302 while (!xquit) {
1303 char *ln = ex_read(":");
1304 if (ln) {
1305 ex_command(ln);
1306 reg_put(':', ln, 1);
1308 free(ln);
1312 int ex_init(char **files)
1314 next = files;
1315 if (ex_next("e", 0))
1316 return 1;
1317 if (getenv("EXINIT"))
1318 ex_command(getenv("EXINIT"));
1319 return 0;
1322 void ex_done(void)
1324 int i;
1325 for (i = 0; i < LEN(bufs); i++)
1326 bufs_free(i);