ex: spaces in substitute's arguments
[neatvi.git] / ex.c
blob2ccd06f00e82d9c84fa11004511339727787cc58
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 char *ex_argeol(char *ec)
148 char arg[EXLEN];
149 char *s = ex_cmd(ec, arg);
150 while (isspace((unsigned char) *s))
151 s++;
152 return s;
155 static int ex_search(char *pat)
157 struct sbuf *kw;
158 int dir = *pat == '/' ? 1 : -1;
159 char *b = pat;
160 char *e = b;
161 char *re_kw[1];
162 int i = xrow;
163 struct rset *re;
164 kw = sbuf_make();
165 while (*++e) {
166 if (*e == *pat)
167 break;
168 sbuf_chr(kw, (unsigned char) *e);
169 if (*e == '\\' && e[1])
170 e++;
172 re_kw[0] = sbuf_buf(kw);
173 re = rset_make(1, re_kw, xic ? RE_ICASE : 0);
174 sbuf_free(kw);
175 if (!re)
176 return i;
177 while (i >= 0 && i < lbuf_len(xb)) {
178 if (rset_find(re, lbuf_get(xb, i), 0, NULL, 0) >= 0)
179 break;
180 i += dir;
182 rset_free(re);
183 return i;
186 static int ex_lineno(char *num)
188 int n = xrow;
189 if (!num[0] || num[0] == '.')
190 n = xrow;
191 if (isdigit(num[0]))
192 n = atoi(num) - 1;
193 if (num[0] == '$')
194 n = lbuf_len(xb) - 1;
195 if (num[0] == '-')
196 n = xrow - (num[1] ? ex_lineno(num + 1) : 1);
197 if (num[0] == '+')
198 n = xrow + (num[1] ? ex_lineno(num + 1) : 1);
199 if (num[0] == '\'')
200 lbuf_jump(xb, num[1], &n, NULL);
201 if (num[0] == '/' && num[1])
202 n = ex_search(num);
203 if (num[0] == '?' && num[1])
204 n = ex_search(num);
205 return n;
208 /* parse ex command location */
209 static int ex_region(char *loc, int *beg, int *end)
211 int naddr = 0;
212 if (!strcmp("%", loc)) {
213 *beg = 0;
214 *end = MAX(0, lbuf_len(xb));
215 return 0;
217 if (!*loc) {
218 *beg = xrow;
219 *end = xrow == lbuf_len(xb) ? xrow : xrow + 1;
220 return 0;
222 while (*loc) {
223 int end0 = *end;
224 *end = ex_lineno(loc) + 1;
225 *beg = naddr++ ? end0 - 1 : *end - 1;
226 if (!naddr++)
227 *beg = *end - 1;
228 while (*loc && *loc != ';' && *loc != ',')
229 loc++;
230 if (!*loc)
231 break;
232 if (*loc == ';')
233 xrow = *end - 1;
234 loc++;
236 if (*beg < 0 || *beg >= lbuf_len(xb))
237 return 1;
238 if (*end < *beg || *end > lbuf_len(xb))
239 return 1;
240 return 0;
243 static int ec_write(char *ec);
245 static int ex_modifiedbuffer(char *msg)
247 if (!lbuf_modified(xb))
248 return 0;
249 if (xaw && ex_path()[0])
250 return ec_write("w");
251 if (msg)
252 ex_show(msg);
253 return 1;
256 static int ec_quit(char *ec)
258 char cmd[EXLEN];
259 ex_cmd(ec, cmd);
260 if (!strchr(cmd, '!'))
261 if (ex_modifiedbuffer("buffer modified\n"))
262 return 1;
263 xquit = 1;
264 return 0;
267 static int ex_expand(char *d, char *s)
269 while (*s) {
270 int c = (unsigned char) *s++;
271 if (c == '%') {
272 if (!bufs[0].path || !bufs[0].path[0]) {
273 ex_show("\"%\" is unset\n");
274 return 1;
276 strcpy(d, bufs[0].path);
277 d = strchr(d, '\0');
278 continue;
280 if (c == '#') {
281 if (!bufs[1].path || !bufs[1].path[0]) {
282 ex_show("\"#\" is unset\n");
283 return 1;
285 strcpy(d, bufs[1].path);
286 d = strchr(d, '\0');
287 continue;
289 if (c == '\\' && (*s == '%' || *s == '#'))
290 c = *s++;
291 *d++ = c;
293 *d = '\0';
294 return 0;
297 static int ec_edit(char *ec)
299 char msg[128];
300 char arg[EXLEN], cmd[EXLEN];
301 char path[EXLEN];
302 int fd;
303 ex_cmd(ec, cmd);
304 ex_arg(ec, arg);
305 if (!strchr(cmd, '!'))
306 if (xb && ex_modifiedbuffer("buffer modified\n"))
307 return 1;
308 if (ex_expand(path, arg))
309 return 1;
310 bufs[0].row = xrow;
311 if (arg[0] && bufs_find(path) >= 0) {
312 bufs_switch(bufs_find(path));
313 return 0;
315 if (path[0] || !bufs[0].path)
316 bufs_switch(bufs_open(path));
317 fd = open(ex_path(), O_RDONLY);
318 if (fd >= 0) {
319 lbuf_rm(xb, 0, lbuf_len(xb));
320 lbuf_rd(xb, fd, 0);
321 close(fd);
322 snprintf(msg, sizeof(msg), "\"%s\" %d lines [r]\n",
323 ex_path(), lbuf_len(xb));
324 ex_show(msg);
326 xrow = MAX(0, MIN(xrow, lbuf_len(xb) - 1));
327 lbuf_saved(xb, path[0] != '\0');
328 return 0;
331 static int ec_read(char *ec)
333 char arg[EXLEN], loc[EXLEN];
334 char msg[128];
335 int beg, end;
336 char *path;
337 char *obuf;
338 int n = lbuf_len(xb);
339 ex_arg(ec, arg);
340 ex_loc(ec, loc);
341 path = arg[0] ? arg : ex_path();
342 if (ex_region(loc, &beg, &end))
343 return 1;
344 if (arg[0] == '!') {
345 if (ex_expand(arg, ex_argeol(ec)))
346 return 1;
347 obuf = cmd_pipe(arg + 1, NULL, 0, 1);
348 if (obuf)
349 lbuf_put(xb, MIN(xrow + 1, lbuf_len(xb)), obuf);
350 free(obuf);
351 } else {
352 int fd = open(path, O_RDONLY);
353 if (fd < 0) {
354 ex_show("read failed\n");
355 return 1;
357 lbuf_rd(xb, fd, lbuf_len(xb) ? end : 0);
358 close(fd);
360 xrow = end + lbuf_len(xb) - n - 1;
361 snprintf(msg, sizeof(msg), "\"%s\" %d lines [r]\n",
362 path, lbuf_len(xb) - n);
363 ex_show(msg);
364 return 0;
367 static int ec_write(char *ec)
369 char cmd[EXLEN], arg[EXLEN], loc[EXLEN];
370 char msg[128];
371 char *path;
372 char *ibuf;
373 int beg, end;
374 ex_cmd(ec, cmd);
375 ex_arg(ec, arg);
376 ex_loc(ec, loc);
377 path = arg[0] ? arg : ex_path();
378 if (cmd[0] == 'x' && !lbuf_modified(xb))
379 return ec_quit(cmd);
380 if (ex_region(loc, &beg, &end))
381 return 1;
382 if (!loc[0]) {
383 beg = 0;
384 end = lbuf_len(xb);
386 if (arg[0] == '!') {
387 if (ex_expand(arg, ex_argeol(ec)))
388 return 1;
389 ibuf = lbuf_cp(xb, beg, end);
390 ex_print(NULL);
391 cmd_pipe(arg + 1, ibuf, 1, 0);
392 free(ibuf);
393 } else {
394 int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
395 if (fd < 0) {
396 ex_show("write failed\n");
397 return 1;
399 lbuf_wr(xb, fd, beg, end);
400 close(fd);
402 snprintf(msg, sizeof(msg), "\"%s\" %d lines [w]\n",
403 path, end - beg);
404 ex_show(msg);
405 if (!ex_path()[0]) {
406 free(bufs[0].path);
407 bufs[0].path = uc_dup(path);
409 if (!strcmp(ex_path(), path))
410 lbuf_saved(xb, 0);
411 if (cmd[0] == 'x' || (cmd[0] == 'w' && cmd[1] == 'q'))
412 ec_quit(cmd);
413 return 0;
416 static int ec_insert(char *ec)
418 char arg[EXLEN], cmd[EXLEN], loc[EXLEN];
419 struct sbuf *sb;
420 char *s;
421 int beg, end;
422 int n;
423 ex_arg(ec, arg);
424 ex_cmd(ec, cmd);
425 ex_loc(ec, loc);
426 if (ex_region(loc, &beg, &end) && (beg != 0 || end != 0))
427 return 1;
428 if (cmd[0] == 'c') {
429 if (lbuf_len(xb))
430 lbuf_rm(xb, beg, end);
431 end = beg + 1;
433 sb = sbuf_make();
434 while ((s = ex_read(""))) {
435 if (!strcmp(".", s)) {
436 free(s);
437 break;
439 sbuf_str(sb, s);
440 sbuf_chr(sb, '\n');
441 free(s);
443 if (cmd[0] == 'a')
444 if (end > lbuf_len(xb))
445 end = lbuf_len(xb);
446 n = lbuf_len(xb);
447 lbuf_put(xb, end, sbuf_buf(sb));
448 xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
449 sbuf_free(sb);
450 return 0;
453 static int ec_print(char *ec)
455 char cmd[EXLEN], loc[EXLEN];
456 int beg, end;
457 int i;
458 ex_cmd(ec, cmd);
459 ex_loc(ec, loc);
460 if (!cmd[0] && !loc[0]) {
461 if (xrow >= lbuf_len(xb) - 1)
462 return 1;
463 xrow = xrow + 1;
465 if (ex_region(loc, &beg, &end))
466 return 1;
467 for (i = beg; i < end; i++)
468 ex_print(lbuf_get(xb, i));
469 xrow = end;
470 return 0;
473 static void ex_yank(int reg, int beg, int end)
475 char *buf = lbuf_cp(xb, beg, end);
476 reg_put(reg, buf, 1);
477 free(buf);
480 static int ec_delete(char *ec)
482 char loc[EXLEN];
483 char arg[EXLEN];
484 int beg, end;
485 ex_loc(ec, loc);
486 ex_arg(ec, arg);
487 if (ex_region(loc, &beg, &end) || !lbuf_len(xb))
488 return 1;
489 ex_yank(arg[0], beg, end);
490 lbuf_rm(xb, beg, end);
491 xrow = beg;
492 return 0;
495 static int ec_yank(char *ec)
497 char loc[EXLEN];
498 char arg[EXLEN];
499 int beg, end;
500 ex_loc(ec, loc);
501 ex_arg(ec, arg);
502 if (ex_region(loc, &beg, &end) || !lbuf_len(xb))
503 return 1;
504 ex_yank(arg[0], beg, end);
505 return 0;
508 static int ec_put(char *ec)
510 char loc[EXLEN];
511 char arg[EXLEN];
512 int beg, end;
513 int lnmode;
514 char *buf;
515 int n = lbuf_len(xb);
516 ex_loc(ec, loc);
517 ex_arg(ec, arg);
518 buf = reg_get(arg[0], &lnmode);
519 if (!buf || ex_region(loc, &beg, &end))
520 return 1;
521 lbuf_put(xb, end, buf);
522 xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
523 return 0;
526 static int ec_lnum(char *ec)
528 char loc[EXLEN];
529 char msg[128];
530 int beg, end;
531 ex_loc(ec, loc);
532 if (ex_region(loc, &beg, &end))
533 return 1;
534 sprintf(msg, "%d\n", end);
535 ex_print(msg);
536 return 0;
539 static int ec_undo(char *ec)
541 return lbuf_undo(xb);
544 static int ec_redo(char *ec)
546 return lbuf_redo(xb);
549 static int ec_mark(char *ec)
551 char loc[EXLEN], arg[EXLEN];
552 int beg, end;
553 ex_arg(ec, arg);
554 ex_loc(ec, loc);
555 if (ex_region(loc, &beg, &end))
556 return 1;
557 lbuf_mark(xb, arg[0], end - 1, 0);
558 return 0;
561 static char *readuntil(char **src, int delim)
563 struct sbuf *sbuf = sbuf_make();
564 char *s = *src;
565 /* reading the pattern */
566 while (*s && *s != delim) {
567 if (s[0] == '\\' && s[1])
568 sbuf_chr(sbuf, (unsigned char) *s++);
569 sbuf_chr(sbuf, (unsigned char) *s++);
571 if (*s) /* skipping the delimiter */
572 s++;
573 *src = s;
574 return sbuf_done(sbuf);
577 static int ec_substitute(char *ec)
579 char loc[EXLEN];
580 struct rset *re;
581 int offs[32];
582 int beg, end;
583 char *pat, *rep;
584 char *s;
585 int delim;
586 int i;
587 ex_loc(ec, loc);
588 if (ex_region(loc, &beg, &end))
589 return 1;
590 s = ex_argeol(ec);
591 delim = (unsigned char) *s++;
592 pat = readuntil(&s, delim);
593 rep = readuntil(&s, delim);
594 re = rset_make(1, &pat, xic ? RE_ICASE : 0);
595 if (!re)
596 return 1;
597 for (i = beg; i < end; i++) {
598 char *ln = lbuf_get(xb, i);
599 struct sbuf *r = sbuf_make();
600 while (rset_find(re, ln, LEN(offs) / 2, offs, 0) >= 0) {
601 sbuf_mem(r, ln, offs[0]);
602 sbuf_str(r, rep);
603 ln += offs[1];
604 if (!strchr(s, 'g'))
605 break;
607 sbuf_str(r, ln);
608 lbuf_rm(xb, i, i + 1);
609 lbuf_put(xb, i, sbuf_buf(r));
610 sbuf_free(r);
612 rset_free(re);
613 free(pat);
614 free(rep);
615 return 0;
618 static int ec_exec(char *ec)
620 char arg[EXLEN];
621 ex_modifiedbuffer(NULL);
622 if (ex_expand(arg, ex_argeol(ec)))
623 return 1;
624 ex_print(NULL);
625 if (cmd_exec(arg))
626 return 1;
627 return 0;
630 static int ec_make(char *ec)
632 char arg[EXLEN];
633 char make[EXLEN];
634 ex_modifiedbuffer(NULL);
635 if (ex_expand(arg, ex_argeol(ec)))
636 return 1;
637 sprintf(make, "make %s", arg);
638 ex_print(NULL);
639 if (cmd_exec(make))
640 return 1;
641 return 0;
644 static struct option {
645 char *abbr;
646 char *name;
647 int *var;
648 } options[] = {
649 {"ai", "autoindent", &xai},
650 {"aw", "autowrite", &xaw},
651 {"ic", "ignorecase", &xic},
652 {"td", "textdirection", &xdir},
653 {"shape", "shape", &xshape},
654 {"order", "xorder", &xorder},
657 static char *cutword(char *s, char *d)
659 while (isspace(*s))
660 s++;
661 while (*s && !isspace(*s))
662 *d++ = *s++;
663 while (isspace(*s))
664 s++;
665 *d = '\0';
666 return s;
669 static int ec_set(char *ec)
671 char arg[EXLEN];
672 char tok[EXLEN];
673 char opt[EXLEN];
674 char *s = arg;
675 int val = 0;
676 int i;
677 ex_arg(ec, arg);
678 if (*s) {
679 s = cutword(s, tok);
680 if (tok[0] == 'n' && tok[1] == 'o') {
681 strcpy(opt, tok + 2);
682 val = 0;
683 } else {
684 char *r = strchr(tok, '=');
685 if (r) {
686 *r = '\0';
687 strcpy(opt, tok);
688 val = atoi(r + 1);
689 } else {
690 strcpy(opt, tok);
691 val = 1;
694 for (i = 0; i < LEN(options); i++) {
695 struct option *o = &options[i];
696 if (!strcmp(o->abbr, opt) || !strcmp(o->name, opt)) {
697 *o->var = val;
698 return 0;
701 ex_show("unknown option");
702 return 1;
704 return 0;
707 static struct excmd {
708 char *abbr;
709 char *name;
710 int (*ec)(char *s);
711 } excmds[] = {
712 {"p", "print", ec_print},
713 {"a", "append", ec_insert},
714 {"i", "insert", ec_insert},
715 {"d", "delete", ec_delete},
716 {"c", "change", ec_insert},
717 {"e", "edit", ec_edit},
718 {"e!", "edit!", ec_edit},
719 {"=", "=", ec_lnum},
720 {"k", "mark", ec_mark},
721 {"pu", "put", ec_put},
722 {"q", "quit", ec_quit},
723 {"q!", "quit!", ec_quit},
724 {"r", "read", ec_read},
725 {"w", "write", ec_write},
726 {"wq", "wq", ec_write},
727 {"u", "undo", ec_undo},
728 {"r", "redo", ec_redo},
729 {"se", "set", ec_set},
730 {"s", "substitute", ec_substitute},
731 {"x", "xit", ec_write},
732 {"x!", "xit!", ec_write},
733 {"ya", "yank", ec_yank},
734 {"!", "!", ec_exec},
735 {"make", "make", ec_make},
736 {"", "", ec_print},
739 /* execute a single ex command */
740 void ex_command(char *ln)
742 char cmd[EXLEN];
743 int i;
744 ex_cmd(ln, cmd);
745 for (i = 0; i < LEN(excmds); i++) {
746 if (!strcmp(excmds[i].abbr, cmd) || !strcmp(excmds[i].name, cmd)) {
747 excmds[i].ec(ln);
748 break;
751 lbuf_modified(xb);
754 /* ex main loop */
755 void ex(void)
757 while (!xquit) {
758 char *ln = ex_read(":");
759 if (ln)
760 ex_command(ln);
761 free(ln);
765 void ex_init(char **files)
767 char cmd[EXLEN];
768 snprintf(cmd, sizeof(cmd), "e %s", files[0] ? files[0] : "");
769 ec_edit(cmd);
772 void ex_done(void)
774 int i;
775 for (i = 0; i < LEN(bufs); i++)
776 bufs_free(i);