mot: skipping zero-length matches in lbuf_search()
[neatvi.git] / ex.c
blobdc885ecd7060a246d236c5e097c34f80fc37538d
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 int xrow, xoff, xtop; /* current row, column, and top row */
11 int xquit; /* exit if set */
12 int xvis; /* visual mode */
13 int xai = 1; /* autoindent option */
14 int xic = 1; /* ignorecase option */
15 int xaw; /* autowrite option */
16 int xhl = 1; /* syntax highlight option */
17 int xled = 1; /* use the line editor */
18 int xdir = +1; /* current direction context */
19 int xshape = 1; /* perform letter shaping */
20 int xorder = 1; /* change the order of characters */
21 char xfindkwd[EXLEN]; /* the last searched keyword */
22 int xfinddir = +1; /* the last search direction */
24 static struct buf {
25 char ft[32];
26 char *path;
27 struct lbuf *lb;
28 int row, off;
29 } bufs[8];
31 static int bufs_find(char *path)
33 int i;
34 for (i = 0; i < LEN(bufs); i++)
35 if (bufs[i].lb && !strcmp(bufs[i].path, path))
36 return i;
37 return -1;
40 static void bufs_free(int idx)
42 if (bufs[idx].lb) {
43 free(bufs[idx].path);
44 lbuf_free(bufs[idx].lb);
45 memset(&bufs[idx], 0, sizeof(bufs[idx]));
49 static int bufs_open(char *path)
51 int i;
52 for (i = 0; i < LEN(bufs) - 1; i++)
53 if (!bufs[i].lb)
54 break;
55 bufs_free(i);
56 bufs[i].path = uc_dup(path);
57 bufs[i].lb = lbuf_make();
58 bufs[i].row = 0;
59 bufs[i].off = 0;
60 strcpy(bufs[i].ft, syn_filetype(path));
61 return i;
64 static void bufs_swap(int i, int j)
66 struct buf tmp;
67 if (i == j)
68 return;
69 memcpy(&tmp, &bufs[i], sizeof(tmp));
70 memcpy(&bufs[i], &bufs[j], sizeof(tmp));
71 memcpy(&bufs[j], &tmp, sizeof(tmp));
74 static void bufs_switch(int idx)
76 if (idx > 1)
77 bufs_swap(0, 1);
78 bufs_swap(0, idx);
79 xrow = bufs[0].row;
80 xoff = bufs[0].off;
83 char *ex_path(void)
85 return bufs[0].path;
88 struct lbuf *ex_lbuf(void)
90 return bufs[0].lb;
93 char *ex_filetype(void)
95 return xhl ? bufs[0].ft : "";
98 /* read ex command location */
99 static char *ex_loc(char *s, char *loc)
101 while (*s == ':' || isspace((unsigned char) *s))
102 s++;
103 while (*s && !isalpha((unsigned char) *s) && *s != '=' && *s != '!') {
104 if (*s == '\'')
105 *loc++ = *s++;
106 if (*s == '/' || *s == '?') {
107 int d = *s;
108 *loc++ = *s++;
109 while (*s && *s != d) {
110 if (*s == '\\' && s[1])
111 *loc++ = *s++;
112 *loc++ = *s++;
115 if (*s)
116 *loc++ = *s++;
118 *loc = '\0';
119 return s;
122 /* read ex command name */
123 static char *ex_cmd(char *s, char *cmd)
125 char *cmd0 = cmd;
126 s = ex_loc(s, cmd);
127 while (isspace((unsigned char) *s))
128 s++;
129 while (isalpha((unsigned char) *s))
130 if ((*cmd++ = *s++) == 'k' && cmd == cmd0 + 1)
131 break;
132 if (*s == '!' || *s == '=')
133 *cmd++ = *s++;
134 *cmd = '\0';
135 return s;
138 /* read ex command argument */
139 static char *ex_arg(char *s, char *arg)
141 s = ex_cmd(s, arg);
142 while (isspace((unsigned char) *s))
143 s++;
144 while (*s && !isspace((unsigned char) *s))
145 *arg++ = *s++;
146 *arg = '\0';
147 return s;
150 static char *ex_argeol(char *ec)
152 char arg[EXLEN];
153 char *s = ex_cmd(ec, arg);
154 while (isspace((unsigned char) *s))
155 s++;
156 return s;
159 static char *ex_line(char *s, char *ln)
161 while (*s && *s != '|' && *s != '\n')
162 *ln++ = *s++;
163 *ln = '\0';
164 return *s ? s + 1 : s;
167 static int ex_search(char *pat)
169 struct sbuf *kw;
170 int dir = *pat == '/' ? 1 : -1;
171 char *b = pat;
172 char *e = b;
173 char *pats[1];
174 struct rset *re;
175 int row;
176 kw = sbuf_make();
177 while (*++e) {
178 if (*e == *pat)
179 break;
180 sbuf_chr(kw, (unsigned char) *e);
181 if (*e == '\\' && e[1])
182 e++;
184 if (sbuf_len(kw))
185 snprintf(xfindkwd, sizeof(xfindkwd), "%s", sbuf_buf(kw));
186 sbuf_free(kw);
187 if (!xfindkwd[0])
188 return xrow;
189 xfinddir = dir;
190 pats[0] = xfindkwd;
191 re = rset_make(1, pats, xic ? RE_ICASE : 0);
192 if (!re)
193 return 1;
194 row = xrow + dir;
195 while (row >= 0 && row < lbuf_len(xb)) {
196 if (rset_find(re, lbuf_get(xb, row), 0, NULL, 0) >= 0)
197 break;
198 row += dir;
200 rset_free(re);
201 return row < 0 || row >= lbuf_len(xb) ? xrow : row;
204 static int ex_lineno(char *num)
206 int n = xrow;
207 if (!num[0] || num[0] == '.')
208 n = xrow;
209 if (isdigit(num[0]))
210 n = atoi(num) - 1;
211 if (num[0] == '$')
212 n = lbuf_len(xb) - 1;
213 if (num[0] == '-')
214 n = xrow - (num[1] ? ex_lineno(num + 1) : 1);
215 if (num[0] == '+')
216 n = xrow + (num[1] ? ex_lineno(num + 1) : 1);
217 if (num[0] == '\'')
218 lbuf_jump(xb, num[1], &n, NULL);
219 if (num[0] == '/' && num[1])
220 n = ex_search(num);
221 if (num[0] == '?' && num[1])
222 n = ex_search(num);
223 return n;
226 /* parse ex command location */
227 static int ex_region(char *loc, int *beg, int *end)
229 int naddr = 0;
230 if (!strcmp("%", loc)) {
231 *beg = 0;
232 *end = MAX(0, lbuf_len(xb));
233 return 0;
235 if (!*loc) {
236 *beg = xrow;
237 *end = xrow == lbuf_len(xb) ? xrow : xrow + 1;
238 return 0;
240 while (*loc) {
241 int end0 = *end;
242 *end = ex_lineno(loc) + 1;
243 *beg = naddr++ ? end0 - 1 : *end - 1;
244 if (!naddr++)
245 *beg = *end - 1;
246 while (*loc && *loc != ';' && *loc != ',')
247 loc++;
248 if (!*loc)
249 break;
250 if (*loc == ';')
251 xrow = *end - 1;
252 loc++;
254 if (*beg < 0 || *beg >= lbuf_len(xb))
255 return 1;
256 if (*end < *beg || *end > lbuf_len(xb))
257 return 1;
258 return 0;
261 static int ec_write(char *ec);
263 static int ex_modifiedbuffer(char *msg)
265 if (!lbuf_modified(xb))
266 return 0;
267 if (xaw && ex_path()[0])
268 return ec_write("w");
269 if (msg)
270 ex_show(msg);
271 return 1;
274 static int ec_quit(char *ec)
276 char cmd[EXLEN];
277 ex_cmd(ec, cmd);
278 if (!strchr(cmd, '!'))
279 if (ex_modifiedbuffer("buffer modified\n"))
280 return 1;
281 xquit = 1;
282 return 0;
285 static int ex_expand(char *d, char *s)
287 while (*s) {
288 int c = (unsigned char) *s++;
289 if (c == '%') {
290 if (!bufs[0].path || !bufs[0].path[0]) {
291 ex_show("\"%\" is unset\n");
292 return 1;
294 strcpy(d, bufs[0].path);
295 d = strchr(d, '\0');
296 continue;
298 if (c == '#') {
299 if (!bufs[1].path || !bufs[1].path[0]) {
300 ex_show("\"#\" is unset\n");
301 return 1;
303 strcpy(d, bufs[1].path);
304 d = strchr(d, '\0');
305 continue;
307 if (c == '\\' && (*s == '%' || *s == '#'))
308 c = *s++;
309 *d++ = c;
311 *d = '\0';
312 return 0;
315 static int ec_edit(char *ec)
317 char msg[128];
318 char arg[EXLEN], cmd[EXLEN];
319 char path[EXLEN];
320 int fd;
321 ex_cmd(ec, cmd);
322 ex_arg(ec, arg);
323 if (!strchr(cmd, '!'))
324 if (xb && ex_modifiedbuffer("buffer modified\n"))
325 return 1;
326 if (ex_expand(path, arg))
327 return 1;
328 bufs[0].row = xrow;
329 bufs[0].off = xoff;
330 if (arg[0] && bufs_find(path) >= 0) {
331 bufs_switch(bufs_find(path));
332 return 0;
334 if (path[0] || !bufs[0].path)
335 bufs_switch(bufs_open(path));
336 fd = open(ex_path(), O_RDONLY);
337 if (fd >= 0) {
338 lbuf_rm(xb, 0, lbuf_len(xb));
339 lbuf_rd(xb, fd, 0);
340 close(fd);
341 snprintf(msg, sizeof(msg), "\"%s\" %d lines [r]\n",
342 ex_path(), lbuf_len(xb));
343 ex_show(msg);
345 xrow = xvis ? 0 : MAX(0, MIN(xrow, lbuf_len(xb) - 1));
346 lbuf_saved(xb, path[0] != '\0');
347 return 0;
350 static int ec_read(char *ec)
352 char arg[EXLEN], loc[EXLEN];
353 char msg[128];
354 int beg, end;
355 char *path;
356 char *obuf;
357 int n = lbuf_len(xb);
358 ex_arg(ec, arg);
359 ex_loc(ec, loc);
360 path = arg[0] ? arg : ex_path();
361 if (ex_region(loc, &beg, &end))
362 return 1;
363 if (arg[0] == '!') {
364 if (ex_expand(arg, ex_argeol(ec)))
365 return 1;
366 obuf = cmd_pipe(arg + 1, NULL, 0, 1);
367 if (obuf)
368 lbuf_put(xb, MIN(xrow + 1, lbuf_len(xb)), obuf);
369 free(obuf);
370 } else {
371 int fd = open(path, O_RDONLY);
372 if (fd < 0) {
373 ex_show("read failed\n");
374 return 1;
376 lbuf_rd(xb, fd, lbuf_len(xb) ? end : 0);
377 close(fd);
379 xrow = end + lbuf_len(xb) - n - 1;
380 snprintf(msg, sizeof(msg), "\"%s\" %d lines [r]\n",
381 path, lbuf_len(xb) - n);
382 ex_show(msg);
383 return 0;
386 static int ec_write(char *ec)
388 char cmd[EXLEN], arg[EXLEN], loc[EXLEN];
389 char msg[128];
390 char *path;
391 char *ibuf;
392 int beg, end;
393 ex_cmd(ec, cmd);
394 ex_arg(ec, arg);
395 ex_loc(ec, loc);
396 path = arg[0] ? arg : ex_path();
397 if (cmd[0] == 'x' && !lbuf_modified(xb))
398 return ec_quit(cmd);
399 if (ex_region(loc, &beg, &end))
400 return 1;
401 if (!loc[0]) {
402 beg = 0;
403 end = lbuf_len(xb);
405 if (arg[0] == '!') {
406 if (ex_expand(arg, ex_argeol(ec)))
407 return 1;
408 ibuf = lbuf_cp(xb, beg, end);
409 ex_print(NULL);
410 cmd_pipe(arg + 1, ibuf, 1, 0);
411 free(ibuf);
412 } else {
413 int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
414 if (fd < 0) {
415 ex_show("write failed\n");
416 return 1;
418 lbuf_wr(xb, fd, beg, end);
419 close(fd);
421 snprintf(msg, sizeof(msg), "\"%s\" %d lines [w]\n",
422 path, end - beg);
423 ex_show(msg);
424 if (!ex_path()[0]) {
425 free(bufs[0].path);
426 bufs[0].path = uc_dup(path);
428 if (!strcmp(ex_path(), path))
429 lbuf_saved(xb, 0);
430 if (cmd[0] == 'x' || (cmd[0] == 'w' && cmd[1] == 'q'))
431 ec_quit(cmd);
432 return 0;
435 static int ec_insert(char *ec)
437 char arg[EXLEN], cmd[EXLEN], loc[EXLEN];
438 struct sbuf *sb;
439 char *s;
440 int beg, end;
441 int n;
442 ex_arg(ec, arg);
443 ex_cmd(ec, cmd);
444 ex_loc(ec, loc);
445 if (ex_region(loc, &beg, &end) && (beg != 0 || end != 0))
446 return 1;
447 if (cmd[0] == 'c') {
448 if (lbuf_len(xb))
449 lbuf_rm(xb, beg, end);
450 end = beg + 1;
452 sb = sbuf_make();
453 while ((s = ex_read(""))) {
454 if (!strcmp(".", s)) {
455 free(s);
456 break;
458 sbuf_str(sb, s);
459 sbuf_chr(sb, '\n');
460 free(s);
462 if (cmd[0] == 'a')
463 if (end > lbuf_len(xb))
464 end = lbuf_len(xb);
465 n = lbuf_len(xb);
466 lbuf_put(xb, end, sbuf_buf(sb));
467 xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
468 sbuf_free(sb);
469 return 0;
472 static int ec_print(char *ec)
474 char cmd[EXLEN], loc[EXLEN];
475 int beg, end;
476 int i;
477 ex_cmd(ec, cmd);
478 ex_loc(ec, loc);
479 if (!cmd[0] && !loc[0]) {
480 if (xrow >= lbuf_len(xb) - 1)
481 return 1;
482 xrow = xrow + 1;
484 if (ex_region(loc, &beg, &end))
485 return 1;
486 for (i = beg; i < end; i++)
487 ex_print(lbuf_get(xb, i));
488 xrow = end;
489 xoff = 0;
490 return 0;
493 static int ec_null(char *ec)
495 char loc[EXLEN];
496 int beg, end;
497 if (!xvis)
498 return ec_print(ec);
499 ex_loc(ec, loc);
500 if (ex_region(loc, &beg, &end))
501 return 1;
502 xrow = MAX(beg, end - 1);
503 xoff = 0;
504 return 0;
507 static void ex_yank(int reg, int beg, int end)
509 char *buf = lbuf_cp(xb, beg, end);
510 reg_put(reg, buf, 1);
511 free(buf);
514 static int ec_delete(char *ec)
516 char loc[EXLEN];
517 char arg[EXLEN];
518 int beg, end;
519 ex_loc(ec, loc);
520 ex_arg(ec, arg);
521 if (ex_region(loc, &beg, &end) || !lbuf_len(xb))
522 return 1;
523 ex_yank(arg[0], beg, end);
524 lbuf_rm(xb, beg, end);
525 xrow = beg;
526 return 0;
529 static int ec_yank(char *ec)
531 char loc[EXLEN];
532 char arg[EXLEN];
533 int beg, end;
534 ex_loc(ec, loc);
535 ex_arg(ec, arg);
536 if (ex_region(loc, &beg, &end) || !lbuf_len(xb))
537 return 1;
538 ex_yank(arg[0], beg, end);
539 return 0;
542 static int ec_put(char *ec)
544 char loc[EXLEN];
545 char arg[EXLEN];
546 int beg, end;
547 int lnmode;
548 char *buf;
549 int n = lbuf_len(xb);
550 ex_loc(ec, loc);
551 ex_arg(ec, arg);
552 buf = reg_get(arg[0], &lnmode);
553 if (!buf || ex_region(loc, &beg, &end))
554 return 1;
555 lbuf_put(xb, end, buf);
556 xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
557 return 0;
560 static int ec_lnum(char *ec)
562 char loc[EXLEN];
563 char msg[128];
564 int beg, end;
565 ex_loc(ec, loc);
566 if (ex_region(loc, &beg, &end))
567 return 1;
568 sprintf(msg, "%d\n", end);
569 ex_print(msg);
570 return 0;
573 static int ec_undo(char *ec)
575 return lbuf_undo(xb);
578 static int ec_redo(char *ec)
580 return lbuf_redo(xb);
583 static int ec_mark(char *ec)
585 char loc[EXLEN], arg[EXLEN];
586 int beg, end;
587 ex_arg(ec, arg);
588 ex_loc(ec, loc);
589 if (ex_region(loc, &beg, &end))
590 return 1;
591 lbuf_mark(xb, arg[0], end - 1, 0);
592 return 0;
595 static char *readuntil(char **src, int delim)
597 struct sbuf *sbuf = sbuf_make();
598 char *s = *src;
599 /* reading the pattern */
600 while (*s && *s != delim) {
601 if (s[0] == '\\' && s[1])
602 sbuf_chr(sbuf, (unsigned char) *s++);
603 sbuf_chr(sbuf, (unsigned char) *s++);
605 if (*s) /* skipping the delimiter */
606 s++;
607 *src = s;
608 return sbuf_done(sbuf);
611 static int ec_substitute(char *ec)
613 char loc[EXLEN];
614 struct rset *re;
615 int offs[32];
616 int beg, end;
617 char *pats[1];
618 char *pat, *rep;
619 char *s;
620 int delim;
621 int i;
622 ex_loc(ec, loc);
623 if (ex_region(loc, &beg, &end))
624 return 1;
625 s = ex_argeol(ec);
626 delim = (unsigned char) *s++;
627 pat = readuntil(&s, delim);
628 rep = readuntil(&s, delim);
629 if (pat[0])
630 snprintf(xfindkwd, sizeof(xfindkwd), "%s", pat);
631 free(pat);
632 pats[0] = xfindkwd;
633 re = rset_make(1, pats, xic ? RE_ICASE : 0);
634 if (!re) {
635 free(rep);
636 return 1;
638 for (i = beg; i < end; i++) {
639 char *ln = lbuf_get(xb, i);
640 struct sbuf *r = sbuf_make();
641 while (rset_find(re, ln, LEN(offs) / 2, offs, 0) >= 0) {
642 sbuf_mem(r, ln, offs[0]);
643 sbuf_str(r, rep);
644 ln += offs[1];
645 if (!strchr(s, 'g'))
646 break;
648 sbuf_str(r, ln);
649 lbuf_rm(xb, i, i + 1);
650 lbuf_put(xb, i, sbuf_buf(r));
651 sbuf_free(r);
653 rset_free(re);
654 free(rep);
655 return 0;
658 static int ec_exec(char *ec)
660 char arg[EXLEN];
661 ex_modifiedbuffer(NULL);
662 if (ex_expand(arg, ex_argeol(ec)))
663 return 1;
664 ex_print(NULL);
665 if (cmd_exec(arg))
666 return 1;
667 return 0;
670 static int ec_make(char *ec)
672 char arg[EXLEN];
673 char make[EXLEN];
674 ex_modifiedbuffer(NULL);
675 if (ex_expand(arg, ex_argeol(ec)))
676 return 1;
677 sprintf(make, "make %s", arg);
678 ex_print(NULL);
679 if (cmd_exec(make))
680 return 1;
681 return 0;
684 static struct option {
685 char *abbr;
686 char *name;
687 int *var;
688 } options[] = {
689 {"ai", "autoindent", &xai},
690 {"aw", "autowrite", &xaw},
691 {"ic", "ignorecase", &xic},
692 {"td", "textdirection", &xdir},
693 {"shape", "shape", &xshape},
694 {"order", "xorder", &xorder},
695 {"hl", "highlight", &xhl},
698 static char *cutword(char *s, char *d)
700 while (isspace(*s))
701 s++;
702 while (*s && !isspace(*s))
703 *d++ = *s++;
704 while (isspace(*s))
705 s++;
706 *d = '\0';
707 return s;
710 static int ec_set(char *ec)
712 char arg[EXLEN];
713 char tok[EXLEN];
714 char opt[EXLEN];
715 char *s = arg;
716 int val = 0;
717 int i;
718 ex_arg(ec, arg);
719 if (*s) {
720 s = cutword(s, tok);
721 if (tok[0] == 'n' && tok[1] == 'o') {
722 strcpy(opt, tok + 2);
723 val = 0;
724 } else {
725 char *r = strchr(tok, '=');
726 if (r) {
727 *r = '\0';
728 strcpy(opt, tok);
729 val = atoi(r + 1);
730 } else {
731 strcpy(opt, tok);
732 val = 1;
735 for (i = 0; i < LEN(options); i++) {
736 struct option *o = &options[i];
737 if (!strcmp(o->abbr, opt) || !strcmp(o->name, opt)) {
738 *o->var = val;
739 return 0;
742 ex_show("unknown option");
743 return 1;
745 return 0;
748 static struct excmd {
749 char *abbr;
750 char *name;
751 int (*ec)(char *s);
752 } excmds[] = {
753 {"p", "print", ec_print},
754 {"a", "append", ec_insert},
755 {"i", "insert", ec_insert},
756 {"d", "delete", ec_delete},
757 {"c", "change", ec_insert},
758 {"e", "edit", ec_edit},
759 {"e!", "edit!", ec_edit},
760 {"=", "=", ec_lnum},
761 {"k", "mark", ec_mark},
762 {"pu", "put", ec_put},
763 {"q", "quit", ec_quit},
764 {"q!", "quit!", ec_quit},
765 {"r", "read", ec_read},
766 {"w", "write", ec_write},
767 {"wq", "wq", ec_write},
768 {"u", "undo", ec_undo},
769 {"r", "redo", ec_redo},
770 {"se", "set", ec_set},
771 {"s", "substitute", ec_substitute},
772 {"x", "xit", ec_write},
773 {"x!", "xit!", ec_write},
774 {"ya", "yank", ec_yank},
775 {"!", "!", ec_exec},
776 {"make", "make", ec_make},
777 {"", "", ec_null},
780 /* execute a single ex command */
781 void ex_command(char *ln)
783 char ec[EXLEN];
784 char cmd[EXLEN];
785 int i;
786 while (*ln) {
787 ln = ex_line(ln, ec);
788 ex_cmd(ec, cmd);
789 for (i = 0; i < LEN(excmds); i++) {
790 if (!strcmp(excmds[i].abbr, cmd) ||
791 !strcmp(excmds[i].name, cmd)) {
792 excmds[i].ec(ec);
793 break;
796 if (!xvis && !cmd[0])
797 ec_print(ec);
799 lbuf_modified(xb);
802 /* ex main loop */
803 void ex(void)
805 while (!xquit) {
806 char *ln = ex_read(":");
807 if (ln)
808 ex_command(ln);
809 free(ln);
813 int ex_init(char **files)
815 char cmd[EXLEN];
816 snprintf(cmd, sizeof(cmd), "e %s", files[0] ? files[0] : "");
817 if (ec_edit(cmd))
818 return 1;
819 if (getenv("EXINIT"))
820 ex_command(getenv("EXINIT"));
821 return 0;
824 void ex_done(void)
826 int i;
827 for (i = 0; i < LEN(bufs); i++)
828 bufs_free(i);