lbuf: report buffer modification
[neatvi.git] / ex.c
blob00bde0859bd1952b4edaabf61578dcd6c3601ef1
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 lbuf_jump(xb, num[1], &n, NULL);
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_saved(xb, 1);
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);
257 if (!strcmp(xpath, path))
258 lbuf_saved(xb, 0);
260 if (!strcmp("wq", cmd))
261 ec_quit("wq");
264 static void ec_insert(char *ec)
266 char arg[EXLEN], cmd[EXLEN], loc[EXLEN];
267 struct sbuf *sb;
268 char *s;
269 int beg, end;
270 int n;
271 ex_arg(ec, arg);
272 ex_cmd(ec, cmd);
273 ex_loc(ec, loc);
274 if (ex_region(loc, &beg, &end) && (beg != 0 || end != 0))
275 return;
276 if (cmd[0] == 'c') {
277 if (lbuf_len(xb))
278 lbuf_rm(xb, beg, end);
279 end = beg + 1;
281 sb = sbuf_make();
282 while ((s = ex_read(""))) {
283 if (!strcmp(".", s)) {
284 free(s);
285 break;
287 sbuf_str(sb, s);
288 sbuf_chr(sb, '\n');
289 free(s);
291 if (cmd[0] == 'a')
292 if (end > lbuf_len(xb))
293 end = lbuf_len(xb);
294 n = lbuf_len(xb);
295 lbuf_put(xb, end, sbuf_buf(sb));
296 xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
297 sbuf_free(sb);
300 static void ec_print(char *ec)
302 char cmd[EXLEN], loc[EXLEN];
303 int beg, end;
304 int i;
305 ex_cmd(ec, cmd);
306 ex_loc(ec, loc);
307 if (!cmd[0] && !loc[0]) {
308 if (xrow >= lbuf_len(xb) - 1)
309 return;
310 xrow = xrow + 1;
312 if (!ex_region(loc, &beg, &end)) {
313 for (i = beg; i < end; i++)
314 ex_show(lbuf_get(xb, i));
315 xrow = end;
319 static void ex_yank(int reg, int beg, int end)
321 char *buf = lbuf_cp(xb, beg, end);
322 reg_put(reg, buf, 1);
323 free(buf);
326 static void ec_delete(char *ec)
328 char loc[EXLEN];
329 char arg[EXLEN];
330 int beg, end;
331 ex_loc(ec, loc);
332 ex_arg(ec, arg);
333 if (!ex_region(loc, &beg, &end) && lbuf_len(xb)) {
334 ex_yank(arg[0], beg, end);
335 lbuf_rm(xb, beg, end);
336 xrow = beg;
340 static void ec_yank(char *ec)
342 char loc[EXLEN];
343 char arg[EXLEN];
344 int beg, end;
345 ex_loc(ec, loc);
346 ex_arg(ec, arg);
347 if (!ex_region(loc, &beg, &end) && lbuf_len(xb))
348 ex_yank(arg[0], beg, end);
351 static void ec_put(char *ec)
353 char loc[EXLEN];
354 char arg[EXLEN];
355 int beg, end;
356 int lnmode;
357 char *buf;
358 int n = lbuf_len(xb);
359 ex_loc(ec, loc);
360 ex_arg(ec, arg);
361 buf = reg_get(arg[0], &lnmode);
362 if (buf && !ex_region(loc, &beg, &end)) {
363 lbuf_put(xb, end, buf);
364 xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
368 static void ec_lnum(char *ec)
370 char loc[EXLEN];
371 char msg[128];
372 int beg, end;
373 ex_loc(ec, loc);
374 if (ex_region(loc, &beg, &end))
375 return;
376 sprintf(msg, "%d\n", end);
377 ex_show(msg);
380 static void ec_undo(char *ec)
382 lbuf_undo(xb);
385 static void ec_redo(char *ec)
387 lbuf_redo(xb);
390 static void ec_mark(char *ec)
392 char loc[EXLEN], arg[EXLEN];
393 int beg, end;
394 ex_arg(ec, arg);
395 ex_loc(ec, loc);
396 if (ex_region(loc, &beg, &end))
397 return;
398 lbuf_mark(xb, arg[0], end - 1, 0);
401 static char *readuntil(char **src, int delim)
403 struct sbuf *sbuf = sbuf_make();
404 char *s = *src;
405 /* reading the pattern */
406 while (*s && *s != delim) {
407 if (s[0] == '\\' && s[1])
408 sbuf_chr(sbuf, (unsigned char) *s++);
409 sbuf_chr(sbuf, (unsigned char) *s++);
411 if (*s) /* skipping the delimiter */
412 s++;
413 *src = s;
414 return sbuf_done(sbuf);
417 static void ec_substitute(char *ec)
419 char loc[EXLEN], arg[EXLEN];
420 struct rset *re;
421 int offs[32];
422 int beg, end;
423 char *pat, *rep;
424 char *s = arg;
425 int delim;
426 int i;
427 ex_arg(ec, arg);
428 ex_loc(ec, loc);
429 if (ex_region(loc, &beg, &end))
430 return;
431 delim = (unsigned char) *s++;
432 pat = readuntil(&s, delim);
433 rep = readuntil(&s, delim);
434 re = rset_make(1, &pat, xic ? RE_ICASE : 0);
435 for (i = beg; i < end; i++) {
436 char *ln = lbuf_get(xb, i);
437 if (rset_find(re, ln, LEN(offs) / 2, offs, 0)) {
438 struct sbuf *r = sbuf_make();
439 sbuf_mem(r, ln, offs[0]);
440 sbuf_str(r, rep);
441 sbuf_str(r, ln + offs[1]);
442 lbuf_put(xb, i, sbuf_buf(r));
443 lbuf_rm(xb, i + 1, i + 2);
444 sbuf_free(r);
447 rset_free(re);
448 free(pat);
449 free(rep);
452 static struct option {
453 char *abbr;
454 char *name;
455 int *var;
456 } options[] = {
457 {"ai", "autoindent", &xai},
458 {"ic", "ignorecase", &xic},
459 {"td", "textdirection", &xdir},
460 {"shape", "shape", &xshape},
461 {"order", "xorder", &xorder},
464 static char *cutword(char *s, char *d)
466 while (isspace(*s))
467 s++;
468 while (*s && !isspace(*s))
469 *d++ = *s++;
470 while (isspace(*s))
471 s++;
472 *d = '\0';
473 return s;
476 static void ec_set(char *ec)
478 char arg[EXLEN];
479 char tok[EXLEN];
480 char opt[EXLEN];
481 char *s = arg;
482 int val = 0;
483 int i;
484 ex_arg(ec, arg);
485 if (*s) {
486 s = cutword(s, tok);
487 if (tok[0] == 'n' && tok[1] == 'o') {
488 strcpy(opt, tok + 2);
489 val = 0;
490 } else {
491 char *r = strchr(tok, '=');
492 if (r) {
493 *r = '\0';
494 strcpy(opt, tok);
495 val = atoi(r + 1);
496 } else {
497 strcpy(opt, tok);
498 val = 1;
501 for (i = 0; i < LEN(options); i++) {
502 struct option *o = &options[i];
503 if (!strcmp(o->abbr, opt) || !strcmp(o->name, opt))
504 *o->var = val;
509 static struct excmd {
510 char *abbr;
511 char *name;
512 void (*ec)(char *s);
513 } excmds[] = {
514 {"p", "print", ec_print},
515 {"a", "append", ec_insert},
516 {"i", "insert", ec_insert},
517 {"d", "delete", ec_delete},
518 {"c", "change", ec_insert},
519 {"e", "edit", ec_edit},
520 {"=", "=", ec_lnum},
521 {"k", "mark", ec_mark},
522 {"pu", "put", ec_put},
523 {"q", "quit", ec_quit},
524 {"r", "read", ec_read},
525 {"w", "write", ec_write},
526 {"wq", "wq", ec_write},
527 {"u", "undo", ec_undo},
528 {"r", "redo", ec_redo},
529 {"se", "set", ec_set},
530 {"s", "substitute", ec_substitute},
531 {"ya", "yank", ec_yank},
532 {"", "", ec_print},
535 /* execute a single ex command */
536 void ex_command(char *ln)
538 char cmd[EXLEN];
539 int i;
540 ex_cmd(ln, cmd);
541 for (i = 0; i < LEN(excmds); i++) {
542 if (!strcmp(excmds[i].abbr, cmd) || !strcmp(excmds[i].name, cmd)) {
543 excmds[i].ec(ln);
544 break;
547 lbuf_modified(xb);
550 /* ex main loop */
551 void ex(void)
553 if (xled)
554 term_init();
555 while (!xquit) {
556 char *ln = ex_read(":");
557 if (ln)
558 ex_command(ln);
559 free(ln);
561 if (xled)
562 term_done();