ex: edit command for non-existent files
[neatvi.git] / ex.c
blobbe7e2947125e211ac035d145e0b8f3cabedf2128
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 /* read an input line; ex's input function */
13 static char *ex_read(char *msg)
15 struct sbuf *sb;
16 char c;
17 if (xled) {
18 char *s = led_prompt(msg, "");
19 if (s)
20 printf("\n");
21 return s;
23 sb = sbuf_make();
24 while ((c = getchar()) != EOF) {
25 if (c == '\n')
26 break;
27 sbuf_chr(sb, c);
29 if (c == EOF) {
30 sbuf_free(sb);
31 return NULL;
33 return sbuf_done(sb);
36 /* print an output line; ex's output function */
37 static void ex_show(char *msg)
39 if (xled) {
40 led_print(msg, -1);
41 term_chr('\n');
42 } else {
43 printf("%s", msg);
47 /* read ex command location */
48 static char *ex_loc(char *s, char *loc)
50 while (*s == ':' || isspace((unsigned char) *s))
51 s++;
52 while (*s && !isalpha((unsigned char) *s) && *s != '=') {
53 if (*s == '\'')
54 *loc++ = *s++;
55 if (*s == '/' || *s == '?') {
56 int d = *s;
57 *loc++ = *s++;
58 while (*s && *s != d) {
59 if (*s == '\\' && s[1])
60 *loc++ = *s++;
61 *loc++ = *s++;
64 *loc++ = *s++;
66 *loc = '\0';
67 return s;
70 /* read ex command name */
71 static char *ex_cmd(char *s, char *cmd)
73 char *cmd0 = cmd;
74 s = ex_loc(s, cmd);
75 while (isspace((unsigned char) *s))
76 s++;
77 while (isalpha((unsigned char) *s) || *s == '=' || *s == '!')
78 if ((*cmd++ = *s++) == 'k' && cmd == cmd0 + 1)
79 break;
80 *cmd = '\0';
81 return s;
84 /* read ex command argument */
85 static char *ex_arg(char *s, char *arg)
87 s = ex_cmd(s, arg);
88 while (isspace((unsigned char) *s))
89 s++;
90 while (*s && !isspace((unsigned char) *s))
91 *arg++ = *s++;
92 *arg = '\0';
93 return s;
96 static int ex_search(char *pat)
98 struct sbuf *kw;
99 int dir = *pat == '/' ? 1 : -1;
100 char *b = pat;
101 char *e = b;
102 char *re_kw[1];
103 int i = xrow;
104 struct rset *re;
105 kw = sbuf_make();
106 while (*++e) {
107 if (*e == *pat)
108 break;
109 sbuf_chr(kw, (unsigned char) *e);
110 if (*e == '\\' && e[1])
111 e++;
113 re_kw[0] = sbuf_buf(kw);
114 re = rset_make(1, re_kw, 0);
115 sbuf_free(kw);
116 if (!re)
117 return i;
118 while (i >= 0 && i < lbuf_len(xb)) {
119 if (rset_find(re, lbuf_get(xb, i), 0, NULL, 0) >= 0)
120 break;
121 i += dir;
123 rset_free(re);
124 return i;
127 static int ex_lineno(char *num)
129 int n = xrow;
130 if (!num[0] || num[0] == '.')
131 n = xrow;
132 if (isdigit(num[0]))
133 n = atoi(num) - 1;
134 if (num[0] == '$')
135 n = lbuf_len(xb) - 1;
136 if (num[0] == '-')
137 n = xrow - (num[1] ? ex_lineno(num + 1) : 1);
138 if (num[0] == '+')
139 n = xrow + (num[1] ? ex_lineno(num + 1) : 1);
140 if (num[0] == '\'')
141 n = lbuf_markpos(xb, num[1]);
142 if (num[0] == '/' && num[1])
143 n = ex_search(num);
144 if (num[0] == '?' && num[1])
145 n = ex_search(num);
146 return n;
149 /* parse ex command location */
150 static int ex_region(char *loc, int *beg, int *end)
152 int naddr = 0;
153 if (!strcmp("%", loc)) {
154 *beg = 0;
155 *end = MAX(0, lbuf_len(xb));
156 return 0;
158 if (!*loc) {
159 *beg = xrow;
160 *end = xrow == lbuf_len(xb) ? xrow : xrow + 1;
161 return 0;
163 while (*loc) {
164 int end0 = *end;
165 *end = ex_lineno(loc) + 1;
166 *beg = naddr++ ? end0 - 1 : *end - 1;
167 if (!naddr++)
168 *beg = *end - 1;
169 while (*loc && *loc != ';' && *loc != ',')
170 loc++;
171 if (!*loc)
172 break;
173 if (*loc == ';')
174 xrow = *end - 1;
175 loc++;
177 if (*beg < 0 || *beg >= lbuf_len(xb))
178 return 1;
179 if (*end < *beg || *end > lbuf_len(xb))
180 return 1;
181 return 0;
184 static void ec_quit(char *ec)
186 xquit = 1;
189 static void ec_edit(char *ec)
191 char arg[EXLEN];
192 int fd;
193 ex_arg(ec, arg);
194 fd = open(arg, O_RDONLY);
195 lbuf_rm(xb, 0, lbuf_len(xb));
196 if (fd >= 0) {
197 lbuf_rd(xb, fd, 0);
198 close(fd);
200 xrow = MAX(0, lbuf_len(xb) - 1);
201 lbuf_undofree(xb);
202 snprintf(xpath, PATHLEN, "%s", arg);
205 static void ec_read(char *ec)
207 char arg[EXLEN], loc[EXLEN];
208 int fd;
209 int beg, end;
210 int n = lbuf_len(xb);
211 ex_arg(ec, arg);
212 ex_loc(ec, loc);
213 fd = open(arg[0] ? arg : xpath, O_RDONLY);
214 if (fd >= 0 && !ex_region(loc, &beg, &end)) {
215 lbuf_rd(xb, fd, lbuf_len(xb) ? end : 0);
216 close(fd);
217 xrow = end + lbuf_len(xb) - n;
221 static void ec_write(char *ec)
223 char cmd[EXLEN], arg[EXLEN], loc[EXLEN];
224 char *path;
225 int beg, end;
226 int fd;
227 ex_cmd(ec, cmd);
228 ex_arg(ec, arg);
229 ex_loc(ec, loc);
230 path = arg[0] ? arg : xpath;
231 if (ex_region(loc, &beg, &end))
232 return;
233 if (!loc[0]) {
234 beg = 0;
235 end = lbuf_len(xb);
237 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
238 if (fd >= 0) {
239 lbuf_wr(xb, fd, beg, end);
240 close(fd);
242 if (!strcmp("wq", cmd))
243 ec_quit("wq");
246 static void ec_insert(char *ec)
248 char arg[EXLEN], cmd[EXLEN], loc[EXLEN];
249 struct sbuf *sb;
250 char *s;
251 int beg, end;
252 int n;
253 ex_arg(ec, arg);
254 ex_cmd(ec, cmd);
255 ex_loc(ec, loc);
256 if (ex_region(loc, &beg, &end) && (beg != 0 || end != 0))
257 return;
258 if (cmd[0] == 'c') {
259 if (lbuf_len(xb))
260 lbuf_rm(xb, beg, end);
261 end = beg + 1;
263 sb = sbuf_make();
264 while ((s = ex_read(""))) {
265 if (!strcmp(".", s)) {
266 free(s);
267 break;
269 sbuf_str(sb, s);
270 sbuf_chr(sb, '\n');
271 free(s);
273 if (cmd[0] == 'a')
274 if (end > lbuf_len(xb))
275 end = lbuf_len(xb);
276 n = lbuf_len(xb);
277 lbuf_put(xb, end, sbuf_buf(sb));
278 xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
279 sbuf_free(sb);
282 static void ec_print(char *ec)
284 char cmd[EXLEN], loc[EXLEN];
285 int beg, end;
286 int i;
287 ex_cmd(ec, cmd);
288 ex_loc(ec, loc);
289 if (!cmd[0] && !loc[0]) {
290 if (xrow >= lbuf_len(xb) - 1)
291 return;
292 xrow = xrow + 1;
294 if (!ex_region(loc, &beg, &end)) {
295 for (i = beg; i < end; i++)
296 ex_show(lbuf_get(xb, i));
297 xrow = end;
301 static void ex_yank(int reg, int beg, int end)
303 char *buf = lbuf_cp(xb, beg, end);
304 reg_put(reg, buf, 1);
305 free(buf);
308 static void ec_delete(char *ec)
310 char loc[EXLEN];
311 char arg[EXLEN];
312 int beg, end;
313 ex_loc(ec, loc);
314 ex_arg(ec, arg);
315 if (!ex_region(loc, &beg, &end) && lbuf_len(xb)) {
316 ex_yank(arg[0], beg, end);
317 lbuf_rm(xb, beg, end);
318 xrow = beg;
322 static void ec_yank(char *ec)
324 char loc[EXLEN];
325 char arg[EXLEN];
326 int beg, end;
327 ex_loc(ec, loc);
328 ex_arg(ec, arg);
329 if (!ex_region(loc, &beg, &end) && lbuf_len(xb))
330 ex_yank(arg[0], beg, end);
333 static void ec_put(char *ec)
335 char loc[EXLEN];
336 char arg[EXLEN];
337 int beg, end;
338 int lnmode;
339 char *buf;
340 int n = lbuf_len(xb);
341 ex_loc(ec, loc);
342 ex_arg(ec, arg);
343 buf = reg_get(arg[0], &lnmode);
344 if (buf && !ex_region(loc, &beg, &end)) {
345 lbuf_put(xb, end, buf);
346 xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
350 static void ec_lnum(char *ec)
352 char loc[EXLEN];
353 char msg[128];
354 int beg, end;
355 ex_loc(ec, loc);
356 if (ex_region(loc, &beg, &end))
357 return;
358 sprintf(msg, "%d\n", end);
359 ex_show(msg);
362 static void ec_undo(char *ec)
364 lbuf_undo(xb);
367 static void ec_redo(char *ec)
369 lbuf_redo(xb);
372 static void ec_mark(char *ec)
374 char loc[EXLEN], arg[EXLEN];
375 int beg, end;
376 ex_arg(ec, arg);
377 ex_loc(ec, loc);
378 if (ex_region(loc, &beg, &end))
379 return;
380 lbuf_mark(xb, arg[0], end - 1);
383 static char *readuntil(char **src, int delim)
385 struct sbuf *sbuf = sbuf_make();
386 char *s = *src;
387 /* reading the pattern */
388 while (*s && *s != delim) {
389 if (s[0] == '\\' && s[1])
390 sbuf_chr(sbuf, (unsigned char) *s++);
391 sbuf_chr(sbuf, (unsigned char) *s++);
393 if (*s) /* skipping the delimiter */
394 s++;
395 *src = s;
396 return sbuf_done(sbuf);
399 static void ec_substitute(char *ec)
401 char loc[EXLEN], arg[EXLEN];
402 struct rset *re;
403 int offs[32];
404 int beg, end;
405 char *pat, *rep;
406 char *s = arg;
407 int delim;
408 int i;
409 ex_arg(ec, arg);
410 ex_loc(ec, loc);
411 if (ex_region(loc, &beg, &end))
412 return;
413 delim = (unsigned char) *s++;
414 pat = readuntil(&s, delim);
415 rep = readuntil(&s, delim);
416 re = rset_make(1, &pat, 0);
417 for (i = beg; i < end; i++) {
418 char *ln = lbuf_get(xb, i);
419 if (rset_find(re, ln, LEN(offs) / 2, offs, 0)) {
420 struct sbuf *r = sbuf_make();
421 sbuf_mem(r, ln, offs[0]);
422 sbuf_str(r, rep);
423 sbuf_str(r, ln + offs[1]);
424 lbuf_put(xb, i, sbuf_buf(r));
425 lbuf_rm(xb, i + 1, i + 2);
426 sbuf_free(r);
429 rset_free(re);
430 free(pat);
431 free(rep);
434 static struct excmd {
435 char *abbr;
436 char *name;
437 void (*ec)(char *s);
438 } excmds[] = {
439 {"p", "print", ec_print},
440 {"a", "append", ec_insert},
441 {"i", "insert", ec_insert},
442 {"d", "delete", ec_delete},
443 {"c", "change", ec_insert},
444 {"e", "edit", ec_edit},
445 {"=", "=", ec_lnum},
446 {"k", "mark", ec_mark},
447 {"pu", "put", ec_put},
448 {"q", "quit", ec_quit},
449 {"r", "read", ec_read},
450 {"w", "write", ec_write},
451 {"wq", "wq", ec_write},
452 {"u", "undo", ec_undo},
453 {"r", "redo", ec_redo},
454 {"s", "substitute", ec_substitute},
455 {"ya", "yank", ec_yank},
456 {"", "", ec_print},
459 /* execute a single ex command */
460 void ex_command(char *ln)
462 char cmd[EXLEN];
463 int i;
464 ex_cmd(ln, cmd);
465 for (i = 0; i < LEN(excmds); i++) {
466 if (!strcmp(excmds[i].abbr, cmd) || !strcmp(excmds[i].name, cmd)) {
467 excmds[i].ec(ln);
468 break;
471 lbuf_undomark(xb);
474 /* ex main loop */
475 void ex(void)
477 if (xled)
478 term_init();
479 while (!xquit) {
480 char *ln = ex_read(":");
481 if (ln)
482 ex_command(ln);
483 free(ln);
485 if (xled)
486 term_done();