ex: td option for text direction
[neatvi.git] / ex.c
blob7cb5296d4265358dcbe5a934e5c5995fd646431f
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 int xquit; /* exit if set */
15 int xvis; /* visual mode */
16 int xai = 1; /* autoindent option */
17 int xic = 1; /* ignorecase option */
18 struct lbuf *xb; /* current buffer */
19 int xrow, xcol, xtop; /* current row, column, and top row */
20 int xrow_alt; /* alternate row, column, and top row */
21 int xled = 1; /* use the line editor */
22 int xdir = +1; /* current direction context */
24 /* read ex command location */
25 static char *ex_loc(char *s, char *loc)
27 while (*s == ':' || isspace((unsigned char) *s))
28 s++;
29 while (*s && !isalpha((unsigned char) *s) && *s != '=') {
30 if (*s == '\'')
31 *loc++ = *s++;
32 if (*s == '/' || *s == '?') {
33 int d = *s;
34 *loc++ = *s++;
35 while (*s && *s != d) {
36 if (*s == '\\' && s[1])
37 *loc++ = *s++;
38 *loc++ = *s++;
41 *loc++ = *s++;
43 *loc = '\0';
44 return s;
47 /* read ex command name */
48 static char *ex_cmd(char *s, char *cmd)
50 char *cmd0 = cmd;
51 s = ex_loc(s, cmd);
52 while (isspace((unsigned char) *s))
53 s++;
54 while (isalpha((unsigned char) *s) || *s == '=' || *s == '!')
55 if ((*cmd++ = *s++) == 'k' && cmd == cmd0 + 1)
56 break;
57 *cmd = '\0';
58 return s;
61 /* read ex command argument */
62 static char *ex_arg(char *s, char *arg)
64 s = ex_cmd(s, arg);
65 while (isspace((unsigned char) *s))
66 s++;
67 while (*s && !isspace((unsigned char) *s))
68 *arg++ = *s++;
69 *arg = '\0';
70 return s;
73 static int ex_search(char *pat)
75 struct sbuf *kw;
76 int dir = *pat == '/' ? 1 : -1;
77 char *b = pat;
78 char *e = b;
79 char *re_kw[1];
80 int i = xrow;
81 struct rset *re;
82 kw = sbuf_make();
83 while (*++e) {
84 if (*e == *pat)
85 break;
86 sbuf_chr(kw, (unsigned char) *e);
87 if (*e == '\\' && e[1])
88 e++;
90 re_kw[0] = sbuf_buf(kw);
91 re = rset_make(1, re_kw, xic ? RE_ICASE : 0);
92 sbuf_free(kw);
93 if (!re)
94 return i;
95 while (i >= 0 && i < lbuf_len(xb)) {
96 if (rset_find(re, lbuf_get(xb, i), 0, NULL, 0) >= 0)
97 break;
98 i += dir;
100 rset_free(re);
101 return i;
104 static int ex_lineno(char *num)
106 int n = xrow;
107 if (!num[0] || num[0] == '.')
108 n = xrow;
109 if (isdigit(num[0]))
110 n = atoi(num) - 1;
111 if (num[0] == '$')
112 n = lbuf_len(xb) - 1;
113 if (num[0] == '-')
114 n = xrow - (num[1] ? ex_lineno(num + 1) : 1);
115 if (num[0] == '+')
116 n = xrow + (num[1] ? ex_lineno(num + 1) : 1);
117 if (num[0] == '\'')
118 n = lbuf_markpos(xb, num[1]);
119 if (num[0] == '/' && num[1])
120 n = ex_search(num);
121 if (num[0] == '?' && num[1])
122 n = ex_search(num);
123 return n;
126 /* parse ex command location */
127 static int ex_region(char *loc, int *beg, int *end)
129 int naddr = 0;
130 if (!strcmp("%", loc)) {
131 *beg = 0;
132 *end = MAX(0, lbuf_len(xb));
133 return 0;
135 if (!*loc) {
136 *beg = xrow;
137 *end = xrow == lbuf_len(xb) ? xrow : xrow + 1;
138 return 0;
140 while (*loc) {
141 int end0 = *end;
142 *end = ex_lineno(loc) + 1;
143 *beg = naddr++ ? end0 - 1 : *end - 1;
144 if (!naddr++)
145 *beg = *end - 1;
146 while (*loc && *loc != ';' && *loc != ',')
147 loc++;
148 if (!*loc)
149 break;
150 if (*loc == ';')
151 xrow = *end - 1;
152 loc++;
154 if (*beg < 0 || *beg >= lbuf_len(xb))
155 return 1;
156 if (*end < *beg || *end > lbuf_len(xb))
157 return 1;
158 return 0;
161 static void ec_quit(char *ec)
163 xquit = 1;
166 static void ec_edit(char *ec)
168 char msg[128];
169 char arg[EXLEN];
170 int fd;
171 ex_arg(ec, arg);
172 if (!strcmp(arg, "%") || !arg[0]) {
173 strcpy(arg, xpath);
174 } else if (!strcmp(arg, "#")) {
175 char xpath_tmp[PATHLEN];
176 int xrow_tmp = xrow;
177 strcpy(xpath_tmp, xpath_alt);
178 strcpy(xpath_alt, xpath);
179 strcpy(xpath, xpath_tmp);
180 xrow = xrow_alt;
181 xrow_alt = xrow_tmp;
182 xcol = 0;
183 xtop = 0;
184 } else {
185 strcpy(xpath_alt, xpath);
186 snprintf(xpath, PATHLEN, "%s", arg);
187 xrow_alt = xrow;
188 xrow = xvis ? 0 : 1 << 20;
190 fd = open(xpath, O_RDONLY);
191 lbuf_rm(xb, 0, lbuf_len(xb));
192 if (fd >= 0) {
193 lbuf_rd(xb, fd, 0);
194 close(fd);
195 snprintf(msg, sizeof(msg), "\"%s\" %d lines [r]\n",
196 xpath, lbuf_len(xb));
197 ex_show(msg);
199 xrow = MAX(0, MIN(xrow, lbuf_len(xb) - 1));
200 lbuf_undofree(xb);
203 static void ec_read(char *ec)
205 char arg[EXLEN], loc[EXLEN];
206 char msg[128];
207 char *path;
208 int fd;
209 int beg, end;
210 int n = lbuf_len(xb);
211 ex_arg(ec, arg);
212 ex_loc(ec, loc);
213 path = arg[0] ? arg : xpath;
214 fd = open(path, O_RDONLY);
215 if (fd >= 0 && !ex_region(loc, &beg, &end)) {
216 lbuf_rd(xb, fd, lbuf_len(xb) ? end : 0);
217 close(fd);
218 xrow = end + lbuf_len(xb) - n;
219 snprintf(msg, sizeof(msg), "\"%s\" %d lines [r]\n",
220 path, lbuf_len(xb) - n);
221 ex_show(msg);
225 static void ec_write(char *ec)
227 char cmd[EXLEN], arg[EXLEN], loc[EXLEN];
228 char msg[128];
229 char *path;
230 int beg, end;
231 int fd;
232 ex_cmd(ec, cmd);
233 ex_arg(ec, arg);
234 ex_loc(ec, loc);
235 path = arg[0] ? arg : xpath;
236 if (ex_region(loc, &beg, &end))
237 return;
238 if (!loc[0]) {
239 beg = 0;
240 end = lbuf_len(xb);
242 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
243 if (fd >= 0) {
244 lbuf_wr(xb, fd, beg, end);
245 close(fd);
246 snprintf(msg, sizeof(msg), "\"%s\" %d lines [w]\n",
247 path, end - beg);
248 ex_show(msg);
250 if (!strcmp("wq", cmd))
251 ec_quit("wq");
254 static void ec_insert(char *ec)
256 char arg[EXLEN], cmd[EXLEN], loc[EXLEN];
257 struct sbuf *sb;
258 char *s;
259 int beg, end;
260 int n;
261 ex_arg(ec, arg);
262 ex_cmd(ec, cmd);
263 ex_loc(ec, loc);
264 if (ex_region(loc, &beg, &end) && (beg != 0 || end != 0))
265 return;
266 if (cmd[0] == 'c') {
267 if (lbuf_len(xb))
268 lbuf_rm(xb, beg, end);
269 end = beg + 1;
271 sb = sbuf_make();
272 while ((s = ex_read(""))) {
273 if (!strcmp(".", s)) {
274 free(s);
275 break;
277 sbuf_str(sb, s);
278 sbuf_chr(sb, '\n');
279 free(s);
281 if (cmd[0] == 'a')
282 if (end > lbuf_len(xb))
283 end = lbuf_len(xb);
284 n = lbuf_len(xb);
285 lbuf_put(xb, end, sbuf_buf(sb));
286 xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
287 sbuf_free(sb);
290 static void ec_print(char *ec)
292 char cmd[EXLEN], loc[EXLEN];
293 int beg, end;
294 int i;
295 ex_cmd(ec, cmd);
296 ex_loc(ec, loc);
297 if (!cmd[0] && !loc[0]) {
298 if (xrow >= lbuf_len(xb) - 1)
299 return;
300 xrow = xrow + 1;
302 if (!ex_region(loc, &beg, &end)) {
303 for (i = beg; i < end; i++)
304 ex_show(lbuf_get(xb, i));
305 xrow = end;
309 static void ex_yank(int reg, int beg, int end)
311 char *buf = lbuf_cp(xb, beg, end);
312 reg_put(reg, buf, 1);
313 free(buf);
316 static void ec_delete(char *ec)
318 char loc[EXLEN];
319 char arg[EXLEN];
320 int beg, end;
321 ex_loc(ec, loc);
322 ex_arg(ec, arg);
323 if (!ex_region(loc, &beg, &end) && lbuf_len(xb)) {
324 ex_yank(arg[0], beg, end);
325 lbuf_rm(xb, beg, end);
326 xrow = beg;
330 static void ec_yank(char *ec)
332 char loc[EXLEN];
333 char arg[EXLEN];
334 int beg, end;
335 ex_loc(ec, loc);
336 ex_arg(ec, arg);
337 if (!ex_region(loc, &beg, &end) && lbuf_len(xb))
338 ex_yank(arg[0], beg, end);
341 static void ec_put(char *ec)
343 char loc[EXLEN];
344 char arg[EXLEN];
345 int beg, end;
346 int lnmode;
347 char *buf;
348 int n = lbuf_len(xb);
349 ex_loc(ec, loc);
350 ex_arg(ec, arg);
351 buf = reg_get(arg[0], &lnmode);
352 if (buf && !ex_region(loc, &beg, &end)) {
353 lbuf_put(xb, end, buf);
354 xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
358 static void ec_lnum(char *ec)
360 char loc[EXLEN];
361 char msg[128];
362 int beg, end;
363 ex_loc(ec, loc);
364 if (ex_region(loc, &beg, &end))
365 return;
366 sprintf(msg, "%d\n", end);
367 ex_show(msg);
370 static void ec_undo(char *ec)
372 lbuf_undo(xb);
375 static void ec_redo(char *ec)
377 lbuf_redo(xb);
380 static void ec_mark(char *ec)
382 char loc[EXLEN], arg[EXLEN];
383 int beg, end;
384 ex_arg(ec, arg);
385 ex_loc(ec, loc);
386 if (ex_region(loc, &beg, &end))
387 return;
388 lbuf_mark(xb, arg[0], end - 1);
391 static char *readuntil(char **src, int delim)
393 struct sbuf *sbuf = sbuf_make();
394 char *s = *src;
395 /* reading the pattern */
396 while (*s && *s != delim) {
397 if (s[0] == '\\' && s[1])
398 sbuf_chr(sbuf, (unsigned char) *s++);
399 sbuf_chr(sbuf, (unsigned char) *s++);
401 if (*s) /* skipping the delimiter */
402 s++;
403 *src = s;
404 return sbuf_done(sbuf);
407 static void ec_substitute(char *ec)
409 char loc[EXLEN], arg[EXLEN];
410 struct rset *re;
411 int offs[32];
412 int beg, end;
413 char *pat, *rep;
414 char *s = arg;
415 int delim;
416 int i;
417 ex_arg(ec, arg);
418 ex_loc(ec, loc);
419 if (ex_region(loc, &beg, &end))
420 return;
421 delim = (unsigned char) *s++;
422 pat = readuntil(&s, delim);
423 rep = readuntil(&s, delim);
424 re = rset_make(1, &pat, xic ? RE_ICASE : 0);
425 for (i = beg; i < end; i++) {
426 char *ln = lbuf_get(xb, i);
427 if (rset_find(re, ln, LEN(offs) / 2, offs, 0)) {
428 struct sbuf *r = sbuf_make();
429 sbuf_mem(r, ln, offs[0]);
430 sbuf_str(r, rep);
431 sbuf_str(r, ln + offs[1]);
432 lbuf_put(xb, i, sbuf_buf(r));
433 lbuf_rm(xb, i + 1, i + 2);
434 sbuf_free(r);
437 rset_free(re);
438 free(pat);
439 free(rep);
442 static struct option {
443 char *abbr;
444 char *name;
445 int *var;
446 } options[] = {
447 {"ai", "autoindent", &xai},
448 {"ic", "ignorecase", &xic},
449 {"td", "textdirection", &xdir},
452 static char *cutword(char *s, char *d)
454 while (isspace(*s))
455 s++;
456 while (*s && !isspace(*s))
457 *d++ = *s++;
458 while (isspace(*s))
459 s++;
460 *d = '\0';
461 return s;
464 static void ec_set(char *ec)
466 char arg[EXLEN];
467 char tok[EXLEN];
468 char opt[EXLEN];
469 char *s = arg;
470 int val = 0;
471 int i;
472 ex_arg(ec, arg);
473 if (*s) {
474 s = cutword(s, tok);
475 if (tok[0] == 'n' && tok[1] == 'o') {
476 strcpy(opt, tok + 2);
477 val = 0;
478 } else {
479 char *r = strchr(tok, '=');
480 if (r) {
481 *r = '\0';
482 strcpy(opt, tok);
483 val = atoi(r + 1);
484 } else {
485 strcpy(opt, tok);
486 val = 1;
489 for (i = 0; i < LEN(options); i++) {
490 struct option *o = &options[i];
491 if (!strcmp(o->abbr, opt) || !strcmp(o->name, opt))
492 *o->var = val;
497 static struct excmd {
498 char *abbr;
499 char *name;
500 void (*ec)(char *s);
501 } excmds[] = {
502 {"p", "print", ec_print},
503 {"a", "append", ec_insert},
504 {"i", "insert", ec_insert},
505 {"d", "delete", ec_delete},
506 {"c", "change", ec_insert},
507 {"e", "edit", ec_edit},
508 {"=", "=", ec_lnum},
509 {"k", "mark", ec_mark},
510 {"pu", "put", ec_put},
511 {"q", "quit", ec_quit},
512 {"r", "read", ec_read},
513 {"w", "write", ec_write},
514 {"wq", "wq", ec_write},
515 {"u", "undo", ec_undo},
516 {"r", "redo", ec_redo},
517 {"se", "set", ec_set},
518 {"s", "substitute", ec_substitute},
519 {"ya", "yank", ec_yank},
520 {"", "", ec_print},
523 /* execute a single ex command */
524 void ex_command(char *ln)
526 char cmd[EXLEN];
527 int i;
528 ex_cmd(ln, cmd);
529 for (i = 0; i < LEN(excmds); i++) {
530 if (!strcmp(excmds[i].abbr, cmd) || !strcmp(excmds[i].name, cmd)) {
531 excmds[i].ec(ln);
532 break;
535 lbuf_undomark(xb);
538 /* ex main loop */
539 void ex(void)
541 if (xled)
542 term_init();
543 while (!xquit) {
544 char *ln = ex_read(":");
545 if (ln)
546 ex_command(ln);
547 free(ln);
549 if (xled)
550 term_done();