vi: move xoff before EOL after motions
[neatvi.git] / ex.c
blob6e5a4da9e4f137accbe1b41bb36a177bb5d24832
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 (!strcmp(arg, "%") || !arg[0]) {
176 strcpy(arg, xpath);
177 } else if (!strcmp(arg, "#")) {
178 char xpath_tmp[PATHLEN];
179 int xrow_tmp = xrow;
180 strcpy(xpath_tmp, xpath_alt);
181 strcpy(xpath_alt, xpath);
182 strcpy(xpath, xpath_tmp);
183 xrow = xrow_alt;
184 xrow_alt = xrow_tmp;
185 xoff = 0;
186 xtop = 0;
187 } else {
188 strcpy(xpath_alt, xpath);
189 snprintf(xpath, PATHLEN, "%s", arg);
190 xrow_alt = xrow;
191 xrow = xvis ? 0 : 1 << 20;
193 strcpy(xft, syn_filetype(xpath));
194 fd = open(xpath, O_RDONLY);
195 lbuf_rm(xb, 0, lbuf_len(xb));
196 if (fd >= 0) {
197 lbuf_rd(xb, fd, 0);
198 close(fd);
199 snprintf(msg, sizeof(msg), "\"%s\" %d lines [r]\n",
200 xpath, lbuf_len(xb));
201 ex_show(msg);
203 xrow = MAX(0, MIN(xrow, lbuf_len(xb) - 1));
204 lbuf_undofree(xb);
207 static void ec_read(char *ec)
209 char arg[EXLEN], loc[EXLEN];
210 char msg[128];
211 char *path;
212 int fd;
213 int beg, end;
214 int n = lbuf_len(xb);
215 ex_arg(ec, arg);
216 ex_loc(ec, loc);
217 path = arg[0] ? arg : xpath;
218 fd = open(path, O_RDONLY);
219 if (fd >= 0 && !ex_region(loc, &beg, &end)) {
220 lbuf_rd(xb, fd, lbuf_len(xb) ? end : 0);
221 close(fd);
222 xrow = end + lbuf_len(xb) - n;
223 snprintf(msg, sizeof(msg), "\"%s\" %d lines [r]\n",
224 path, lbuf_len(xb) - n);
225 ex_show(msg);
229 static void ec_write(char *ec)
231 char cmd[EXLEN], arg[EXLEN], loc[EXLEN];
232 char msg[128];
233 char *path;
234 int beg, end;
235 int fd;
236 ex_cmd(ec, cmd);
237 ex_arg(ec, arg);
238 ex_loc(ec, loc);
239 path = arg[0] ? arg : xpath;
240 if (ex_region(loc, &beg, &end))
241 return;
242 if (!loc[0]) {
243 beg = 0;
244 end = lbuf_len(xb);
246 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
247 if (fd >= 0) {
248 lbuf_wr(xb, fd, beg, end);
249 close(fd);
250 snprintf(msg, sizeof(msg), "\"%s\" %d lines [w]\n",
251 path, end - beg);
252 ex_show(msg);
254 if (!strcmp("wq", cmd))
255 ec_quit("wq");
258 static void ec_insert(char *ec)
260 char arg[EXLEN], cmd[EXLEN], loc[EXLEN];
261 struct sbuf *sb;
262 char *s;
263 int beg, end;
264 int n;
265 ex_arg(ec, arg);
266 ex_cmd(ec, cmd);
267 ex_loc(ec, loc);
268 if (ex_region(loc, &beg, &end) && (beg != 0 || end != 0))
269 return;
270 if (cmd[0] == 'c') {
271 if (lbuf_len(xb))
272 lbuf_rm(xb, beg, end);
273 end = beg + 1;
275 sb = sbuf_make();
276 while ((s = ex_read(""))) {
277 if (!strcmp(".", s)) {
278 free(s);
279 break;
281 sbuf_str(sb, s);
282 sbuf_chr(sb, '\n');
283 free(s);
285 if (cmd[0] == 'a')
286 if (end > lbuf_len(xb))
287 end = lbuf_len(xb);
288 n = lbuf_len(xb);
289 lbuf_put(xb, end, sbuf_buf(sb));
290 xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
291 sbuf_free(sb);
294 static void ec_print(char *ec)
296 char cmd[EXLEN], loc[EXLEN];
297 int beg, end;
298 int i;
299 ex_cmd(ec, cmd);
300 ex_loc(ec, loc);
301 if (!cmd[0] && !loc[0]) {
302 if (xrow >= lbuf_len(xb) - 1)
303 return;
304 xrow = xrow + 1;
306 if (!ex_region(loc, &beg, &end)) {
307 for (i = beg; i < end; i++)
308 ex_show(lbuf_get(xb, i));
309 xrow = end;
313 static void ex_yank(int reg, int beg, int end)
315 char *buf = lbuf_cp(xb, beg, end);
316 reg_put(reg, buf, 1);
317 free(buf);
320 static void ec_delete(char *ec)
322 char loc[EXLEN];
323 char arg[EXLEN];
324 int beg, end;
325 ex_loc(ec, loc);
326 ex_arg(ec, arg);
327 if (!ex_region(loc, &beg, &end) && lbuf_len(xb)) {
328 ex_yank(arg[0], beg, end);
329 lbuf_rm(xb, beg, end);
330 xrow = beg;
334 static void ec_yank(char *ec)
336 char loc[EXLEN];
337 char arg[EXLEN];
338 int beg, end;
339 ex_loc(ec, loc);
340 ex_arg(ec, arg);
341 if (!ex_region(loc, &beg, &end) && lbuf_len(xb))
342 ex_yank(arg[0], beg, end);
345 static void ec_put(char *ec)
347 char loc[EXLEN];
348 char arg[EXLEN];
349 int beg, end;
350 int lnmode;
351 char *buf;
352 int n = lbuf_len(xb);
353 ex_loc(ec, loc);
354 ex_arg(ec, arg);
355 buf = reg_get(arg[0], &lnmode);
356 if (buf && !ex_region(loc, &beg, &end)) {
357 lbuf_put(xb, end, buf);
358 xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
362 static void ec_lnum(char *ec)
364 char loc[EXLEN];
365 char msg[128];
366 int beg, end;
367 ex_loc(ec, loc);
368 if (ex_region(loc, &beg, &end))
369 return;
370 sprintf(msg, "%d\n", end);
371 ex_show(msg);
374 static void ec_undo(char *ec)
376 lbuf_undo(xb);
379 static void ec_redo(char *ec)
381 lbuf_redo(xb);
384 static void ec_mark(char *ec)
386 char loc[EXLEN], arg[EXLEN];
387 int beg, end;
388 ex_arg(ec, arg);
389 ex_loc(ec, loc);
390 if (ex_region(loc, &beg, &end))
391 return;
392 lbuf_mark(xb, arg[0], end - 1);
395 static char *readuntil(char **src, int delim)
397 struct sbuf *sbuf = sbuf_make();
398 char *s = *src;
399 /* reading the pattern */
400 while (*s && *s != delim) {
401 if (s[0] == '\\' && s[1])
402 sbuf_chr(sbuf, (unsigned char) *s++);
403 sbuf_chr(sbuf, (unsigned char) *s++);
405 if (*s) /* skipping the delimiter */
406 s++;
407 *src = s;
408 return sbuf_done(sbuf);
411 static void ec_substitute(char *ec)
413 char loc[EXLEN], arg[EXLEN];
414 struct rset *re;
415 int offs[32];
416 int beg, end;
417 char *pat, *rep;
418 char *s = arg;
419 int delim;
420 int i;
421 ex_arg(ec, arg);
422 ex_loc(ec, loc);
423 if (ex_region(loc, &beg, &end))
424 return;
425 delim = (unsigned char) *s++;
426 pat = readuntil(&s, delim);
427 rep = readuntil(&s, delim);
428 re = rset_make(1, &pat, xic ? RE_ICASE : 0);
429 for (i = beg; i < end; i++) {
430 char *ln = lbuf_get(xb, i);
431 if (rset_find(re, ln, LEN(offs) / 2, offs, 0)) {
432 struct sbuf *r = sbuf_make();
433 sbuf_mem(r, ln, offs[0]);
434 sbuf_str(r, rep);
435 sbuf_str(r, ln + offs[1]);
436 lbuf_put(xb, i, sbuf_buf(r));
437 lbuf_rm(xb, i + 1, i + 2);
438 sbuf_free(r);
441 rset_free(re);
442 free(pat);
443 free(rep);
446 static struct option {
447 char *abbr;
448 char *name;
449 int *var;
450 } options[] = {
451 {"ai", "autoindent", &xai},
452 {"ic", "ignorecase", &xic},
453 {"td", "textdirection", &xdir},
454 {"shape", "shape", &xshape},
455 {"order", "xorder", &xorder},
458 static char *cutword(char *s, char *d)
460 while (isspace(*s))
461 s++;
462 while (*s && !isspace(*s))
463 *d++ = *s++;
464 while (isspace(*s))
465 s++;
466 *d = '\0';
467 return s;
470 static void ec_set(char *ec)
472 char arg[EXLEN];
473 char tok[EXLEN];
474 char opt[EXLEN];
475 char *s = arg;
476 int val = 0;
477 int i;
478 ex_arg(ec, arg);
479 if (*s) {
480 s = cutword(s, tok);
481 if (tok[0] == 'n' && tok[1] == 'o') {
482 strcpy(opt, tok + 2);
483 val = 0;
484 } else {
485 char *r = strchr(tok, '=');
486 if (r) {
487 *r = '\0';
488 strcpy(opt, tok);
489 val = atoi(r + 1);
490 } else {
491 strcpy(opt, tok);
492 val = 1;
495 for (i = 0; i < LEN(options); i++) {
496 struct option *o = &options[i];
497 if (!strcmp(o->abbr, opt) || !strcmp(o->name, opt))
498 *o->var = val;
503 static struct excmd {
504 char *abbr;
505 char *name;
506 void (*ec)(char *s);
507 } excmds[] = {
508 {"p", "print", ec_print},
509 {"a", "append", ec_insert},
510 {"i", "insert", ec_insert},
511 {"d", "delete", ec_delete},
512 {"c", "change", ec_insert},
513 {"e", "edit", ec_edit},
514 {"=", "=", ec_lnum},
515 {"k", "mark", ec_mark},
516 {"pu", "put", ec_put},
517 {"q", "quit", ec_quit},
518 {"r", "read", ec_read},
519 {"w", "write", ec_write},
520 {"wq", "wq", ec_write},
521 {"u", "undo", ec_undo},
522 {"r", "redo", ec_redo},
523 {"se", "set", ec_set},
524 {"s", "substitute", ec_substitute},
525 {"ya", "yank", ec_yank},
526 {"", "", ec_print},
529 /* execute a single ex command */
530 void ex_command(char *ln)
532 char cmd[EXLEN];
533 int i;
534 ex_cmd(ln, cmd);
535 for (i = 0; i < LEN(excmds); i++) {
536 if (!strcmp(excmds[i].abbr, cmd) || !strcmp(excmds[i].name, cmd)) {
537 excmds[i].ec(ln);
538 break;
541 lbuf_undomark(xb);
544 /* ex main loop */
545 void ex(void)
547 if (xled)
548 term_init();
549 while (!xquit) {
550 char *ln = ex_read(":");
551 if (ln)
552 ex_command(ln);
553 free(ln);
555 if (xled)
556 term_done();