syn: nested and overlapping highlighting regions
[neatvi.git] / ex.c
blob33ca3b948da1ff85d6c0b81a8ce3503f2e053cbc
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 EXLEN 512
12 int xrow, xoff, xtop; /* current row, column, and top row */
13 int xquit; /* exit if set */
14 int xvis; /* visual mode */
15 int xai = 1; /* autoindent option */
16 int xic = 1; /* ignorecase option */
17 int xaw; /* autowrite option */
18 int xled = 1; /* use the line editor */
19 int xdir = +1; /* current direction context */
20 int xshape = 1; /* perform letter shaping */
21 int xorder = 1; /* change the order of characters */
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 *loc++ = *s++;
114 *loc = '\0';
115 return s;
118 /* read ex command name */
119 static char *ex_cmd(char *s, char *cmd)
121 char *cmd0 = cmd;
122 s = ex_loc(s, cmd);
123 while (isspace((unsigned char) *s))
124 s++;
125 while (isalpha((unsigned char) *s))
126 if ((*cmd++ = *s++) == 'k' && cmd == cmd0 + 1)
127 break;
128 if (*s == '!' || *s == '=')
129 *cmd++ = *s++;
130 *cmd = '\0';
131 return s;
134 /* read ex command argument */
135 static char *ex_arg(char *s, char *arg)
137 s = ex_cmd(s, arg);
138 while (isspace((unsigned char) *s))
139 s++;
140 while (*s && !isspace((unsigned char) *s))
141 *arg++ = *s++;
142 *arg = '\0';
143 return s;
146 static int ex_search(char *pat)
148 struct sbuf *kw;
149 int dir = *pat == '/' ? 1 : -1;
150 char *b = pat;
151 char *e = b;
152 char *re_kw[1];
153 int i = xrow;
154 struct rset *re;
155 kw = sbuf_make();
156 while (*++e) {
157 if (*e == *pat)
158 break;
159 sbuf_chr(kw, (unsigned char) *e);
160 if (*e == '\\' && e[1])
161 e++;
163 re_kw[0] = sbuf_buf(kw);
164 re = rset_make(1, re_kw, xic ? RE_ICASE : 0);
165 sbuf_free(kw);
166 if (!re)
167 return i;
168 while (i >= 0 && i < lbuf_len(xb)) {
169 if (rset_find(re, lbuf_get(xb, i), 0, NULL, 0) >= 0)
170 break;
171 i += dir;
173 rset_free(re);
174 return i;
177 static int ex_lineno(char *num)
179 int n = xrow;
180 if (!num[0] || num[0] == '.')
181 n = xrow;
182 if (isdigit(num[0]))
183 n = atoi(num) - 1;
184 if (num[0] == '$')
185 n = lbuf_len(xb) - 1;
186 if (num[0] == '-')
187 n = xrow - (num[1] ? ex_lineno(num + 1) : 1);
188 if (num[0] == '+')
189 n = xrow + (num[1] ? ex_lineno(num + 1) : 1);
190 if (num[0] == '\'')
191 lbuf_jump(xb, num[1], &n, NULL);
192 if (num[0] == '/' && num[1])
193 n = ex_search(num);
194 if (num[0] == '?' && num[1])
195 n = ex_search(num);
196 return n;
199 /* parse ex command location */
200 static int ex_region(char *loc, int *beg, int *end)
202 int naddr = 0;
203 if (!strcmp("%", loc)) {
204 *beg = 0;
205 *end = MAX(0, lbuf_len(xb));
206 return 0;
208 if (!*loc) {
209 *beg = xrow;
210 *end = xrow == lbuf_len(xb) ? xrow : xrow + 1;
211 return 0;
213 while (*loc) {
214 int end0 = *end;
215 *end = ex_lineno(loc) + 1;
216 *beg = naddr++ ? end0 - 1 : *end - 1;
217 if (!naddr++)
218 *beg = *end - 1;
219 while (*loc && *loc != ';' && *loc != ',')
220 loc++;
221 if (!*loc)
222 break;
223 if (*loc == ';')
224 xrow = *end - 1;
225 loc++;
227 if (*beg < 0 || *beg >= lbuf_len(xb))
228 return 1;
229 if (*end < *beg || *end > lbuf_len(xb))
230 return 1;
231 return 0;
234 static int ec_write(char *ec);
236 static int ex_modifiedbuffer(char *msg)
238 if (!lbuf_modified(xb))
239 return 0;
240 if (xaw && ex_path()[0])
241 return ec_write("w");
242 if (msg)
243 ex_show(msg);
244 return 1;
247 static int ec_quit(char *ec)
249 char cmd[EXLEN];
250 ex_cmd(ec, cmd);
251 if (!strchr(cmd, '!'))
252 if (ex_modifiedbuffer("buffer modified\n"))
253 return 1;
254 xquit = 1;
255 return 0;
258 static int ex_expand(char *d, char *s)
260 while (*s) {
261 int c = (unsigned char) *s++;
262 if (c == '%') {
263 if (!bufs[0].path || !bufs[0].path[0]) {
264 ex_show("\"%\" is unset\n");
265 return 1;
267 strcpy(d, bufs[0].path);
268 d = strchr(d, '\0');
269 continue;
271 if (c == '#') {
272 if (!bufs[1].path || !bufs[1].path[0]) {
273 ex_show("\"#\" is unset\n");
274 return 1;
276 strcpy(d, bufs[1].path);
277 d = strchr(d, '\0');
278 continue;
280 if (c == '\\' && (*s == '%' || *s == '#'))
281 c = *s++;
282 *d++ = c;
284 *d = '\0';
285 return 0;
288 static int ec_edit(char *ec)
290 char msg[128];
291 char arg[EXLEN], cmd[EXLEN];
292 char path[PATHLEN];
293 int fd;
294 ex_cmd(ec, cmd);
295 ex_arg(ec, arg);
296 if (!strchr(cmd, '!'))
297 if (xb && ex_modifiedbuffer("buffer modified\n"))
298 return 1;
299 if (ex_expand(path, arg))
300 return 1;
301 bufs[0].row = xrow;
302 if (arg[0] && bufs_find(path) >= 0) {
303 bufs_switch(bufs_find(path));
304 return 0;
306 if (path[0] || !bufs[0].path)
307 bufs_switch(bufs_open(path));
308 fd = open(ex_path(), O_RDONLY);
309 if (fd >= 0) {
310 lbuf_rm(xb, 0, lbuf_len(xb));
311 lbuf_rd(xb, fd, 0);
312 close(fd);
313 snprintf(msg, sizeof(msg), "\"%s\" %d lines [r]\n",
314 ex_path(), lbuf_len(xb));
315 ex_show(msg);
317 xrow = MAX(0, MIN(xrow, lbuf_len(xb) - 1));
318 lbuf_modified(xb);
319 lbuf_saved(xb, path[0] != '\0');
320 return 0;
323 static int ec_read(char *ec)
325 char arg[EXLEN], loc[EXLEN];
326 char msg[128];
327 char *path;
328 int fd;
329 int beg, end;
330 int n = lbuf_len(xb);
331 ex_arg(ec, arg);
332 ex_loc(ec, loc);
333 path = arg[0] ? arg : ex_path();
334 fd = open(path, O_RDONLY);
335 if (fd < 0) {
336 ex_show("read failed\n");
337 return 1;
339 if (ex_region(loc, &beg, &end))
340 return 1;
341 lbuf_rd(xb, fd, lbuf_len(xb) ? end : 0);
342 close(fd);
343 xrow = end + lbuf_len(xb) - n;
344 snprintf(msg, sizeof(msg), "\"%s\" %d lines [r]\n",
345 path, lbuf_len(xb) - n);
346 ex_show(msg);
347 return 0;
350 static int ec_write(char *ec)
352 char cmd[EXLEN], arg[EXLEN], loc[EXLEN];
353 char msg[128];
354 char *path;
355 int beg, end;
356 int fd;
357 ex_cmd(ec, cmd);
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 (!loc[0]) {
364 beg = 0;
365 end = lbuf_len(xb);
367 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
368 if (fd < 0) {
369 ex_show("write failed\n");
370 return 1;
372 lbuf_wr(xb, fd, beg, end);
373 close(fd);
374 snprintf(msg, sizeof(msg), "\"%s\" %d lines [w]\n",
375 path, end - beg);
376 ex_show(msg);
377 if (!ex_path()[0]) {
378 free(bufs[0].path);
379 bufs[0].path = uc_dup(path);
381 if (!strcmp(ex_path(), path))
382 lbuf_saved(xb, 0);
383 if (!strcmp("wq", cmd))
384 ec_quit("wq");
385 return 0;
388 static int ec_insert(char *ec)
390 char arg[EXLEN], cmd[EXLEN], loc[EXLEN];
391 struct sbuf *sb;
392 char *s;
393 int beg, end;
394 int n;
395 ex_arg(ec, arg);
396 ex_cmd(ec, cmd);
397 ex_loc(ec, loc);
398 if (ex_region(loc, &beg, &end) && (beg != 0 || end != 0))
399 return 1;
400 if (cmd[0] == 'c') {
401 if (lbuf_len(xb))
402 lbuf_rm(xb, beg, end);
403 end = beg + 1;
405 sb = sbuf_make();
406 while ((s = ex_read(""))) {
407 if (!strcmp(".", s)) {
408 free(s);
409 break;
411 sbuf_str(sb, s);
412 sbuf_chr(sb, '\n');
413 free(s);
415 if (cmd[0] == 'a')
416 if (end > lbuf_len(xb))
417 end = lbuf_len(xb);
418 n = lbuf_len(xb);
419 lbuf_put(xb, end, sbuf_buf(sb));
420 xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
421 sbuf_free(sb);
422 return 0;
425 static int ec_print(char *ec)
427 char cmd[EXLEN], loc[EXLEN];
428 int beg, end;
429 int i;
430 ex_cmd(ec, cmd);
431 ex_loc(ec, loc);
432 if (!cmd[0] && !loc[0]) {
433 if (xrow >= lbuf_len(xb) - 1)
434 return 1;
435 xrow = xrow + 1;
437 if (ex_region(loc, &beg, &end))
438 return 1;
439 for (i = beg; i < end; i++)
440 ex_show(lbuf_get(xb, i));
441 xrow = end;
442 return 0;
445 static void ex_yank(int reg, int beg, int end)
447 char *buf = lbuf_cp(xb, beg, end);
448 reg_put(reg, buf, 1);
449 free(buf);
452 static int ec_delete(char *ec)
454 char loc[EXLEN];
455 char arg[EXLEN];
456 int beg, end;
457 ex_loc(ec, loc);
458 ex_arg(ec, arg);
459 if (ex_region(loc, &beg, &end) || !lbuf_len(xb))
460 return 1;
461 ex_yank(arg[0], beg, end);
462 lbuf_rm(xb, beg, end);
463 xrow = beg;
464 return 0;
467 static int ec_yank(char *ec)
469 char loc[EXLEN];
470 char arg[EXLEN];
471 int beg, end;
472 ex_loc(ec, loc);
473 ex_arg(ec, arg);
474 if (ex_region(loc, &beg, &end) || !lbuf_len(xb))
475 return 1;
476 ex_yank(arg[0], beg, end);
477 return 0;
480 static int ec_put(char *ec)
482 char loc[EXLEN];
483 char arg[EXLEN];
484 int beg, end;
485 int lnmode;
486 char *buf;
487 int n = lbuf_len(xb);
488 ex_loc(ec, loc);
489 ex_arg(ec, arg);
490 buf = reg_get(arg[0], &lnmode);
491 if (!buf || ex_region(loc, &beg, &end))
492 return 1;
493 lbuf_put(xb, end, buf);
494 xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
495 return 0;
498 static int ec_lnum(char *ec)
500 char loc[EXLEN];
501 char msg[128];
502 int beg, end;
503 ex_loc(ec, loc);
504 if (ex_region(loc, &beg, &end))
505 return 1;
506 sprintf(msg, "%d\n", end);
507 ex_show(msg);
508 return 0;
511 static int ec_undo(char *ec)
513 return lbuf_undo(xb);
516 static int ec_redo(char *ec)
518 return lbuf_redo(xb);
521 static int ec_mark(char *ec)
523 char loc[EXLEN], arg[EXLEN];
524 int beg, end;
525 ex_arg(ec, arg);
526 ex_loc(ec, loc);
527 if (ex_region(loc, &beg, &end))
528 return 1;
529 lbuf_mark(xb, arg[0], end - 1, 0);
530 return 0;
533 static char *readuntil(char **src, int delim)
535 struct sbuf *sbuf = sbuf_make();
536 char *s = *src;
537 /* reading the pattern */
538 while (*s && *s != delim) {
539 if (s[0] == '\\' && s[1])
540 sbuf_chr(sbuf, (unsigned char) *s++);
541 sbuf_chr(sbuf, (unsigned char) *s++);
543 if (*s) /* skipping the delimiter */
544 s++;
545 *src = s;
546 return sbuf_done(sbuf);
549 static int ec_substitute(char *ec)
551 char loc[EXLEN], arg[EXLEN];
552 struct rset *re;
553 int offs[32];
554 int beg, end;
555 char *pat, *rep;
556 char *s = arg;
557 int delim;
558 int i;
559 ex_arg(ec, arg);
560 ex_loc(ec, loc);
561 if (ex_region(loc, &beg, &end))
562 return 1;
563 delim = (unsigned char) *s++;
564 pat = readuntil(&s, delim);
565 rep = readuntil(&s, delim);
566 re = rset_make(1, &pat, xic ? RE_ICASE : 0);
567 if (!re)
568 return 1;
569 for (i = beg; i < end; i++) {
570 char *ln = lbuf_get(xb, i);
571 struct sbuf *r = sbuf_make();
572 while (rset_find(re, ln, LEN(offs) / 2, offs, 0) >= 0) {
573 sbuf_mem(r, ln, offs[0]);
574 sbuf_str(r, rep);
575 ln += offs[1];
576 if (!strchr(s, 'g'))
577 break;
579 sbuf_str(r, ln);
580 lbuf_rm(xb, i, i + 1);
581 lbuf_put(xb, i, sbuf_buf(r));
582 sbuf_free(r);
584 rset_free(re);
585 free(pat);
586 free(rep);
587 return 0;
590 static int ec_exec(char *ec)
592 char cmd[EXLEN];
593 char arg[EXLEN];
594 ex_modifiedbuffer(NULL);
595 if (ex_expand(arg, ex_cmd(ec, cmd)))
596 return 1;
597 return cmd_exec(arg);
600 static int ec_make(char *ec)
602 char cmd[EXLEN];
603 char arg[EXLEN];
604 char make[EXLEN];
605 ex_modifiedbuffer(NULL);
606 if (ex_expand(arg, ex_cmd(ec, cmd)))
607 return 1;
608 sprintf(make, "make %s", arg);
609 return cmd_exec(make);
612 static struct option {
613 char *abbr;
614 char *name;
615 int *var;
616 } options[] = {
617 {"ai", "autoindent", &xai},
618 {"aw", "autowrite", &xaw},
619 {"ic", "ignorecase", &xic},
620 {"td", "textdirection", &xdir},
621 {"shape", "shape", &xshape},
622 {"order", "xorder", &xorder},
625 static char *cutword(char *s, char *d)
627 while (isspace(*s))
628 s++;
629 while (*s && !isspace(*s))
630 *d++ = *s++;
631 while (isspace(*s))
632 s++;
633 *d = '\0';
634 return s;
637 static int ec_set(char *ec)
639 char arg[EXLEN];
640 char tok[EXLEN];
641 char opt[EXLEN];
642 char *s = arg;
643 int val = 0;
644 int i;
645 ex_arg(ec, arg);
646 if (*s) {
647 s = cutword(s, tok);
648 if (tok[0] == 'n' && tok[1] == 'o') {
649 strcpy(opt, tok + 2);
650 val = 0;
651 } else {
652 char *r = strchr(tok, '=');
653 if (r) {
654 *r = '\0';
655 strcpy(opt, tok);
656 val = atoi(r + 1);
657 } else {
658 strcpy(opt, tok);
659 val = 1;
662 for (i = 0; i < LEN(options); i++) {
663 struct option *o = &options[i];
664 if (!strcmp(o->abbr, opt) || !strcmp(o->name, opt)) {
665 *o->var = val;
666 return 0;
669 ex_show("unknown option");
670 return 1;
672 return 0;
675 static struct excmd {
676 char *abbr;
677 char *name;
678 int (*ec)(char *s);
679 } excmds[] = {
680 {"p", "print", ec_print},
681 {"a", "append", ec_insert},
682 {"i", "insert", ec_insert},
683 {"d", "delete", ec_delete},
684 {"c", "change", ec_insert},
685 {"e", "edit", ec_edit},
686 {"e!", "edit!", ec_edit},
687 {"=", "=", ec_lnum},
688 {"k", "mark", ec_mark},
689 {"pu", "put", ec_put},
690 {"q", "quit", ec_quit},
691 {"q!", "quit!", ec_quit},
692 {"r", "read", ec_read},
693 {"w", "write", ec_write},
694 {"wq", "wq", ec_write},
695 {"u", "undo", ec_undo},
696 {"r", "redo", ec_redo},
697 {"se", "set", ec_set},
698 {"s", "substitute", ec_substitute},
699 {"ya", "yank", ec_yank},
700 {"!", "!", ec_exec},
701 {"make", "make", ec_make},
702 {"", "", ec_print},
705 /* execute a single ex command */
706 void ex_command(char *ln)
708 char cmd[EXLEN];
709 int i;
710 ex_cmd(ln, cmd);
711 for (i = 0; i < LEN(excmds); i++) {
712 if (!strcmp(excmds[i].abbr, cmd) || !strcmp(excmds[i].name, cmd)) {
713 excmds[i].ec(ln);
714 break;
717 lbuf_modified(xb);
720 /* ex main loop */
721 void ex(void)
723 if (xled)
724 term_init();
725 while (!xquit) {
726 char *ln = ex_read(":");
727 if (ln)
728 ex_command(ln);
729 free(ln);
731 if (xled)
732 term_done();
735 void ex_init(char **files)
737 char cmd[EXLEN];
738 snprintf(cmd, sizeof(cmd), "e %s", files[0] ? files[0] : "");
739 ec_edit(cmd);
742 void ex_done(void)
744 int i;
745 for (i = 0; i < LEN(bufs); i++)
746 bufs_free(i);