ex: edit command and unspecified alternate file
[neatvi.git] / ex.c
blob75bb7de311c9f6668241cf3a4733676f51110f52
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 char xpath[PATHLEN]; /* current file */
13 char xpath_alt[PATHLEN]; /* alternate file */
14 char xft[32]; /* filetype */
15 int xquit; /* exit if set */
16 int xvis; /* visual mode */
17 int xai = 1; /* autoindent option */
18 int xic = 1; /* ignorecase option */
19 struct lbuf *xb; /* current buffer */
20 int xrow, xoff, xtop; /* current row, column, and top row */
21 int xrow_alt; /* alternate row, column, and top row */
22 int xled = 1; /* use the line editor */
23 int xdir = +1; /* current direction context */
24 int xshape = 1; /* perform letter shaping */
25 int xorder = 1; /* change the order of characters */
27 /* read ex command location */
28 static char *ex_loc(char *s, char *loc)
30 while (*s == ':' || isspace((unsigned char) *s))
31 s++;
32 while (*s && !isalpha((unsigned char) *s) && *s != '=') {
33 if (*s == '\'')
34 *loc++ = *s++;
35 if (*s == '/' || *s == '?') {
36 int d = *s;
37 *loc++ = *s++;
38 while (*s && *s != d) {
39 if (*s == '\\' && s[1])
40 *loc++ = *s++;
41 *loc++ = *s++;
44 *loc++ = *s++;
46 *loc = '\0';
47 return s;
50 /* read ex command name */
51 static char *ex_cmd(char *s, char *cmd)
53 char *cmd0 = cmd;
54 s = ex_loc(s, cmd);
55 while (isspace((unsigned char) *s))
56 s++;
57 while (isalpha((unsigned char) *s) || *s == '=' || *s == '!')
58 if ((*cmd++ = *s++) == 'k' && cmd == cmd0 + 1)
59 break;
60 *cmd = '\0';
61 return s;
64 /* read ex command argument */
65 static char *ex_arg(char *s, char *arg)
67 s = ex_cmd(s, arg);
68 while (isspace((unsigned char) *s))
69 s++;
70 while (*s && !isspace((unsigned char) *s))
71 *arg++ = *s++;
72 *arg = '\0';
73 return s;
76 static int ex_search(char *pat)
78 struct sbuf *kw;
79 int dir = *pat == '/' ? 1 : -1;
80 char *b = pat;
81 char *e = b;
82 char *re_kw[1];
83 int i = xrow;
84 struct rset *re;
85 kw = sbuf_make();
86 while (*++e) {
87 if (*e == *pat)
88 break;
89 sbuf_chr(kw, (unsigned char) *e);
90 if (*e == '\\' && e[1])
91 e++;
93 re_kw[0] = sbuf_buf(kw);
94 re = rset_make(1, re_kw, xic ? RE_ICASE : 0);
95 sbuf_free(kw);
96 if (!re)
97 return i;
98 while (i >= 0 && i < lbuf_len(xb)) {
99 if (rset_find(re, lbuf_get(xb, i), 0, NULL, 0) >= 0)
100 break;
101 i += dir;
103 rset_free(re);
104 return i;
107 static int ex_lineno(char *num)
109 int n = xrow;
110 if (!num[0] || num[0] == '.')
111 n = xrow;
112 if (isdigit(num[0]))
113 n = atoi(num) - 1;
114 if (num[0] == '$')
115 n = lbuf_len(xb) - 1;
116 if (num[0] == '-')
117 n = xrow - (num[1] ? ex_lineno(num + 1) : 1);
118 if (num[0] == '+')
119 n = xrow + (num[1] ? ex_lineno(num + 1) : 1);
120 if (num[0] == '\'')
121 n = lbuf_markpos(xb, num[1]);
122 if (num[0] == '/' && num[1])
123 n = ex_search(num);
124 if (num[0] == '?' && num[1])
125 n = ex_search(num);
126 return n;
129 /* parse ex command location */
130 static int ex_region(char *loc, int *beg, int *end)
132 int naddr = 0;
133 if (!strcmp("%", loc)) {
134 *beg = 0;
135 *end = MAX(0, lbuf_len(xb));
136 return 0;
138 if (!*loc) {
139 *beg = xrow;
140 *end = xrow == lbuf_len(xb) ? xrow : xrow + 1;
141 return 0;
143 while (*loc) {
144 int end0 = *end;
145 *end = ex_lineno(loc) + 1;
146 *beg = naddr++ ? end0 - 1 : *end - 1;
147 if (!naddr++)
148 *beg = *end - 1;
149 while (*loc && *loc != ';' && *loc != ',')
150 loc++;
151 if (!*loc)
152 break;
153 if (*loc == ';')
154 xrow = *end - 1;
155 loc++;
157 if (*beg < 0 || *beg >= lbuf_len(xb))
158 return 1;
159 if (*end < *beg || *end > lbuf_len(xb))
160 return 1;
161 return 0;
164 static void ec_quit(char *ec)
166 xquit = 1;
169 static void ec_edit(char *ec)
171 char msg[128];
172 char arg[EXLEN];
173 int fd;
174 ex_arg(ec, arg);
175 if (!arg[0] || !strcmp(arg, "%") || !strcmp(xpath, arg)) {
176 strcpy(arg, xpath);
177 } else if (!strcmp(arg, "#")) {
178 char xpath_tmp[PATHLEN];
179 int xrow_tmp = xrow;
180 if (!xpath_alt[0]) {
181 ex_show("\"#\" is unset\n");
182 return;
184 strcpy(xpath_tmp, xpath_alt);
185 strcpy(xpath_alt, xpath);
186 strcpy(xpath, xpath_tmp);
187 xrow = xrow_alt;
188 xrow_alt = xrow_tmp;
189 xoff = 0;
190 xtop = 0;
191 } else {
192 strcpy(xpath_alt, xpath);
193 snprintf(xpath, PATHLEN, "%s", arg);
194 xrow_alt = xrow;
195 xrow = xvis ? 0 : 1 << 20;
197 strcpy(xft, syn_filetype(xpath));
198 fd = open(xpath, O_RDONLY);
199 lbuf_rm(xb, 0, lbuf_len(xb));
200 if (fd >= 0) {
201 lbuf_rd(xb, fd, 0);
202 close(fd);
203 snprintf(msg, sizeof(msg), "\"%s\" %d lines [r]\n",
204 xpath, lbuf_len(xb));
205 ex_show(msg);
207 xrow = MAX(0, MIN(xrow, lbuf_len(xb) - 1));
208 lbuf_undofree(xb);
211 static void ec_read(char *ec)
213 char arg[EXLEN], loc[EXLEN];
214 char msg[128];
215 char *path;
216 int fd;
217 int beg, end;
218 int n = lbuf_len(xb);
219 ex_arg(ec, arg);
220 ex_loc(ec, loc);
221 path = arg[0] ? arg : xpath;
222 fd = open(path, O_RDONLY);
223 if (fd >= 0 && !ex_region(loc, &beg, &end)) {
224 lbuf_rd(xb, fd, lbuf_len(xb) ? end : 0);
225 close(fd);
226 xrow = end + lbuf_len(xb) - n;
227 snprintf(msg, sizeof(msg), "\"%s\" %d lines [r]\n",
228 path, lbuf_len(xb) - n);
229 ex_show(msg);
233 static void ec_write(char *ec)
235 char cmd[EXLEN], arg[EXLEN], loc[EXLEN];
236 char msg[128];
237 char *path;
238 int beg, end;
239 int fd;
240 ex_cmd(ec, cmd);
241 ex_arg(ec, arg);
242 ex_loc(ec, loc);
243 path = arg[0] ? arg : xpath;
244 if (ex_region(loc, &beg, &end))
245 return;
246 if (!loc[0]) {
247 beg = 0;
248 end = lbuf_len(xb);
250 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
251 if (fd >= 0) {
252 lbuf_wr(xb, fd, beg, end);
253 close(fd);
254 snprintf(msg, sizeof(msg), "\"%s\" %d lines [w]\n",
255 path, end - beg);
256 ex_show(msg);
258 if (!strcmp("wq", cmd))
259 ec_quit("wq");
262 static void ec_insert(char *ec)
264 char arg[EXLEN], cmd[EXLEN], loc[EXLEN];
265 struct sbuf *sb;
266 char *s;
267 int beg, end;
268 int n;
269 ex_arg(ec, arg);
270 ex_cmd(ec, cmd);
271 ex_loc(ec, loc);
272 if (ex_region(loc, &beg, &end) && (beg != 0 || end != 0))
273 return;
274 if (cmd[0] == 'c') {
275 if (lbuf_len(xb))
276 lbuf_rm(xb, beg, end);
277 end = beg + 1;
279 sb = sbuf_make();
280 while ((s = ex_read(""))) {
281 if (!strcmp(".", s)) {
282 free(s);
283 break;
285 sbuf_str(sb, s);
286 sbuf_chr(sb, '\n');
287 free(s);
289 if (cmd[0] == 'a')
290 if (end > lbuf_len(xb))
291 end = lbuf_len(xb);
292 n = lbuf_len(xb);
293 lbuf_put(xb, end, sbuf_buf(sb));
294 xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
295 sbuf_free(sb);
298 static void ec_print(char *ec)
300 char cmd[EXLEN], loc[EXLEN];
301 int beg, end;
302 int i;
303 ex_cmd(ec, cmd);
304 ex_loc(ec, loc);
305 if (!cmd[0] && !loc[0]) {
306 if (xrow >= lbuf_len(xb) - 1)
307 return;
308 xrow = xrow + 1;
310 if (!ex_region(loc, &beg, &end)) {
311 for (i = beg; i < end; i++)
312 ex_show(lbuf_get(xb, i));
313 xrow = end;
317 static void ex_yank(int reg, int beg, int end)
319 char *buf = lbuf_cp(xb, beg, end);
320 reg_put(reg, buf, 1);
321 free(buf);
324 static void ec_delete(char *ec)
326 char loc[EXLEN];
327 char arg[EXLEN];
328 int beg, end;
329 ex_loc(ec, loc);
330 ex_arg(ec, arg);
331 if (!ex_region(loc, &beg, &end) && lbuf_len(xb)) {
332 ex_yank(arg[0], beg, end);
333 lbuf_rm(xb, beg, end);
334 xrow = beg;
338 static void ec_yank(char *ec)
340 char loc[EXLEN];
341 char arg[EXLEN];
342 int beg, end;
343 ex_loc(ec, loc);
344 ex_arg(ec, arg);
345 if (!ex_region(loc, &beg, &end) && lbuf_len(xb))
346 ex_yank(arg[0], beg, end);
349 static void ec_put(char *ec)
351 char loc[EXLEN];
352 char arg[EXLEN];
353 int beg, end;
354 int lnmode;
355 char *buf;
356 int n = lbuf_len(xb);
357 ex_loc(ec, loc);
358 ex_arg(ec, arg);
359 buf = reg_get(arg[0], &lnmode);
360 if (buf && !ex_region(loc, &beg, &end)) {
361 lbuf_put(xb, end, buf);
362 xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
366 static void ec_lnum(char *ec)
368 char loc[EXLEN];
369 char msg[128];
370 int beg, end;
371 ex_loc(ec, loc);
372 if (ex_region(loc, &beg, &end))
373 return;
374 sprintf(msg, "%d\n", end);
375 ex_show(msg);
378 static void ec_undo(char *ec)
380 lbuf_undo(xb);
383 static void ec_redo(char *ec)
385 lbuf_redo(xb);
388 static void ec_mark(char *ec)
390 char loc[EXLEN], arg[EXLEN];
391 int beg, end;
392 ex_arg(ec, arg);
393 ex_loc(ec, loc);
394 if (ex_region(loc, &beg, &end))
395 return;
396 lbuf_mark(xb, arg[0], end - 1);
399 static char *readuntil(char **src, int delim)
401 struct sbuf *sbuf = sbuf_make();
402 char *s = *src;
403 /* reading the pattern */
404 while (*s && *s != delim) {
405 if (s[0] == '\\' && s[1])
406 sbuf_chr(sbuf, (unsigned char) *s++);
407 sbuf_chr(sbuf, (unsigned char) *s++);
409 if (*s) /* skipping the delimiter */
410 s++;
411 *src = s;
412 return sbuf_done(sbuf);
415 static void ec_substitute(char *ec)
417 char loc[EXLEN], arg[EXLEN];
418 struct rset *re;
419 int offs[32];
420 int beg, end;
421 char *pat, *rep;
422 char *s = arg;
423 int delim;
424 int i;
425 ex_arg(ec, arg);
426 ex_loc(ec, loc);
427 if (ex_region(loc, &beg, &end))
428 return;
429 delim = (unsigned char) *s++;
430 pat = readuntil(&s, delim);
431 rep = readuntil(&s, delim);
432 re = rset_make(1, &pat, xic ? RE_ICASE : 0);
433 for (i = beg; i < end; i++) {
434 char *ln = lbuf_get(xb, i);
435 if (rset_find(re, ln, LEN(offs) / 2, offs, 0)) {
436 struct sbuf *r = sbuf_make();
437 sbuf_mem(r, ln, offs[0]);
438 sbuf_str(r, rep);
439 sbuf_str(r, ln + offs[1]);
440 lbuf_put(xb, i, sbuf_buf(r));
441 lbuf_rm(xb, i + 1, i + 2);
442 sbuf_free(r);
445 rset_free(re);
446 free(pat);
447 free(rep);
450 static struct option {
451 char *abbr;
452 char *name;
453 int *var;
454 } options[] = {
455 {"ai", "autoindent", &xai},
456 {"ic", "ignorecase", &xic},
457 {"td", "textdirection", &xdir},
458 {"shape", "shape", &xshape},
459 {"order", "xorder", &xorder},
462 static char *cutword(char *s, char *d)
464 while (isspace(*s))
465 s++;
466 while (*s && !isspace(*s))
467 *d++ = *s++;
468 while (isspace(*s))
469 s++;
470 *d = '\0';
471 return s;
474 static void ec_set(char *ec)
476 char arg[EXLEN];
477 char tok[EXLEN];
478 char opt[EXLEN];
479 char *s = arg;
480 int val = 0;
481 int i;
482 ex_arg(ec, arg);
483 if (*s) {
484 s = cutword(s, tok);
485 if (tok[0] == 'n' && tok[1] == 'o') {
486 strcpy(opt, tok + 2);
487 val = 0;
488 } else {
489 char *r = strchr(tok, '=');
490 if (r) {
491 *r = '\0';
492 strcpy(opt, tok);
493 val = atoi(r + 1);
494 } else {
495 strcpy(opt, tok);
496 val = 1;
499 for (i = 0; i < LEN(options); i++) {
500 struct option *o = &options[i];
501 if (!strcmp(o->abbr, opt) || !strcmp(o->name, opt))
502 *o->var = val;
507 static struct excmd {
508 char *abbr;
509 char *name;
510 void (*ec)(char *s);
511 } excmds[] = {
512 {"p", "print", ec_print},
513 {"a", "append", ec_insert},
514 {"i", "insert", ec_insert},
515 {"d", "delete", ec_delete},
516 {"c", "change", ec_insert},
517 {"e", "edit", ec_edit},
518 {"=", "=", ec_lnum},
519 {"k", "mark", ec_mark},
520 {"pu", "put", ec_put},
521 {"q", "quit", ec_quit},
522 {"r", "read", ec_read},
523 {"w", "write", ec_write},
524 {"wq", "wq", ec_write},
525 {"u", "undo", ec_undo},
526 {"r", "redo", ec_redo},
527 {"se", "set", ec_set},
528 {"s", "substitute", ec_substitute},
529 {"ya", "yank", ec_yank},
530 {"", "", ec_print},
533 /* execute a single ex command */
534 void ex_command(char *ln)
536 char cmd[EXLEN];
537 int i;
538 ex_cmd(ln, cmd);
539 for (i = 0; i < LEN(excmds); i++) {
540 if (!strcmp(excmds[i].abbr, cmd) || !strcmp(excmds[i].name, cmd)) {
541 excmds[i].ec(ln);
542 break;
545 lbuf_undomark(xb);
548 /* ex main loop */
549 void ex(void)
551 if (xled)
552 term_init();
553 while (!xquit) {
554 char *ln = ex_read(":");
555 if (ln)
556 ex_command(ln);
557 free(ln);
559 if (xled)
560 term_done();