syn: handle non-black terminal forground colour
[neatvi.git] / ex.c
blob0465ae774b7236ce365984c81ee32df5b0224433
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 xled = 1; /* use the line editor */
17 int xdir = +1; /* current direction context */
18 int xshape = 1; /* perform letter shaping */
19 int xorder = 1; /* change the order of characters */
20 char xfindkwd[EXLEN]; /* the last searched keyword */
21 int xfinddir = +1; /* the last search direction */
23 static struct buf {
24 char ft[32];
25 char *path;
26 struct lbuf *lb;
27 int row;
28 } bufs[8];
30 static int bufs_find(char *path)
32 int i;
33 for (i = 0; i < LEN(bufs); i++)
34 if (bufs[i].lb && !strcmp(bufs[i].path, path))
35 return i;
36 return -1;
39 static void bufs_free(int idx)
41 if (bufs[idx].lb) {
42 free(bufs[idx].path);
43 lbuf_free(bufs[idx].lb);
44 memset(&bufs[idx], 0, sizeof(bufs[idx]));
48 static int bufs_open(char *path)
50 int i;
51 for (i = 0; i < LEN(bufs) - 1; i++)
52 if (!bufs[i].lb)
53 break;
54 bufs_free(i);
55 bufs[i].path = uc_dup(path);
56 bufs[i].lb = lbuf_make();
57 bufs[i].row = 0;
58 strcpy(bufs[i].ft, syn_filetype(path));
59 return i;
62 static void bufs_swap(int i, int j)
64 struct buf tmp;
65 if (i == j)
66 return;
67 memcpy(&tmp, &bufs[i], sizeof(tmp));
68 memcpy(&bufs[i], &bufs[j], sizeof(tmp));
69 memcpy(&bufs[j], &tmp, sizeof(tmp));
72 static void bufs_switch(int idx)
74 if (idx > 1)
75 bufs_swap(0, 1);
76 bufs_swap(0, idx);
77 xrow = bufs[0].row;
80 char *ex_path(void)
82 return bufs[0].path;
85 struct lbuf *ex_lbuf(void)
87 return bufs[0].lb;
90 char *ex_filetype(void)
92 return bufs[0].ft;
95 /* read ex command location */
96 static char *ex_loc(char *s, char *loc)
98 while (*s == ':' || isspace((unsigned char) *s))
99 s++;
100 while (*s && !isalpha((unsigned char) *s) && *s != '=' && *s != '!') {
101 if (*s == '\'')
102 *loc++ = *s++;
103 if (*s == '/' || *s == '?') {
104 int d = *s;
105 *loc++ = *s++;
106 while (*s && *s != d) {
107 if (*s == '\\' && s[1])
108 *loc++ = *s++;
109 *loc++ = *s++;
112 if (*s)
113 *loc++ = *s++;
115 *loc = '\0';
116 return s;
119 /* read ex command name */
120 static char *ex_cmd(char *s, char *cmd)
122 char *cmd0 = cmd;
123 s = ex_loc(s, cmd);
124 while (isspace((unsigned char) *s))
125 s++;
126 while (isalpha((unsigned char) *s))
127 if ((*cmd++ = *s++) == 'k' && cmd == cmd0 + 1)
128 break;
129 if (*s == '!' || *s == '=')
130 *cmd++ = *s++;
131 *cmd = '\0';
132 return s;
135 /* read ex command argument */
136 static char *ex_arg(char *s, char *arg)
138 s = ex_cmd(s, arg);
139 while (isspace((unsigned char) *s))
140 s++;
141 while (*s && !isspace((unsigned char) *s))
142 *arg++ = *s++;
143 *arg = '\0';
144 return s;
147 static char *ex_argeol(char *ec)
149 char arg[EXLEN];
150 char *s = ex_cmd(ec, arg);
151 while (isspace((unsigned char) *s))
152 s++;
153 return s;
156 static int ex_search(char *pat)
158 struct sbuf *kw;
159 int dir = *pat == '/' ? 1 : -1;
160 char *b = pat;
161 char *e = b;
162 char *pats[1];
163 struct rset *re;
164 int row;
165 kw = sbuf_make();
166 while (*++e) {
167 if (*e == *pat)
168 break;
169 sbuf_chr(kw, (unsigned char) *e);
170 if (*e == '\\' && e[1])
171 e++;
173 if (sbuf_len(kw))
174 snprintf(xfindkwd, sizeof(xfindkwd), "%s", sbuf_buf(kw));
175 sbuf_free(kw);
176 if (!xfindkwd[0])
177 return xrow;
178 xfinddir = dir;
179 pats[0] = xfindkwd;
180 re = rset_make(1, pats, xic ? RE_ICASE : 0);
181 if (!re)
182 return 1;
183 row = xrow + dir;
184 while (row >= 0 && row < lbuf_len(xb)) {
185 if (rset_find(re, lbuf_get(xb, row), 0, NULL, 0) >= 0)
186 break;
187 row += dir;
189 rset_free(re);
190 return row < 0 || row >= lbuf_len(xb) ? xrow : row;
193 static int ex_lineno(char *num)
195 int n = xrow;
196 if (!num[0] || num[0] == '.')
197 n = xrow;
198 if (isdigit(num[0]))
199 n = atoi(num) - 1;
200 if (num[0] == '$')
201 n = lbuf_len(xb) - 1;
202 if (num[0] == '-')
203 n = xrow - (num[1] ? ex_lineno(num + 1) : 1);
204 if (num[0] == '+')
205 n = xrow + (num[1] ? ex_lineno(num + 1) : 1);
206 if (num[0] == '\'')
207 lbuf_jump(xb, num[1], &n, NULL);
208 if (num[0] == '/' && num[1])
209 n = ex_search(num);
210 if (num[0] == '?' && num[1])
211 n = ex_search(num);
212 return n;
215 /* parse ex command location */
216 static int ex_region(char *loc, int *beg, int *end)
218 int naddr = 0;
219 if (!strcmp("%", loc)) {
220 *beg = 0;
221 *end = MAX(0, lbuf_len(xb));
222 return 0;
224 if (!*loc) {
225 *beg = xrow;
226 *end = xrow == lbuf_len(xb) ? xrow : xrow + 1;
227 return 0;
229 while (*loc) {
230 int end0 = *end;
231 *end = ex_lineno(loc) + 1;
232 *beg = naddr++ ? end0 - 1 : *end - 1;
233 if (!naddr++)
234 *beg = *end - 1;
235 while (*loc && *loc != ';' && *loc != ',')
236 loc++;
237 if (!*loc)
238 break;
239 if (*loc == ';')
240 xrow = *end - 1;
241 loc++;
243 if (*beg < 0 || *beg >= lbuf_len(xb))
244 return 1;
245 if (*end < *beg || *end > lbuf_len(xb))
246 return 1;
247 return 0;
250 static int ec_write(char *ec);
252 static int ex_modifiedbuffer(char *msg)
254 if (!lbuf_modified(xb))
255 return 0;
256 if (xaw && ex_path()[0])
257 return ec_write("w");
258 if (msg)
259 ex_show(msg);
260 return 1;
263 static int ec_quit(char *ec)
265 char cmd[EXLEN];
266 ex_cmd(ec, cmd);
267 if (!strchr(cmd, '!'))
268 if (ex_modifiedbuffer("buffer modified\n"))
269 return 1;
270 xquit = 1;
271 return 0;
274 static int ex_expand(char *d, char *s)
276 while (*s) {
277 int c = (unsigned char) *s++;
278 if (c == '%') {
279 if (!bufs[0].path || !bufs[0].path[0]) {
280 ex_show("\"%\" is unset\n");
281 return 1;
283 strcpy(d, bufs[0].path);
284 d = strchr(d, '\0');
285 continue;
287 if (c == '#') {
288 if (!bufs[1].path || !bufs[1].path[0]) {
289 ex_show("\"#\" is unset\n");
290 return 1;
292 strcpy(d, bufs[1].path);
293 d = strchr(d, '\0');
294 continue;
296 if (c == '\\' && (*s == '%' || *s == '#'))
297 c = *s++;
298 *d++ = c;
300 *d = '\0';
301 return 0;
304 static int ec_edit(char *ec)
306 char msg[128];
307 char arg[EXLEN], cmd[EXLEN];
308 char path[EXLEN];
309 int fd;
310 ex_cmd(ec, cmd);
311 ex_arg(ec, arg);
312 if (!strchr(cmd, '!'))
313 if (xb && ex_modifiedbuffer("buffer modified\n"))
314 return 1;
315 if (ex_expand(path, arg))
316 return 1;
317 bufs[0].row = xrow;
318 if (arg[0] && bufs_find(path) >= 0) {
319 bufs_switch(bufs_find(path));
320 return 0;
322 if (path[0] || !bufs[0].path)
323 bufs_switch(bufs_open(path));
324 fd = open(ex_path(), O_RDONLY);
325 if (fd >= 0) {
326 lbuf_rm(xb, 0, lbuf_len(xb));
327 lbuf_rd(xb, fd, 0);
328 close(fd);
329 snprintf(msg, sizeof(msg), "\"%s\" %d lines [r]\n",
330 ex_path(), lbuf_len(xb));
331 ex_show(msg);
333 xrow = MAX(0, MIN(xrow, lbuf_len(xb) - 1));
334 lbuf_saved(xb, path[0] != '\0');
335 return 0;
338 static int ec_read(char *ec)
340 char arg[EXLEN], loc[EXLEN];
341 char msg[128];
342 int beg, end;
343 char *path;
344 char *obuf;
345 int n = lbuf_len(xb);
346 ex_arg(ec, arg);
347 ex_loc(ec, loc);
348 path = arg[0] ? arg : ex_path();
349 if (ex_region(loc, &beg, &end))
350 return 1;
351 if (arg[0] == '!') {
352 if (ex_expand(arg, ex_argeol(ec)))
353 return 1;
354 obuf = cmd_pipe(arg + 1, NULL, 0, 1);
355 if (obuf)
356 lbuf_put(xb, MIN(xrow + 1, lbuf_len(xb)), obuf);
357 free(obuf);
358 } else {
359 int fd = open(path, O_RDONLY);
360 if (fd < 0) {
361 ex_show("read failed\n");
362 return 1;
364 lbuf_rd(xb, fd, lbuf_len(xb) ? end : 0);
365 close(fd);
367 xrow = end + lbuf_len(xb) - n - 1;
368 snprintf(msg, sizeof(msg), "\"%s\" %d lines [r]\n",
369 path, lbuf_len(xb) - n);
370 ex_show(msg);
371 return 0;
374 static int ec_write(char *ec)
376 char cmd[EXLEN], arg[EXLEN], loc[EXLEN];
377 char msg[128];
378 char *path;
379 char *ibuf;
380 int beg, end;
381 ex_cmd(ec, cmd);
382 ex_arg(ec, arg);
383 ex_loc(ec, loc);
384 path = arg[0] ? arg : ex_path();
385 if (cmd[0] == 'x' && !lbuf_modified(xb))
386 return ec_quit(cmd);
387 if (ex_region(loc, &beg, &end))
388 return 1;
389 if (!loc[0]) {
390 beg = 0;
391 end = lbuf_len(xb);
393 if (arg[0] == '!') {
394 if (ex_expand(arg, ex_argeol(ec)))
395 return 1;
396 ibuf = lbuf_cp(xb, beg, end);
397 ex_print(NULL);
398 cmd_pipe(arg + 1, ibuf, 1, 0);
399 free(ibuf);
400 } else {
401 int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
402 if (fd < 0) {
403 ex_show("write failed\n");
404 return 1;
406 lbuf_wr(xb, fd, beg, end);
407 close(fd);
409 snprintf(msg, sizeof(msg), "\"%s\" %d lines [w]\n",
410 path, end - beg);
411 ex_show(msg);
412 if (!ex_path()[0]) {
413 free(bufs[0].path);
414 bufs[0].path = uc_dup(path);
416 if (!strcmp(ex_path(), path))
417 lbuf_saved(xb, 0);
418 if (cmd[0] == 'x' || (cmd[0] == 'w' && cmd[1] == 'q'))
419 ec_quit(cmd);
420 return 0;
423 static int ec_insert(char *ec)
425 char arg[EXLEN], cmd[EXLEN], loc[EXLEN];
426 struct sbuf *sb;
427 char *s;
428 int beg, end;
429 int n;
430 ex_arg(ec, arg);
431 ex_cmd(ec, cmd);
432 ex_loc(ec, loc);
433 if (ex_region(loc, &beg, &end) && (beg != 0 || end != 0))
434 return 1;
435 if (cmd[0] == 'c') {
436 if (lbuf_len(xb))
437 lbuf_rm(xb, beg, end);
438 end = beg + 1;
440 sb = sbuf_make();
441 while ((s = ex_read(""))) {
442 if (!strcmp(".", s)) {
443 free(s);
444 break;
446 sbuf_str(sb, s);
447 sbuf_chr(sb, '\n');
448 free(s);
450 if (cmd[0] == 'a')
451 if (end > lbuf_len(xb))
452 end = lbuf_len(xb);
453 n = lbuf_len(xb);
454 lbuf_put(xb, end, sbuf_buf(sb));
455 xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
456 sbuf_free(sb);
457 return 0;
460 static int ec_print(char *ec)
462 char cmd[EXLEN], loc[EXLEN];
463 int beg, end;
464 int i;
465 ex_cmd(ec, cmd);
466 ex_loc(ec, loc);
467 if (!cmd[0] && !loc[0]) {
468 if (xrow >= lbuf_len(xb) - 1)
469 return 1;
470 xrow = xrow + 1;
472 if (ex_region(loc, &beg, &end))
473 return 1;
474 for (i = beg; i < end; i++)
475 ex_print(lbuf_get(xb, i));
476 xrow = end;
477 xoff = 0;
478 return 0;
481 static int ec_null(char *ec)
483 char loc[EXLEN];
484 int beg, end;
485 if (!xvis)
486 return ec_print(ec);
487 ex_loc(ec, loc);
488 if (ex_region(loc, &beg, &end))
489 return 1;
490 xrow = MAX(beg, end - 1);
491 xoff = 0;
492 return 0;
495 static void ex_yank(int reg, int beg, int end)
497 char *buf = lbuf_cp(xb, beg, end);
498 reg_put(reg, buf, 1);
499 free(buf);
502 static int ec_delete(char *ec)
504 char loc[EXLEN];
505 char arg[EXLEN];
506 int beg, end;
507 ex_loc(ec, loc);
508 ex_arg(ec, arg);
509 if (ex_region(loc, &beg, &end) || !lbuf_len(xb))
510 return 1;
511 ex_yank(arg[0], beg, end);
512 lbuf_rm(xb, beg, end);
513 xrow = beg;
514 return 0;
517 static int ec_yank(char *ec)
519 char loc[EXLEN];
520 char arg[EXLEN];
521 int beg, end;
522 ex_loc(ec, loc);
523 ex_arg(ec, arg);
524 if (ex_region(loc, &beg, &end) || !lbuf_len(xb))
525 return 1;
526 ex_yank(arg[0], beg, end);
527 return 0;
530 static int ec_put(char *ec)
532 char loc[EXLEN];
533 char arg[EXLEN];
534 int beg, end;
535 int lnmode;
536 char *buf;
537 int n = lbuf_len(xb);
538 ex_loc(ec, loc);
539 ex_arg(ec, arg);
540 buf = reg_get(arg[0], &lnmode);
541 if (!buf || ex_region(loc, &beg, &end))
542 return 1;
543 lbuf_put(xb, end, buf);
544 xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
545 return 0;
548 static int ec_lnum(char *ec)
550 char loc[EXLEN];
551 char msg[128];
552 int beg, end;
553 ex_loc(ec, loc);
554 if (ex_region(loc, &beg, &end))
555 return 1;
556 sprintf(msg, "%d\n", end);
557 ex_print(msg);
558 return 0;
561 static int ec_undo(char *ec)
563 return lbuf_undo(xb);
566 static int ec_redo(char *ec)
568 return lbuf_redo(xb);
571 static int ec_mark(char *ec)
573 char loc[EXLEN], arg[EXLEN];
574 int beg, end;
575 ex_arg(ec, arg);
576 ex_loc(ec, loc);
577 if (ex_region(loc, &beg, &end))
578 return 1;
579 lbuf_mark(xb, arg[0], end - 1, 0);
580 return 0;
583 static char *readuntil(char **src, int delim)
585 struct sbuf *sbuf = sbuf_make();
586 char *s = *src;
587 /* reading the pattern */
588 while (*s && *s != delim) {
589 if (s[0] == '\\' && s[1])
590 sbuf_chr(sbuf, (unsigned char) *s++);
591 sbuf_chr(sbuf, (unsigned char) *s++);
593 if (*s) /* skipping the delimiter */
594 s++;
595 *src = s;
596 return sbuf_done(sbuf);
599 static int ec_substitute(char *ec)
601 char loc[EXLEN];
602 struct rset *re;
603 int offs[32];
604 int beg, end;
605 char *pats[1];
606 char *pat, *rep;
607 char *s;
608 int delim;
609 int i;
610 ex_loc(ec, loc);
611 if (ex_region(loc, &beg, &end))
612 return 1;
613 s = ex_argeol(ec);
614 delim = (unsigned char) *s++;
615 pat = readuntil(&s, delim);
616 rep = readuntil(&s, delim);
617 if (pat[0])
618 snprintf(xfindkwd, sizeof(xfindkwd), "%s", pat);
619 free(pat);
620 pats[0] = xfindkwd;
621 re = rset_make(1, pats, xic ? RE_ICASE : 0);
622 if (!re) {
623 free(rep);
624 return 1;
626 for (i = beg; i < end; i++) {
627 char *ln = lbuf_get(xb, i);
628 struct sbuf *r = sbuf_make();
629 while (rset_find(re, ln, LEN(offs) / 2, offs, 0) >= 0) {
630 sbuf_mem(r, ln, offs[0]);
631 sbuf_str(r, rep);
632 ln += offs[1];
633 if (!strchr(s, 'g'))
634 break;
636 sbuf_str(r, ln);
637 lbuf_rm(xb, i, i + 1);
638 lbuf_put(xb, i, sbuf_buf(r));
639 sbuf_free(r);
641 rset_free(re);
642 free(rep);
643 return 0;
646 static int ec_exec(char *ec)
648 char arg[EXLEN];
649 ex_modifiedbuffer(NULL);
650 if (ex_expand(arg, ex_argeol(ec)))
651 return 1;
652 ex_print(NULL);
653 if (cmd_exec(arg))
654 return 1;
655 return 0;
658 static int ec_make(char *ec)
660 char arg[EXLEN];
661 char make[EXLEN];
662 ex_modifiedbuffer(NULL);
663 if (ex_expand(arg, ex_argeol(ec)))
664 return 1;
665 sprintf(make, "make %s", arg);
666 ex_print(NULL);
667 if (cmd_exec(make))
668 return 1;
669 return 0;
672 static struct option {
673 char *abbr;
674 char *name;
675 int *var;
676 } options[] = {
677 {"ai", "autoindent", &xai},
678 {"aw", "autowrite", &xaw},
679 {"ic", "ignorecase", &xic},
680 {"td", "textdirection", &xdir},
681 {"shape", "shape", &xshape},
682 {"order", "xorder", &xorder},
685 static char *cutword(char *s, char *d)
687 while (isspace(*s))
688 s++;
689 while (*s && !isspace(*s))
690 *d++ = *s++;
691 while (isspace(*s))
692 s++;
693 *d = '\0';
694 return s;
697 static int ec_set(char *ec)
699 char arg[EXLEN];
700 char tok[EXLEN];
701 char opt[EXLEN];
702 char *s = arg;
703 int val = 0;
704 int i;
705 ex_arg(ec, arg);
706 if (*s) {
707 s = cutword(s, tok);
708 if (tok[0] == 'n' && tok[1] == 'o') {
709 strcpy(opt, tok + 2);
710 val = 0;
711 } else {
712 char *r = strchr(tok, '=');
713 if (r) {
714 *r = '\0';
715 strcpy(opt, tok);
716 val = atoi(r + 1);
717 } else {
718 strcpy(opt, tok);
719 val = 1;
722 for (i = 0; i < LEN(options); i++) {
723 struct option *o = &options[i];
724 if (!strcmp(o->abbr, opt) || !strcmp(o->name, opt)) {
725 *o->var = val;
726 return 0;
729 ex_show("unknown option");
730 return 1;
732 return 0;
735 static struct excmd {
736 char *abbr;
737 char *name;
738 int (*ec)(char *s);
739 } excmds[] = {
740 {"p", "print", ec_print},
741 {"a", "append", ec_insert},
742 {"i", "insert", ec_insert},
743 {"d", "delete", ec_delete},
744 {"c", "change", ec_insert},
745 {"e", "edit", ec_edit},
746 {"e!", "edit!", ec_edit},
747 {"=", "=", ec_lnum},
748 {"k", "mark", ec_mark},
749 {"pu", "put", ec_put},
750 {"q", "quit", ec_quit},
751 {"q!", "quit!", ec_quit},
752 {"r", "read", ec_read},
753 {"w", "write", ec_write},
754 {"wq", "wq", ec_write},
755 {"u", "undo", ec_undo},
756 {"r", "redo", ec_redo},
757 {"se", "set", ec_set},
758 {"s", "substitute", ec_substitute},
759 {"x", "xit", ec_write},
760 {"x!", "xit!", ec_write},
761 {"ya", "yank", ec_yank},
762 {"!", "!", ec_exec},
763 {"make", "make", ec_make},
764 {"", "", ec_null},
767 /* execute a single ex command */
768 void ex_command(char *ln)
770 char cmd[EXLEN];
771 int i;
772 ex_cmd(ln, cmd);
773 for (i = 0; i < LEN(excmds); i++) {
774 if (!strcmp(excmds[i].abbr, cmd) || !strcmp(excmds[i].name, cmd)) {
775 excmds[i].ec(ln);
776 break;
779 if (!xvis && !cmd[0])
780 ec_print(ln);
781 lbuf_modified(xb);
784 /* ex main loop */
785 void ex(void)
787 while (!xquit) {
788 char *ln = ex_read(":");
789 if (ln)
790 ex_command(ln);
791 free(ln);
795 void ex_init(char **files)
797 char cmd[EXLEN];
798 snprintf(cmd, sizeof(cmd), "e %s", files[0] ? files[0] : "");
799 ec_edit(cmd);
802 void ex_done(void)
804 int i;
805 for (i = 0; i < LEN(bufs); i++)
806 bufs_free(i);