1 /* $OpenBSD: vi.c,v 1.60 2021/03/12 02:10:25 millert Exp $ */
2
3 /*
4 * vi command editing
5 * written by John Rochester (initially for nsh)
6 * bludgeoned to fit pdksh by Larry Bouzane, Jeff Sparkes & Eric Gisin
7 *
8 */
9 #include "config.h"
10 #ifdef VI
11
12 #include <sys/stat.h> /* completion */
13
14 #include <ctype.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #ifndef SMALL
18 # include <term.h>
19 # include <curses.h>
20 #endif
21
22 #include "sh.h"
23 #include "edit.h"
24
25 #undef CTRL
26 #define CTRL(x) ((x) & 0x1F) /* ASCII */
27
28 struct edstate {
29 char *cbuf; /* main buffer to build the command line */
30 int cbufsize; /* number of bytes allocated for cbuf */
31 int linelen; /* current number of bytes in cbuf */
32 int winleft; /* first byte# in cbuf to be displayed */
33 int cursor; /* byte# in cbuf having the cursor */
34 };
35
36
37 static int vi_hook(int);
38 static void vi_reset(char *, size_t);
39 static int nextstate(int);
40 static int vi_insert(int);
41 static int vi_cmd(int, const char *);
42 static int domove(int, const char *, int);
43 static int redo_insert(int);
44 static void yank_range(int, int);
45 static int bracktype(int);
46 static void save_cbuf(void);
47 static void restore_cbuf(void);
48 static void edit_reset(char *, size_t);
49 static int putbuf(const char *, int, int);
50 static void del_range(int, int);
51 static int findch(int, int, int, int);
52 static int forwword(int);
53 static int backword(int);
54 static int endword(int);
55 static int Forwword(int);
56 static int Backword(int);
57 static int Endword(int);
58 static int grabhist(int, int);
59 static int grabsearch(int, int, int, char *);
60 static void do_clear_screen(void);
61 static void redraw_line(int, int);
62 static void refresh_line(int);
63 static int outofwin(void);
64 static void rewindow(void);
65 static int newcol(int, int);
66 static void display(char *, char *, int);
67 static void ed_mov_opt(int, char *);
68 static int expand_word(int);
69 static int complete_word(int, int);
70 static int print_expansions(struct edstate *);
71 static int char_len(int);
72 static void x_vi_zotc(int);
73 static void vi_pprompt(int);
74 static void vi_error(void);
75 static void vi_macro_reset(void);
76 static int x_vi_putbuf(const char *, size_t);
77 static int isu8cont(unsigned char);
78
79 #define C_ 0x1 /* a valid command that isn't a M_, E_, U_ */
80 #define M_ 0x2 /* movement command (h, l, etc.) */
81 #define E_ 0x4 /* extended command (c, d, y) */
82 #define X_ 0x8 /* long command (@, f, F, t, T, etc.) */
83 #define U_ 0x10 /* an UN-undoable command (that isn't a M_) */
84 #define B_ 0x20 /* bad command (^@) */
85 #define Z_ 0x40 /* repeat count defaults to 0 (not 1) */
86 #define S_ 0x80 /* search (/, ?) */
87
88 #define is_bad(c) (classify[(c)&0x7f]&B_)
89 #define is_cmd(c) (classify[(c)&0x7f]&(M_|E_|C_|U_))
90 #define is_move(c) (classify[(c)&0x7f]&M_)
91 #define is_extend(c) (classify[(c)&0x7f]&E_)
92 #define is_long(c) (classify[(c)&0x7f]&X_)
93 #define is_undoable(c) (!(classify[(c)&0x7f]&U_))
94 #define is_srch(c) (classify[(c)&0x7f]&S_)
95 #define is_zerocount(c) (classify[(c)&0x7f]&Z_)
96
97 const unsigned char classify[128] = {
98 /* 0 1 2 3 4 5 6 7 */
99 /* 0 ^@ ^A ^B ^C ^D ^E ^F ^G */
100 B_, 0, 0, 0, 0, C_|U_, C_|Z_, 0,
101 /* 01 ^H ^I ^J ^K ^L ^M ^N ^O */
102 M_, C_|Z_, 0, 0, C_|U_, 0, C_, 0,
103 /* 02 ^P ^Q ^R ^S ^T ^U ^V ^W */
104 C_, 0, C_|U_, 0, 0, 0, C_, 0,
105 /* 03 ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
106 C_, 0, 0, C_|Z_, 0, 0, 0, 0,
107 /* 04 <space> ! " # $ % & ' */
108 M_, 0, 0, C_, M_, M_, 0, 0,
109 /* 05 ( ) * + , - . / */
110 0, 0, C_, C_, M_, C_, 0, C_|S_,
111 /* 06 0 1 2 3 4 5 6 7 */
112 M_, 0, 0, 0, 0, 0, 0, 0,
113 /* 07 8 9 : ; < = > ? */
114 0, 0, 0, M_, 0, C_, 0, C_|S_,
115 /* 010 @ A B C D E F G */
116 C_|X_, C_, M_, C_, C_, M_, M_|X_, C_|U_|Z_,
117 /* 011 H I J K L M N O */
118 0, C_, 0, 0, 0, 0, C_|U_, 0,
119 /* 012 P Q R S T U V W */
120 C_, 0, C_, C_, M_|X_, C_, 0, M_,
121 /* 013 X Y Z [ \ ] ^ _ */
122 C_, C_|U_, 0, 0, C_|Z_, 0, M_, C_|Z_,
123 /* 014 ` a b c d e f g */
124 0, C_, M_, E_, E_, M_, M_|X_, C_|Z_,
125 /* 015 h i j k l m n o */
126 M_, C_, C_|U_, C_|U_, M_, 0, C_|U_, 0,
127 /* 016 p q r s t u v w */
128 C_, 0, X_, C_, M_|X_, C_|U_, C_|U_|Z_,M_,
129 /* 017 x y z { | } ~ ^? */
130 C_, E_|U_, 0, 0, M_|Z_, 0, C_, 0
131 };
132
133 #define MAXVICMD 3
134 #define SRCHLEN 40
135
136 #define INSERT 1
137 #define REPLACE 2
138
139 #define VNORMAL 0 /* command, insert or replace mode */
140 #define VARG1 1 /* digit prefix (first, eg, 5l) */
141 #define VEXTCMD 2 /* cmd + movement (eg, cl) */
142 #define VARG2 3 /* digit prefix (second, eg, 2c3l) */
143 #define VXCH 4 /* f, F, t, T, @ */
144 #define VFAIL 5 /* bad command */
145 #define VCMD 6 /* single char command (eg, X) */
146 #define VREDO 7 /* . */
147 #define VLIT 8 /* ^V */
148 #define VSEARCH 9 /* /, ? */
149
150 static char undocbuf[LINE];
151
152 static struct edstate *save_edstate(struct edstate *old);
153 static void restore_edstate(struct edstate *old, struct edstate *new);
154 static void free_edstate(struct edstate *old);
155
156 static struct edstate ebuf;
157 static struct edstate undobuf = { undocbuf, LINE, 0, 0, 0 };
158
159 static struct edstate *es; /* current editor state */
160 static struct edstate *undo;
161
162 static char ibuf[LINE]; /* input buffer */
163 static int first_insert; /* set when starting in insert mode */
164 static int saved_inslen; /* saved inslen for first insert */
165 static int inslen; /* length of input buffer */
166 static int srchlen; /* number of bytes in search pattern */
167 static char ybuf[LINE]; /* yank buffer */
168 static int yanklen; /* length of yank buffer */
169 static int fsavecmd = ' '; /* last find command */
170 static int fsavech; /* character to find */
171 static char lastcmd[MAXVICMD]; /* last non-move command */
172 static int lastac; /* argcnt for lastcmd */
173 static int lastsearch = ' '; /* last search command */
174 static char srchpat[SRCHLEN]; /* last search pattern */
175 static int insert; /* mode: INSERT, REPLACE, or 0 */
176 static int hnum; /* position in history */
177 static int ohnum; /* history line copied (after mod) */
178 static int hlast; /* 1 past last position in history */
179 static int modified; /* buffer has been "modified" */
180 static int state;
181
182 /* Information for keeping track of macros that are being expanded.
183 * The format of buf is the alias contents followed by a null byte followed
184 * by the name (letter) of the alias. The end of the buffer is marked by
185 * a double null. The name of the alias is stored so recursive macros can
186 * be detected.
187 */
188 struct macro_state {
189 unsigned char *p; /* current position in buf */
190 unsigned char *buf; /* pointer to macro(s) being expanded */
191 int len; /* how much data in buffer */
192 };
193 static struct macro_state macro;
194
195 enum expand_mode { NONE, EXPAND, COMPLETE, PRINT };
196 static enum expand_mode expanded = NONE;/* last input was expanded */
197
198 int
x_vi(char * buf,size_t len)199 x_vi(char *buf, size_t len)
200 {
201 int c;
202
203 vi_reset(buf, len > LINE ? LINE : len);
204 vi_pprompt(1);
205 x_flush();
206 while (1) {
207 if (macro.p) {
208 c = (unsigned char)*macro.p++;
209 /* end of current macro? */
210 if (!c) {
211 /* more macros left to finish? */
212 if (*macro.p++)
213 continue;
214 /* must be the end of all the macros */
215 vi_macro_reset();
216 c = x_getc();
217 }
218 } else
219 c = x_getc();
220
221 if (c == -1)
222 break;
223 if (state != VLIT) {
224 if (c == edchars.intr || c == edchars.quit) {
225 /* pretend we got an interrupt */
226 x_vi_zotc(c);
227 x_flush();
228 trapsig(c == edchars.intr ? SIGINT : SIGQUIT);
229 x_mode(false);
230 unwind(LSHELL);
231 } else if (c == edchars.eof) {
232 if (es->linelen == 0) {
233 x_vi_zotc(edchars.eof);
234 c = -1;
235 break;
236 }
237 continue;
238 }
239 }
240 if (vi_hook(c))
241 break;
242 x_flush();
243 }
244
245 x_putc('\r'); x_putc('\n'); x_flush();
246
247 if (c == -1 || len <= (size_t)es->linelen)
248 return -1;
249
250 if (es->cbuf != buf)
251 memmove(buf, es->cbuf, es->linelen);
252
253 buf[es->linelen++] = '\n';
254
255 return es->linelen;
256 }
257
258 static int
vi_hook(int ch)259 vi_hook(int ch)
260 {
261 static char curcmd[MAXVICMD], locpat[SRCHLEN];
262 static int cmdlen, argc1, argc2;
263
264 switch (state) {
265
266 case VNORMAL:
267 if (insert != 0) {
268 if (ch == CTRL('v')) {
269 state = VLIT;
270 ch = '^';
271 }
272 switch (vi_insert(ch)) {
273 case -1:
274 vi_error();
275 state = VNORMAL;
276 break;
277 case 0:
278 if (state == VLIT) {
279 es->cursor--;
280 refresh_line(0);
281 } else
282 refresh_line(insert != 0);
283 break;
284 case 1:
285 return 1;
286 }
287 } else {
288 if (ch == '\r' || ch == '\n')
289 return 1;
290 cmdlen = 0;
291 argc1 = 0;
292 if (ch >= '1' && ch <= '9') {
293 argc1 = ch - '0';
294 state = VARG1;
295 } else {
296 curcmd[cmdlen++] = ch;
297 state = nextstate(ch);
298 if (state == VSEARCH) {
299 save_cbuf();
300 es->cursor = 0;
301 es->linelen = 0;
302 if (ch == '/') {
303 if (putbuf("/", 1, 0) != 0)
304 return -1;
305 } else if (putbuf("?", 1, 0) != 0)
306 return -1;
307 refresh_line(0);
308 }
309 }
310 }
311 break;
312
313 case VLIT:
314 if (is_bad(ch)) {
315 del_range(es->cursor, es->cursor + 1);
316 vi_error();
317 } else
318 es->cbuf[es->cursor++] = ch;
319 refresh_line(1);
320 state = VNORMAL;
321 break;
322
323 case VARG1:
324 if (isdigit(ch))
325 argc1 = argc1 * 10 + ch - '0';
326 else {
327 curcmd[cmdlen++] = ch;
328 state = nextstate(ch);
329 }
330 break;
331
332 case VEXTCMD:
333 argc2 = 0;
334 if (ch >= '1' && ch <= '9') {
335 argc2 = ch - '0';
336 state = VARG2;
337 return 0;
338 } else {
339 curcmd[cmdlen++] = ch;
340 if (ch == curcmd[0])
341 state = VCMD;
342 else if (is_move(ch))
343 state = nextstate(ch);
344 else
345 state = VFAIL;
346 }
347 break;
348
349 case VARG2:
350 if (isdigit(ch))
351 argc2 = argc2 * 10 + ch - '0';
352 else {
353 if (argc1 == 0)
354 argc1 = argc2;
355 else
356 argc1 *= argc2;
357 curcmd[cmdlen++] = ch;
358 if (ch == curcmd[0])
359 state = VCMD;
360 else if (is_move(ch))
361 state = nextstate(ch);
362 else
363 state = VFAIL;
364 }
365 break;
366
367 case VXCH:
368 if (ch == CTRL('['))
369 state = VNORMAL;
370 else {
371 curcmd[cmdlen++] = ch;
372 state = VCMD;
373 }
374 break;
375
376 case VSEARCH:
377 if (ch == '\r' || ch == '\n' /*|| ch == CTRL('[')*/ ) {
378 restore_cbuf();
379 /* Repeat last search? */
380 if (srchlen == 0) {
381 if (!srchpat[0]) {
382 vi_error();
383 state = VNORMAL;
384 refresh_line(0);
385 return 0;
386 }
387 } else {
388 locpat[srchlen] = '\0';
389 (void) strlcpy(srchpat, locpat, sizeof srchpat);
390 }
391 state = VCMD;
392 } else if (ch == edchars.erase || ch == CTRL('h')) {
393 if (srchlen != 0) {
394 do {
395 srchlen--;
396 es->linelen -= char_len(
397 (unsigned char)locpat[srchlen]);
398 } while (srchlen > 0 &&
399 isu8cont(locpat[srchlen]));
400 es->cursor = es->linelen;
401 refresh_line(0);
402 return 0;
403 }
404 restore_cbuf();
405 state = VNORMAL;
406 refresh_line(0);
407 } else if (ch == edchars.kill) {
408 srchlen = 0;
409 es->linelen = 1;
410 es->cursor = 1;
411 refresh_line(0);
412 return 0;
413 } else if (ch == edchars.werase) {
414 struct edstate new_es, *save_es;
415 int i;
416 int n = srchlen;
417
418 new_es.cursor = n;
419 new_es.cbuf = locpat;
420
421 save_es = es;
422 es = &new_es;
423 n = backword(1);
424 es = save_es;
425
426 for (i = srchlen; --i >= n; )
427 es->linelen -= char_len((unsigned char)locpat[i]);
428 srchlen = n;
429 es->cursor = es->linelen;
430 refresh_line(0);
431 return 0;
432 } else {
433 if (srchlen == SRCHLEN - 1)
434 vi_error();
435 else {
436 locpat[srchlen++] = ch;
437 if ((ch & 0x80) && Flag(FVISHOW8)) {
438 if (es->linelen + 2 > es->cbufsize)
439 vi_error();
440 es->cbuf[es->linelen++] = 'M';
441 es->cbuf[es->linelen++] = '-';
442 ch &= 0x7f;
443 }
444 if (ch < ' ' || ch == 0x7f) {
445 if (es->linelen + 2 > es->cbufsize)
446 vi_error();
447 es->cbuf[es->linelen++] = '^';
448 es->cbuf[es->linelen++] = ch ^ '@';
449 } else {
450 if (es->linelen >= es->cbufsize)
451 vi_error();
452 es->cbuf[es->linelen++] = ch;
453 }
454 es->cursor = es->linelen;
455 refresh_line(0);
456 }
457 return 0;
458 }
459 break;
460 }
461
462 switch (state) {
463 case VCMD:
464 state = VNORMAL;
465 switch (vi_cmd(argc1, curcmd)) {
466 case -1:
467 vi_error();
468 refresh_line(0);
469 break;
470 case 0:
471 if (insert != 0)
472 inslen = 0;
473 refresh_line(insert != 0);
474 break;
475 case 1:
476 refresh_line(0);
477 return 1;
478 case 2:
479 /* back from a 'v' command - don't redraw the screen */
480 return 1;
481 }
482 break;
483
484 case VREDO:
485 state = VNORMAL;
486 if (argc1 != 0)
487 lastac = argc1;
488 switch (vi_cmd(lastac, lastcmd)) {
489 case -1:
490 vi_error();
491 refresh_line(0);
492 break;
493 case 0:
494 if (insert != 0) {
495 if (lastcmd[0] == 's' || lastcmd[0] == 'c' ||
496 lastcmd[0] == 'C') {
497 if (redo_insert(1) != 0)
498 vi_error();
499 } else {
500 if (redo_insert(lastac) != 0)
501 vi_error();
502 }
503 }
504 refresh_line(0);
505 break;
506 case 1:
507 refresh_line(0);
508 return 1;
509 case 2:
510 /* back from a 'v' command - can't happen */
511 break;
512 }
513 break;
514
515 case VFAIL:
516 state = VNORMAL;
517 vi_error();
518 break;
519 }
520 return 0;
521 }
522
523 static void
vi_reset(char * buf,size_t len)524 vi_reset(char *buf, size_t len)
525 {
526 state = VNORMAL;
527 ohnum = hnum = hlast = histnum(-1) + 1;
528 insert = INSERT;
529 saved_inslen = inslen;
530 first_insert = 1;
531 inslen = 0;
532 modified = 1;
533 vi_macro_reset();
534 edit_reset(buf, len);
535 }
536
537 static int
nextstate(int ch)538 nextstate(int ch)
539 {
540 if (is_extend(ch))
541 return VEXTCMD;
542 else if (is_srch(ch))
543 return VSEARCH;
544 else if (is_long(ch))
545 return VXCH;
546 else if (ch == '.')
547 return VREDO;
548 else if (is_cmd(ch))
549 return VCMD;
550 else
551 return VFAIL;
552 }
553
554 static int
vi_insert(int ch)555 vi_insert(int ch)
556 {
557 int tcursor;
558
559 if (ch == edchars.erase || ch == CTRL('h')) {
560 if (insert == REPLACE) {
561 if (es->cursor == undo->cursor) {
562 vi_error();
563 return 0;
564 }
565 } else {
566 if (es->cursor == 0) {
567 /* x_putc(BEL); no annoying bell here */
568 return 0;
569 }
570 }
571 tcursor = es->cursor - 1;
572 while(tcursor > 0 && isu8cont(es->cbuf[tcursor]))
573 tcursor--;
574 if (insert == INSERT)
575 memmove(es->cbuf + tcursor, es->cbuf + es->cursor,
576 es->linelen - es->cursor);
577 if (insert == REPLACE && es->cursor < undo->linelen)
578 memcpy(es->cbuf + tcursor, undo->cbuf + tcursor,
579 es->cursor - tcursor);
580 else
581 es->linelen -= es->cursor - tcursor;
582 if (inslen < es->cursor - tcursor)
583 inslen = 0;
584 else
585 inslen -= es->cursor - tcursor;
586 es->cursor = tcursor;
587 expanded = NONE;
588 return 0;
589 }
590 if (ch == edchars.kill) {
591 if (es->cursor != 0) {
592 inslen = 0;
593 memmove(es->cbuf, &es->cbuf[es->cursor],
594 es->linelen - es->cursor);
595 es->linelen -= es->cursor;
596 es->cursor = 0;
597 }
598 expanded = NONE;
599 return 0;
600 }
601 if (ch == edchars.werase) {
602 if (es->cursor != 0) {
603 tcursor = backword(1);
604 memmove(&es->cbuf[tcursor], &es->cbuf[es->cursor],
605 es->linelen - es->cursor);
606 es->linelen -= es->cursor - tcursor;
607 if (inslen < es->cursor - tcursor)
608 inslen = 0;
609 else
610 inslen -= es->cursor - tcursor;
611 es->cursor = tcursor;
612 }
613 expanded = NONE;
614 return 0;
615 }
616 /* If any chars are entered before escape, trash the saved insert
617 * buffer (if user inserts & deletes char, ibuf gets trashed and
618 * we don't want to use it)
619 */
620 if (first_insert && ch != CTRL('['))
621 saved_inslen = 0;
622 switch (ch) {
623 case '\0':
624 return -1;
625
626 case '\r':
627 case '\n':
628 return 1;
629
630 case CTRL('['):
631 expanded = NONE;
632 if (first_insert) {
633 first_insert = 0;
634 if (inslen == 0) {
635 inslen = saved_inslen;
636 return redo_insert(0);
637 }
638 lastcmd[0] = 'a';
639 lastac = 1;
640 }
641 if (lastcmd[0] == 's' || lastcmd[0] == 'c' ||
642 lastcmd[0] == 'C')
643 return redo_insert(0);
644 else
645 return redo_insert(lastac - 1);
646
647 /* { Begin nonstandard vi commands */
648 case CTRL('x'):
649 expand_word(0);
650 break;
651
652 case CTRL('f'):
653 complete_word(0, 0);
654 break;
655
656 case CTRL('e'):
657 print_expansions(es);
658 break;
659
660 case CTRL('l'):
661 do_clear_screen();
662 break;
663
664 case CTRL('r'):
665 redraw_line(1, 0);
666 break;
667
668 case CTRL('i'):
669 if (Flag(FVITABCOMPLETE)) {
670 complete_word(0, 0);
671 break;
672 }
673 /* FALLTHROUGH */
674 /* End nonstandard vi commands } */
675
676 default:
677 if (es->linelen >= es->cbufsize - 1)
678 return -1;
679 ibuf[inslen++] = ch;
680 if (insert == INSERT) {
681 memmove(&es->cbuf[es->cursor+1], &es->cbuf[es->cursor],
682 es->linelen - es->cursor);
683 es->linelen++;
684 }
685 es->cbuf[es->cursor++] = ch;
686 if (insert == REPLACE && es->cursor > es->linelen)
687 es->linelen++;
688 expanded = NONE;
689 }
690 return 0;
691 }
692
693 static int
vi_cmd(int argcnt,const char * cmd)694 vi_cmd(int argcnt, const char *cmd)
695 {
696 int ncursor;
697 int cur, c1, c2, c3 = 0;
698 int any;
699 struct edstate *t;
700
701 if (argcnt == 0 && !is_zerocount(*cmd))
702 argcnt = 1;
703
704 if (is_move(*cmd)) {
705 if ((cur = domove(argcnt, cmd, 0)) >= 0) {
706 if (cur == es->linelen && cur != 0)
707 while (isu8cont(es->cbuf[--cur]))
708 continue;
709 es->cursor = cur;
710 } else
711 return -1;
712 } else {
713 /* Don't save state in middle of macro.. */
714 if (is_undoable(*cmd) && !macro.p) {
715 undo->winleft = es->winleft;
716 memmove(undo->cbuf, es->cbuf, es->linelen);
717 undo->linelen = es->linelen;
718 undo->cursor = es->cursor;
719 lastac = argcnt;
720 memmove(lastcmd, cmd, MAXVICMD);
721 }
722 switch (*cmd) {
723
724 case CTRL('l'):
725 do_clear_screen();
726 break;
727
728 case CTRL('r'):
729 redraw_line(1, 0);
730 break;
731
732 case '@':
733 {
734 static char alias[] = "_\0";
735 struct tbl *ap;
736 int olen, nlen;
737 char *p, *nbuf;
738
739 /* lookup letter in alias list... */
740 alias[1] = cmd[1];
741 ap = ktsearch(&aliases, alias, hash(alias));
742 if (!cmd[1] || !ap || !(ap->flag & ISSET))
743 return -1;
744 /* check if this is a recursive call... */
745 if ((p = (char *) macro.p))
746 while ((p = strchr(p, '\0')) && p[1])
747 if (*++p == cmd[1])
748 return -1;
749 /* insert alias into macro buffer */
750 nlen = strlen(ap->val.s) + 1;
751 olen = !macro.p ? 2 :
752 macro.len - (macro.p - macro.buf);
753 nbuf = alloc(nlen + 1 + olen, APERM);
754 memcpy(nbuf, ap->val.s, nlen);
755 nbuf[nlen++] = cmd[1];
756 if (macro.p) {
757 memcpy(nbuf + nlen, macro.p, olen);
758 afree(macro.buf, APERM);
759 nlen += olen;
760 } else {
761 nbuf[nlen++] = '\0';
762 nbuf[nlen++] = '\0';
763 }
764 macro.p = macro.buf = (unsigned char *) nbuf;
765 macro.len = nlen;
766 }
767 break;
768
769 case 'a':
770 modified = 1; hnum = hlast;
771 if (es->linelen != 0)
772 while (isu8cont(es->cbuf[++es->cursor]))
773 continue;
774 insert = INSERT;
775 break;
776
777 case 'A':
778 modified = 1; hnum = hlast;
779 del_range(0, 0);
780 es->cursor = es->linelen;
781 insert = INSERT;
782 break;
783
784 case 'S':
785 es->cursor = domove(1, "^", 1);
786 del_range(es->cursor, es->linelen);
787 modified = 1; hnum = hlast;
788 insert = INSERT;
789 break;
790
791 case 'Y':
792 cmd = "y$";
793 /* ahhhhhh... */
794 case 'c':
795 case 'd':
796 case 'y':
797 if (*cmd == cmd[1]) {
798 c1 = *cmd == 'c' ? domove(1, "^", 1) : 0;
799 c2 = es->linelen;
800 } else if (!is_move(cmd[1]))
801 return -1;
802 else {
803 if ((ncursor = domove(argcnt, &cmd[1], 1)) < 0)
804 return -1;
805 if (*cmd == 'c' &&
806 (cmd[1]=='w' || cmd[1]=='W') &&
807 !isspace((unsigned char)es->cbuf[es->cursor])) {
808 while (isspace(
809 (unsigned char)es->cbuf[--ncursor]))
810 ;
811 ncursor++;
812 }
813 if (ncursor > es->cursor) {
814 c1 = es->cursor;
815 c2 = ncursor;
816 } else {
817 c1 = ncursor;
818 c2 = es->cursor;
819 if (cmd[1] == '%')
820 c2++;
821 }
822 }
823 if (*cmd != 'c' && c1 != c2)
824 yank_range(c1, c2);
825 if (*cmd != 'y') {
826 del_range(c1, c2);
827 es->cursor = c1;
828 }
829 if (*cmd == 'c') {
830 modified = 1; hnum = hlast;
831 insert = INSERT;
832 }
833 break;
834
835 case 'p':
836 modified = 1; hnum = hlast;
837 if (es->linelen != 0)
838 es->cursor++;
839 while (putbuf(ybuf, yanklen, 0) == 0 && --argcnt > 0)
840 ;
841 if (es->cursor != 0)
842 es->cursor--;
843 if (argcnt != 0)
844 return -1;
845 break;
846
847 case 'P':
848 modified = 1; hnum = hlast;
849 any = 0;
850 while (putbuf(ybuf, yanklen, 0) == 0 && --argcnt > 0)
851 any = 1;
852 if (any && es->cursor != 0)
853 es->cursor--;
854 if (argcnt != 0)
855 return -1;
856 break;
857
858 case 'C':
859 modified = 1; hnum = hlast;
860 del_range(es->cursor, es->linelen);
861 insert = INSERT;
862 break;
863
864 case 'D':
865 yank_range(es->cursor, es->linelen);
866 del_range(es->cursor, es->linelen);
867 if (es->cursor != 0)
868 es->cursor--;
869 break;
870
871 case 'g':
872 if (!argcnt)
873 argcnt = hlast;
874 /* FALLTHROUGH */
875 case 'G':
876 if (!argcnt)
877 argcnt = 1;
878 else
879 argcnt = hlast - (source->line - argcnt);
880 if (grabhist(modified, argcnt - 1) < 0)
881 return -1;
882 else {
883 modified = 0;
884 hnum = argcnt - 1;
885 }
886 break;
887
888 case 'i':
889 modified = 1; hnum = hlast;
890 insert = INSERT;
891 break;
892
893 case 'I':
894 modified = 1; hnum = hlast;
895 es->cursor = domove(1, "^", 1);
896 insert = INSERT;
897 break;
898
899 case 'j':
900 case '+':
901 case CTRL('n'):
902 if (grabhist(modified, hnum + argcnt) < 0)
903 return -1;
904 else {
905 modified = 0;
906 hnum += argcnt;
907 }
908 break;
909
910 case 'k':
911 case '-':
912 case CTRL('p'):
913 if (grabhist(modified, hnum - argcnt) < 0)
914 return -1;
915 else {
916 modified = 0;
917 hnum -= argcnt;
918 }
919 break;
920
921 case 'r':
922 if (es->linelen == 0)
923 return -1;
924 modified = 1; hnum = hlast;
925 if (cmd[1] == 0)
926 vi_error();
927 else {
928 c1 = 0;
929 for (cur = es->cursor;
930 cur < es->linelen; cur++) {
931 if (!isu8cont(es->cbuf[cur]))
932 c1++;
933 if (c1 > argcnt)
934 break;
935 }
936 if (argcnt > c1)
937 return -1;
938
939 del_range(es->cursor, cur);
940 while (argcnt-- > 0)
941 putbuf(&cmd[1], 1, 0);
942 while (es->cursor > 0)
943 if (!isu8cont(es->cbuf[--es->cursor]))
944 break;
945 es->cbuf[es->linelen] = '\0';
946 }
947 break;
948
949 case 'R':
950 modified = 1; hnum = hlast;
951 insert = REPLACE;
952 break;
953
954 case 's':
955 if (es->linelen == 0)
956 return -1;
957 modified = 1; hnum = hlast;
958 for (cur = es->cursor; cur < es->linelen; cur++)
959 if (!isu8cont(es->cbuf[cur]))
960 if (argcnt-- == 0)
961 break;
962 del_range(es->cursor, cur);
963 insert = INSERT;
964 break;
965
966 case 'v':
967 if (es->linelen == 0 && argcnt == 0)
968 return -1;
969 if (!argcnt) {
970 if (modified) {
971 es->cbuf[es->linelen] = '\0';
972 source->line++;
973 histsave(source->line, es->cbuf, 1);
974 } else
975 argcnt = source->line + 1
976 - (hlast - hnum);
977 }
978 shf_snprintf(es->cbuf, es->cbufsize,
979 argcnt ? "%s %d" : "%s",
980 "fc -e ${VISUAL:-${EDITOR:-vi}} --",
981 argcnt);
982 es->linelen = strlen(es->cbuf);
983 return 2;
984
985 case 'x':
986 if (es->linelen == 0)
987 return -1;
988 modified = 1; hnum = hlast;
989 for (cur = es->cursor; cur < es->linelen; cur++)
990 if (!isu8cont(es->cbuf[cur]))
991 if (argcnt-- == 0)
992 break;
993 yank_range(es->cursor, cur);
994 del_range(es->cursor, cur);
995 break;
996
997 case 'X':
998 if (es->cursor == 0)
999 return -1;
1000 modified = 1; hnum = hlast;
1001 for (cur = es->cursor; cur > 0; cur--)
1002 if (!isu8cont(es->cbuf[cur]))
1003 if (argcnt-- == 0)
1004 break;
1005 yank_range(cur, es->cursor);
1006 del_range(cur, es->cursor);
1007 es->cursor = cur;
1008 break;
1009
1010 case 'u':
1011 t = es;
1012 es = undo;
1013 undo = t;
1014 break;
1015
1016 case 'U':
1017 if (!modified)
1018 return -1;
1019 if (grabhist(modified, ohnum) < 0)
1020 return -1;
1021 modified = 0;
1022 hnum = ohnum;
1023 break;
1024
1025 case '?':
1026 if (hnum == hlast)
1027 hnum = -1;
1028 /* ahhh */
1029 case '/':
1030 c3 = 1;
1031 srchlen = 0;
1032 lastsearch = *cmd;
1033 /* FALLTHROUGH */
1034 case 'n':
1035 case 'N':
1036 if (lastsearch == ' ')
1037 return -1;
1038 if (lastsearch == '?')
1039 c1 = 1;
1040 else
1041 c1 = 0;
1042 if (*cmd == 'N')
1043 c1 = !c1;
1044 if ((c2 = grabsearch(modified, hnum,
1045 c1, srchpat)) < 0) {
1046 if (c3) {
1047 restore_cbuf();
1048 refresh_line(0);
1049 }
1050 return -1;
1051 } else {
1052 modified = 0;
1053 hnum = c2;
1054 ohnum = hnum;
1055 }
1056 break;
1057 case '_': {
1058 int inspace;
1059 char *p, *sp;
1060
1061 if (histnum(-1) < 0)
1062 return -1;
1063 p = *histpos();
1064 #define issp(c) (isspace((unsigned char)(c)) || (c) == '\n')
1065 if (argcnt) {
1066 while (*p && issp(*p))
1067 p++;
1068 while (*p && --argcnt) {
1069 while (*p && !issp(*p))
1070 p++;
1071 while (*p && issp(*p))
1072 p++;
1073 }
1074 if (!*p)
1075 return -1;
1076 sp = p;
1077 } else {
1078 sp = p;
1079 inspace = 0;
1080 while (*p) {
1081 if (issp(*p))
1082 inspace = 1;
1083 else if (inspace) {
1084 inspace = 0;
1085 sp = p;
1086 }
1087 p++;
1088 }
1089 p = sp;
1090 }
1091 modified = 1; hnum = hlast;
1092 if (es->cursor != es->linelen)
1093 es->cursor++;
1094 while (*p && !issp(*p)) {
1095 argcnt++;
1096 p++;
1097 }
1098 if (putbuf(" ", 1, 0) != 0)
1099 argcnt = -1;
1100 else if (putbuf(sp, argcnt, 0) != 0)
1101 argcnt = -1;
1102 if (argcnt < 0) {
1103 if (es->cursor != 0)
1104 es->cursor--;
1105 return -1;
1106 }
1107 insert = INSERT;
1108 }
1109 break;
1110
1111 case '~': {
1112 char *p;
1113 unsigned char c;
1114 int i;
1115
1116 if (es->linelen == 0)
1117 return -1;
1118 for (i = 0; i < argcnt; i++) {
1119 p = &es->cbuf[es->cursor];
1120 c = (unsigned char)*p;
1121 if (islower(c)) {
1122 modified = 1; hnum = hlast;
1123 *p = toupper(c);
1124 } else if (isupper(c)) {
1125 modified = 1; hnum = hlast;
1126 *p = tolower(c);
1127 }
1128 if (es->cursor < es->linelen - 1)
1129 es->cursor++;
1130 }
1131 break;
1132 }
1133
1134 case '#':
1135 {
1136 int ret = x_do_comment(es->cbuf, es->cbufsize,
1137 &es->linelen);
1138 if (ret >= 0)
1139 es->cursor = 0;
1140 return ret;
1141 }
1142
1143 case '=': /* at&t ksh */
1144 case CTRL('e'): /* Nonstandard vi/ksh */
1145 print_expansions(es);
1146 break;
1147
1148
1149 case CTRL('i'): /* Nonstandard vi/ksh */
1150 if (!Flag(FVITABCOMPLETE))
1151 return -1;
1152 complete_word(1, argcnt);
1153 break;
1154
1155 case CTRL('['): /* some annoying at&t ksh's */
1156 if (!Flag(FVIESCCOMPLETE))
1157 return -1;
1158 case '\\': /* at&t ksh */
1159 case CTRL('f'): /* Nonstandard vi/ksh */
1160 complete_word(1, argcnt);
1161 break;
1162
1163
1164 case '*': /* at&t ksh */
1165 case CTRL('x'): /* Nonstandard vi/ksh */
1166 expand_word(1);
1167 break;
1168 }
1169 if (insert == 0 && es->cursor >= es->linelen)
1170 while (es->cursor > 0)
1171 if (!isu8cont(es->cbuf[--es->cursor]))
1172 break;
1173 }
1174 return 0;
1175 }
1176
1177 static int
domove(int argcnt,const char * cmd,int sub)1178 domove(int argcnt, const char *cmd, int sub)
1179 {
1180 int bcount, i = 0, t;
1181 int ncursor = 0;
1182
1183 switch (*cmd) {
1184
1185 case 'b':
1186 case 'B':
1187 if (!sub && es->cursor == 0)
1188 return -1;
1189 ncursor = (*cmd == 'b' ? backword : Backword)(argcnt);
1190 break;
1191
1192 case 'e':
1193 case 'E':
1194 if (!sub && es->cursor + 1 >= es->linelen)
1195 return -1;
1196 ncursor = (*cmd == 'e' ? endword : Endword)(argcnt);
1197 if (!sub)
1198 while (isu8cont((unsigned char)es->cbuf[--ncursor]))
1199 continue;
1200 break;
1201
1202 case 'f':
1203 case 'F':
1204 case 't':
1205 case 'T':
1206 fsavecmd = *cmd;
1207 fsavech = cmd[1];
1208 /* drop through */
1209
1210 case ',':
1211 case ';':
1212 if (fsavecmd == ' ')
1213 return -1;
1214 i = fsavecmd == 'f' || fsavecmd == 'F';
1215 t = fsavecmd > 'a';
1216 if (*cmd == ',')
1217 t = !t;
1218 if ((ncursor = findch(fsavech, argcnt, t, i)) < 0)
1219 return -1;
1220 if (sub && t)
1221 ncursor++;
1222 break;
1223
1224 case 'h':
1225 case CTRL('h'):
1226 if (!sub && es->cursor == 0)
1227 return -1;
1228 for (ncursor = es->cursor; ncursor > 0; ncursor--)
1229 if (!isu8cont(es->cbuf[ncursor]))
1230 if (argcnt-- == 0)
1231 break;
1232 break;
1233
1234 case ' ':
1235 case 'l':
1236 if (!sub && es->cursor + 1 >= es->linelen)
1237 return -1;
1238 for (ncursor = es->cursor; ncursor < es->linelen; ncursor++)
1239 if (!isu8cont(es->cbuf[ncursor]))
1240 if (argcnt-- == 0)
1241 break;
1242 break;
1243
1244 case 'w':
1245 case 'W':
1246 if (!sub && es->cursor + 1 >= es->linelen)
1247 return -1;
1248 ncursor = (*cmd == 'w' ? forwword : Forwword)(argcnt);
1249 break;
1250
1251 case '0':
1252 ncursor = 0;
1253 break;
1254
1255 case '^':
1256 ncursor = 0;
1257 while (ncursor < es->linelen - 1 &&
1258 isspace((unsigned char)es->cbuf[ncursor]))
1259 ncursor++;
1260 break;
1261
1262 case '|':
1263 ncursor = argcnt;
1264 if (ncursor > es->linelen)
1265 ncursor = es->linelen;
1266 if (ncursor)
1267 ncursor--;
1268 while (isu8cont(es->cbuf[ncursor]))
1269 ncursor--;
1270 break;
1271
1272 case '$':
1273 ncursor = es->linelen;
1274 break;
1275
1276 case '%':
1277 ncursor = es->cursor;
1278 while (ncursor < es->linelen &&
1279 (i = bracktype(es->cbuf[ncursor])) == 0)
1280 ncursor++;
1281 if (ncursor == es->linelen)
1282 return -1;
1283 bcount = 1;
1284 do {
1285 if (i > 0) {
1286 if (++ncursor >= es->linelen)
1287 return -1;
1288 } else {
1289 if (--ncursor < 0)
1290 return -1;
1291 }
1292 t = bracktype(es->cbuf[ncursor]);
1293 if (t == i)
1294 bcount++;
1295 else if (t == -i)
1296 bcount--;
1297 } while (bcount != 0);
1298 if (sub && i > 0)
1299 ncursor++;
1300 break;
1301
1302 default:
1303 return -1;
1304 }
1305 return ncursor;
1306 }
1307
1308 static int
redo_insert(int count)1309 redo_insert(int count)
1310 {
1311 while (count-- > 0)
1312 if (putbuf(ibuf, inslen, insert==REPLACE) != 0)
1313 return -1;
1314 if (es->cursor > 0)
1315 while (isu8cont(es->cbuf[--es->cursor]))
1316 continue;
1317 insert = 0;
1318 return 0;
1319 }
1320
1321 static void
yank_range(int a,int b)1322 yank_range(int a, int b)
1323 {
1324 yanklen = b - a;
1325 if (yanklen != 0)
1326 memmove(ybuf, &es->cbuf[a], yanklen);
1327 }
1328
1329 static int
bracktype(int ch)1330 bracktype(int ch)
1331 {
1332 switch (ch) {
1333
1334 case '(':
1335 return 1;
1336
1337 case '[':
1338 return 2;
1339
1340 case '{':
1341 return 3;
1342
1343 case ')':
1344 return -1;
1345
1346 case ']':
1347 return -2;
1348
1349 case '}':
1350 return -3;
1351
1352 default:
1353 return 0;
1354 }
1355 }
1356
1357 /*
1358 * Non user interface editor routines below here
1359 */
1360
1361 static int cur_col; /* current display column */
1362 static int pwidth; /* display columns needed for prompt */
1363 static int prompt_trunc; /* how much of prompt to truncate */
1364 static int prompt_skip; /* how much of prompt to skip */
1365 static int winwidth; /* available column positions */
1366 static char *wbuf[2]; /* current & previous window buffer */
1367 static int wbuf_len; /* length of window buffers (x_cols-3)*/
1368 static int win; /* number of window buffer in use */
1369 static char morec; /* more character at right of window */
1370 static char holdbuf[LINE]; /* place to hold last edit buffer */
1371 static int holdlen; /* length of holdbuf */
1372
1373 static void
save_cbuf(void)1374 save_cbuf(void)
1375 {
1376 memmove(holdbuf, es->cbuf, es->linelen);
1377 holdlen = es->linelen;
1378 holdbuf[holdlen] = '\0';
1379 }
1380
1381 static void
restore_cbuf(void)1382 restore_cbuf(void)
1383 {
1384 es->cursor = 0;
1385 es->linelen = holdlen;
1386 memmove(es->cbuf, holdbuf, holdlen);
1387 }
1388
1389 /* return a new edstate */
1390 static struct edstate *
save_edstate(struct edstate * old)1391 save_edstate(struct edstate *old)
1392 {
1393 struct edstate *new;
1394
1395 new = alloc(sizeof(struct edstate), APERM);
1396 new->cbuf = alloc(old->cbufsize, APERM);
1397 memcpy(new->cbuf, old->cbuf, old->linelen);
1398 new->cbufsize = old->cbufsize;
1399 new->linelen = old->linelen;
1400 new->cursor = old->cursor;
1401 new->winleft = old->winleft;
1402 return new;
1403 }
1404
1405 static void
restore_edstate(struct edstate * new,struct edstate * old)1406 restore_edstate(struct edstate *new, struct edstate *old)
1407 {
1408 memcpy(new->cbuf, old->cbuf, old->linelen);
1409 new->linelen = old->linelen;
1410 new->cursor = old->cursor;
1411 new->winleft = old->winleft;
1412 free_edstate(old);
1413 }
1414
1415 static void
free_edstate(struct edstate * old)1416 free_edstate(struct edstate *old)
1417 {
1418 afree(old->cbuf, APERM);
1419 afree(old, APERM);
1420 }
1421
1422
1423
1424 static void
edit_reset(char * buf,size_t len)1425 edit_reset(char *buf, size_t len)
1426 {
1427 const char *p;
1428
1429 es = &ebuf;
1430 es->cbuf = buf;
1431 es->cbufsize = len;
1432 undo = &undobuf;
1433 undo->cbufsize = len;
1434
1435 es->linelen = undo->linelen = 0;
1436 es->cursor = undo->cursor = 0;
1437 es->winleft = undo->winleft = 0;
1438
1439 cur_col = pwidth = promptlen(prompt, &p);
1440 prompt_skip = p - prompt;
1441 if (pwidth > x_cols - 3 - MIN_EDIT_SPACE) {
1442 cur_col = x_cols - 3 - MIN_EDIT_SPACE;
1443 prompt_trunc = pwidth - cur_col;
1444 pwidth -= prompt_trunc;
1445 } else
1446 prompt_trunc = 0;
1447 if (!wbuf_len || wbuf_len != x_cols - 3) {
1448 wbuf_len = x_cols - 3;
1449 wbuf[0] = aresize(wbuf[0], wbuf_len, APERM);
1450 wbuf[1] = aresize(wbuf[1], wbuf_len, APERM);
1451 }
1452 (void) memset(wbuf[0], ' ', wbuf_len);
1453 (void) memset(wbuf[1], ' ', wbuf_len);
1454 winwidth = x_cols - pwidth - 3;
1455 win = 0;
1456 morec = ' ';
1457 holdlen = 0;
1458 }
1459
1460 /*
1461 * this is used for calling x_escape() in complete_word()
1462 */
1463 static int
x_vi_putbuf(const char * s,size_t len)1464 x_vi_putbuf(const char *s, size_t len)
1465 {
1466 return putbuf(s, len, 0);
1467 }
1468
1469 static int
putbuf(const char * buf,int len,int repl)1470 putbuf(const char *buf, int len, int repl)
1471 {
1472 if (len == 0)
1473 return 0;
1474 if (repl) {
1475 if (es->cursor + len >= es->cbufsize)
1476 return -1;
1477 if (es->cursor + len > es->linelen)
1478 es->linelen = es->cursor + len;
1479 } else {
1480 if (es->linelen + len >= es->cbufsize)
1481 return -1;
1482 memmove(&es->cbuf[es->cursor + len], &es->cbuf[es->cursor],
1483 es->linelen - es->cursor);
1484 es->linelen += len;
1485 }
1486 memmove(&es->cbuf[es->cursor], buf, len);
1487 es->cursor += len;
1488 return 0;
1489 }
1490
1491 static void
del_range(int a,int b)1492 del_range(int a, int b)
1493 {
1494 if (es->linelen != b)
1495 memmove(&es->cbuf[a], &es->cbuf[b], es->linelen - b);
1496 es->linelen -= b - a;
1497 }
1498
1499 static int
findch(int ch,int cnt,int forw,int incl)1500 findch(int ch, int cnt, int forw, int incl)
1501 {
1502 int ncursor;
1503
1504 if (es->linelen == 0)
1505 return -1;
1506 ncursor = es->cursor;
1507 while (cnt--) {
1508 do {
1509 if (forw) {
1510 if (++ncursor == es->linelen)
1511 return -1;
1512 } else {
1513 if (--ncursor < 0)
1514 return -1;
1515 }
1516 } while (es->cbuf[ncursor] != ch);
1517 }
1518 if (!incl) {
1519 if (forw)
1520 ncursor--;
1521 else
1522 ncursor++;
1523 }
1524 return ncursor;
1525 }
1526
1527 /* Move right one character, and then to the beginning of the next word. */
1528 static int
forwword(int argcnt)1529 forwword(int argcnt)
1530 {
1531 int ncursor, skip_space, want_letnum;
1532 unsigned char uc;
1533
1534 ncursor = es->cursor;
1535 while (ncursor < es->linelen && argcnt--) {
1536 skip_space = 0;
1537 want_letnum = -1;
1538 ncursor--;
1539 while (++ncursor < es->linelen) {
1540 uc = es->cbuf[ncursor];
1541 if (isspace(uc)) {
1542 skip_space = 1;
1543 continue;
1544 } else if (skip_space)
1545 break;
1546 if (uc & 0x80)
1547 continue;
1548 if (want_letnum == -1)
1549 want_letnum = letnum(uc);
1550 else if (want_letnum != letnum(uc))
1551 break;
1552 }
1553 }
1554 return ncursor;
1555 }
1556
1557 /* Move left one character, and then to the beginning of the word. */
1558 static int
backword(int argcnt)1559 backword(int argcnt)
1560 {
1561 int ncursor, skip_space, want_letnum;
1562 unsigned char uc;
1563
1564 ncursor = es->cursor;
1565 while (ncursor > 0 && argcnt--) {
1566 skip_space = 1;
1567 want_letnum = -1;
1568 while (ncursor-- > 0) {
1569 uc = es->cbuf[ncursor];
1570 if (isspace(uc)) {
1571 if (skip_space)
1572 continue;
1573 else
1574 break;
1575 }
1576 skip_space = 0;
1577 if (uc & 0x80)
1578 continue;
1579 if (want_letnum == -1)
1580 want_letnum = letnum(uc);
1581 else if (want_letnum != letnum(uc))
1582 break;
1583 }
1584 ncursor++;
1585 }
1586 return ncursor;
1587 }
1588
1589 /* Move right one character, and then to the byte after the word. */
1590 static int
endword(int argcnt)1591 endword(int argcnt)
1592 {
1593 int ncursor, skip_space, want_letnum;
1594 unsigned char uc;
1595
1596 ncursor = es->cursor;
1597 while (ncursor < es->linelen && argcnt--) {
1598 skip_space = 1;
1599 want_letnum = -1;
1600 while (++ncursor < es->linelen) {
1601 uc = es->cbuf[ncursor];
1602 if (isspace(uc)) {
1603 if (skip_space)
1604 continue;
1605 else
1606 break;
1607 }
1608 skip_space = 0;
1609 if (uc & 0x80)
1610 continue;
1611 if (want_letnum == -1)
1612 want_letnum = letnum(uc);
1613 else if (want_letnum != letnum(uc))
1614 break;
1615 }
1616 }
1617 return ncursor;
1618 }
1619
1620 /* Move right one character, and then to the beginning of the next big word. */
1621 static int
Forwword(int argcnt)1622 Forwword(int argcnt)
1623 {
1624 int ncursor;
1625
1626 ncursor = es->cursor;
1627 while (ncursor < es->linelen && argcnt--) {
1628 while (!isspace((unsigned char)es->cbuf[ncursor]) &&
1629 ncursor < es->linelen)
1630 ncursor++;
1631 while (isspace((unsigned char)es->cbuf[ncursor]) &&
1632 ncursor < es->linelen)
1633 ncursor++;
1634 }
1635 return ncursor;
1636 }
1637
1638 /* Move left one character, and then to the beginning of the big word. */
1639 static int
Backword(int argcnt)1640 Backword(int argcnt)
1641 {
1642 int ncursor;
1643
1644 ncursor = es->cursor;
1645 while (ncursor > 0 && argcnt--) {
1646 while (--ncursor >= 0 &&
1647 isspace((unsigned char)es->cbuf[ncursor]))
1648 ;
1649 while (ncursor >= 0 &&
1650 !isspace((unsigned char)es->cbuf[ncursor]))
1651 ncursor--;
1652 ncursor++;
1653 }
1654 return ncursor;
1655 }
1656
1657 /* Move right one character, and then to the byte after the big word. */
1658 static int
Endword(int argcnt)1659 Endword(int argcnt)
1660 {
1661 int ncursor;
1662
1663 ncursor = es->cursor;
1664 while (ncursor < es->linelen && argcnt--) {
1665 while (++ncursor < es->linelen &&
1666 isspace((unsigned char)es->cbuf[ncursor]))
1667 ;
1668 while (ncursor < es->linelen &&
1669 !isspace((unsigned char)es->cbuf[ncursor]))
1670 ncursor++;
1671 }
1672 return ncursor;
1673 }
1674
1675 static int
grabhist(int save,int n)1676 grabhist(int save, int n)
1677 {
1678 char *hptr;
1679
1680 if (n < 0 || n > hlast)
1681 return -1;
1682 if (n == hlast) {
1683 restore_cbuf();
1684 ohnum = n;
1685 return 0;
1686 }
1687 (void) histnum(n);
1688 if ((hptr = *histpos()) == NULL) {
1689 internal_warningf("%s: bad history array", __func__);
1690 return -1;
1691 }
1692 if (save)
1693 save_cbuf();
1694 if ((es->linelen = strlen(hptr)) >= es->cbufsize)
1695 es->linelen = es->cbufsize - 1;
1696 memmove(es->cbuf, hptr, es->linelen);
1697 es->cursor = 0;
1698 ohnum = n;
1699 return 0;
1700 }
1701
1702 static int
grabsearch(int save,int start,int fwd,char * pat)1703 grabsearch(int save, int start, int fwd, char *pat)
1704 {
1705 char *hptr;
1706 int hist;
1707 int anchored;
1708
1709 if ((start == 0 && fwd == 0) || (start >= hlast-1 && fwd == 1))
1710 return -1;
1711 if (fwd)
1712 start++;
1713 else
1714 start--;
1715 anchored = *pat == '^' ? (++pat, 1) : 0;
1716 if ((hist = findhist(start, fwd, pat, anchored)) < 0) {
1717 /* if (start != 0 && fwd && match(holdbuf, pat) >= 0) { */
1718 /* XXX should strcmp be strncmp? */
1719 if (start != 0 && fwd && strcmp(holdbuf, pat) >= 0) {
1720 restore_cbuf();
1721 return 0;
1722 } else
1723 return -1;
1724 }
1725 if (save)
1726 save_cbuf();
1727 histnum(hist);
1728 hptr = *histpos();
1729 if ((es->linelen = strlen(hptr)) >= es->cbufsize)
1730 es->linelen = es->cbufsize - 1;
1731 memmove(es->cbuf, hptr, es->linelen);
1732 es->cursor = 0;
1733 return hist;
1734 }
1735
1736 static void
do_clear_screen(void)1737 do_clear_screen(void)
1738 {
1739 int neednl = 1;
1740
1741 #ifndef SMALL
1742 if (cur_term != NULL && clear_screen != NULL) {
1743 if (tputs(clear_screen, 1, x_putc) != ERR)
1744 neednl = 0;
1745 }
1746 #endif
1747 /* Only print the full prompt if we cleared the screen. */
1748 redraw_line(neednl, !neednl);
1749 }
1750
1751 static void
redraw_line(int neednl,int full)1752 redraw_line(int neednl, int full)
1753 {
1754 (void) memset(wbuf[win], ' ', wbuf_len);
1755 if (neednl) {
1756 x_putc('\r');
1757 x_putc('\n');
1758 }
1759 vi_pprompt(full);
1760 cur_col = pwidth;
1761 morec = ' ';
1762 }
1763
1764 static void
refresh_line(int leftside)1765 refresh_line(int leftside)
1766 {
1767 if (outofwin())
1768 rewindow();
1769 display(wbuf[1 - win], wbuf[win], leftside);
1770 win = 1 - win;
1771 }
1772
1773 static int
outofwin(void)1774 outofwin(void)
1775 {
1776 int cur, col;
1777
1778 if (es->cursor < es->winleft)
1779 return 1;
1780 col = 0;
1781 cur = es->winleft;
1782 while (cur < es->cursor)
1783 col = newcol((unsigned char) es->cbuf[cur++], col);
1784 if (col >= winwidth)
1785 return 1;
1786 return 0;
1787 }
1788
1789 static void
rewindow(void)1790 rewindow(void)
1791 {
1792 int tcur, tcol;
1793 int holdcur1, holdcol1;
1794 int holdcur2, holdcol2;
1795
1796 holdcur1 = holdcur2 = tcur = 0;
1797 holdcol1 = holdcol2 = tcol = 0;
1798 while (tcur < es->cursor) {
1799 if (tcol - holdcol2 > winwidth / 2) {
1800 holdcur1 = holdcur2;
1801 holdcol1 = holdcol2;
1802 holdcur2 = tcur;
1803 holdcol2 = tcol;
1804 }
1805 tcol = newcol((unsigned char) es->cbuf[tcur++], tcol);
1806 }
1807 while (tcol - holdcol1 > winwidth / 2)
1808 holdcol1 = newcol((unsigned char) es->cbuf[holdcur1++],
1809 holdcol1);
1810 es->winleft = holdcur1;
1811 }
1812
1813 /* Printing the byte ch at display column col moves to which column? */
1814 static int
newcol(int ch,int col)1815 newcol(int ch, int col)
1816 {
1817 if (ch == '\t')
1818 return (col | 7) + 1;
1819 if (isu8cont(ch))
1820 return col;
1821 return col + char_len(ch);
1822 }
1823
1824 /* Display wb1 assuming that wb2 is currently displayed. */
1825 static void
display(char * wb1,char * wb2,int leftside)1826 display(char *wb1, char *wb2, int leftside)
1827 {
1828 char *twb1; /* pointer into the buffer to display */
1829 char *twb2; /* pointer into the previous display buffer */
1830 static int lastb = -1; /* last byte# written from wb1, if UTF-8 */
1831 int cur; /* byte# in the main command line buffer */
1832 int col; /* display column loop variable */
1833 int ncol; /* display column of the cursor */
1834 int cnt; /* remaining display columns to fill */
1835 int moreright;
1836 char mc; /* new "more character" at the right of window */
1837 unsigned char ch;
1838
1839 /*
1840 * Fill the current display buffer with data from cbuf.
1841 * In this first loop, col does not include the prompt.
1842 */
1843
1844 ncol = col = 0;
1845 cur = es->winleft;
1846 moreright = 0;
1847 twb1 = wb1;
1848 while (col < winwidth && cur < es->linelen) {
1849 if (cur == es->cursor && leftside)
1850 ncol = col + pwidth;
1851 if ((ch = es->cbuf[cur]) == '\t') {
1852 do {
1853 *twb1++ = ' ';
1854 } while (++col < winwidth && (col & 7) != 0);
1855 } else {
1856 if ((ch & 0x80) && Flag(FVISHOW8)) {
1857 *twb1++ = 'M';
1858 if (++col < winwidth) {
1859 *twb1++ = '-';
1860 col++;
1861 }
1862 ch &= 0x7f;
1863 }
1864 if (col < winwidth) {
1865 if (ch < ' ' || ch == 0x7f) {
1866 *twb1++ = '^';
1867 if (++col < winwidth) {
1868 *twb1++ = ch ^ '@';
1869 col++;
1870 }
1871 } else {
1872 *twb1++ = ch;
1873 if (!isu8cont(ch))
1874 col++;
1875 }
1876 }
1877 }
1878 if (cur == es->cursor && !leftside)
1879 ncol = col + pwidth - 1;
1880 cur++;
1881 }
1882 if (cur == es->cursor)
1883 ncol = col + pwidth;
1884
1885 /* Pad the current display buffer to the right margin. */
1886
1887 if (col < winwidth) {
1888 while (col < winwidth) {
1889 *twb1++ = ' ';
1890 col++;
1891 }
1892 } else
1893 moreright++;
1894 *twb1 = ' ';
1895
1896 /*
1897 * Update the terminal display with data from wb1.
1898 * In this final loop, col includes the prompt.
1899 */
1900
1901 col = pwidth;
1902 cnt = winwidth;
1903 for (twb1 = wb1, twb2 = wb2; cnt; twb1++, twb2++) {
1904 if (*twb1 != *twb2) {
1905
1906 /*
1907 * When a byte changes in the middle of a UTF-8
1908 * character, back up to the start byte, unless
1909 * the previous byte was the last one written.
1910 */
1911
1912 if (col > 0 && isu8cont(*twb1)) {
1913 col--;
1914 if (lastb >= 0 && twb1 == wb1 + lastb + 1)
1915 cur_col = col;
1916 else while (twb1 > wb1 && isu8cont(*twb1)) {
1917 twb1--;
1918 twb2--;
1919 }
1920 }
1921
1922 if (cur_col != col)
1923 ed_mov_opt(col, wb1);
1924
1925 /*
1926 * Always write complete characters, and
1927 * advance all pointers accordingly.
1928 */
1929
1930 x_putc(*twb1);
1931 while (isu8cont(twb1[1])) {
1932 x_putc(*++twb1);
1933 twb2++;
1934 }
1935 lastb = *twb1 & 0x80 ? twb1 - wb1 : -1;
1936 cur_col++;
1937 } else if (isu8cont(*twb1))
1938 continue;
1939
1940 /*
1941 * For changed continuation bytes, we backed up.
1942 * For unchanged ones, we jumped to the next byte.
1943 * So, getting here, we had a real column.
1944 */
1945
1946 col++;
1947 cnt--;
1948 }
1949
1950 /* Update the "more character". */
1951
1952 if (es->winleft > 0 && moreright)
1953 /* POSIX says to use * for this but that is a globbing
1954 * character and may confuse people; + is more innocuous
1955 */
1956 mc = '+';
1957 else if (es->winleft > 0)
1958 mc = '<';
1959 else if (moreright)
1960 mc = '>';
1961 else
1962 mc = ' ';
1963 if (mc != morec) {
1964 ed_mov_opt(pwidth + winwidth + 1, wb1);
1965 x_putc(mc);
1966 cur_col++;
1967 morec = mc;
1968 lastb = -1;
1969 }
1970
1971 /* Move the cursor to its new position. */
1972
1973 if (cur_col != ncol) {
1974 ed_mov_opt(ncol, wb1);
1975 lastb = -1;
1976 }
1977 }
1978
1979 /* Move the display cursor to display column number col. */
1980 static void
ed_mov_opt(int col,char * wb)1981 ed_mov_opt(int col, char *wb)
1982 {
1983 int ci;
1984
1985 /* The cursor is already at the right place. */
1986
1987 if (cur_col == col)
1988 return;
1989
1990 /* The cursor is too far right. */
1991
1992 if (cur_col > col) {
1993 if (cur_col > 2 * col + 1) {
1994 /* Much too far right, redraw from scratch. */
1995 x_putc('\r');
1996 vi_pprompt(0);
1997 cur_col = pwidth;
1998 } else {
1999 /* Slightly too far right, back up. */
2000 do {
2001 x_putc('\b');
2002 } while (--cur_col > col);
2003 return;
2004 }
2005 }
2006
2007 /* Advance the cursor. */
2008
2009 for (ci = pwidth; ci < col || isu8cont(*wb);
2010 ci = newcol((unsigned char)*wb++, ci))
2011 if (ci > cur_col || (ci == cur_col && !isu8cont(*wb)))
2012 x_putc(*wb);
2013 cur_col = ci;
2014 }
2015
2016
2017 /* replace word with all expansions (ie, expand word*) */
2018 static int
expand_word(int command)2019 expand_word(int command)
2020 {
2021 static struct edstate *buf;
2022 int rval = 0;
2023 int nwords;
2024 int start, end;
2025 char **words;
2026 int i;
2027
2028 /* Undo previous expansion */
2029 if (command == 0 && expanded == EXPAND && buf) {
2030 restore_edstate(es, buf);
2031 buf = NULL;
2032 expanded = NONE;
2033 return 0;
2034 }
2035 if (buf) {
2036 free_edstate(buf);
2037 buf = NULL;
2038 }
2039
2040 nwords = x_cf_glob(XCF_COMMAND_FILE|XCF_FULLPATH,
2041 es->cbuf, es->linelen, es->cursor,
2042 &start, &end, &words, NULL);
2043 if (nwords == 0) {
2044 vi_error();
2045 return -1;
2046 }
2047
2048 buf = save_edstate(es);
2049 expanded = EXPAND;
2050 del_range(start, end);
2051 es->cursor = start;
2052 for (i = 0; i < nwords; ) {
2053 if (x_escape(words[i], strlen(words[i]), x_vi_putbuf) != 0) {
2054 rval = -1;
2055 break;
2056 }
2057 if (++i < nwords && putbuf(" ", 1, 0) != 0) {
2058 rval = -1;
2059 break;
2060 }
2061 }
2062 i = buf->cursor - end;
2063 if (rval == 0 && i > 0)
2064 es->cursor += i;
2065 modified = 1; hnum = hlast;
2066 insert = INSERT;
2067 lastac = 0;
2068 refresh_line(0);
2069 return rval;
2070 }
2071
2072 static int
complete_word(int command,int count)2073 complete_word(int command, int count)
2074 {
2075 static struct edstate *buf;
2076 int rval = 0;
2077 int nwords;
2078 int start, end;
2079 char **words;
2080 char *match;
2081 int match_len;
2082 int is_unique;
2083 int is_command;
2084
2085 /* Undo previous completion */
2086 if (command == 0 && expanded == COMPLETE && buf) {
2087 print_expansions(buf);
2088 expanded = PRINT;
2089 return 0;
2090 }
2091 if (command == 0 && expanded == PRINT && buf) {
2092 restore_edstate(es, buf);
2093 buf = NULL;
2094 expanded = NONE;
2095 return 0;
2096 }
2097 if (buf) {
2098 free_edstate(buf);
2099 buf = NULL;
2100 }
2101
2102 /* XCF_FULLPATH for count 'cause the menu printed by print_expansions()
2103 * was done this way.
2104 */
2105 nwords = x_cf_glob(XCF_COMMAND_FILE | (count ? XCF_FULLPATH : 0),
2106 es->cbuf, es->linelen, es->cursor,
2107 &start, &end, &words, &is_command);
2108 if (nwords == 0) {
2109 vi_error();
2110 return -1;
2111 }
2112 if (count) {
2113 int i;
2114
2115 count--;
2116 if (count >= nwords) {
2117 vi_error();
2118 x_print_expansions(nwords, words, is_command);
2119 x_free_words(nwords, words);
2120 redraw_line(0, 0);
2121 return -1;
2122 }
2123 /*
2124 * Expand the count'th word to its basename
2125 */
2126 if (is_command) {
2127 match = words[count] +
2128 x_basename(words[count], NULL);
2129 /* If more than one possible match, use full path */
2130 for (i = 0; i < nwords; i++)
2131 if (i != count &&
2132 strcmp(words[i] + x_basename(words[i],
2133 NULL), match) == 0) {
2134 match = words[count];
2135 break;
2136 }
2137 } else
2138 match = words[count];
2139 match_len = strlen(match);
2140 is_unique = 1;
2141 /* expanded = PRINT; next call undo */
2142 } else {
2143 match = words[0];
2144 match_len = x_longest_prefix(nwords, words);
2145 expanded = COMPLETE; /* next call will list completions */
2146 is_unique = nwords == 1;
2147 }
2148
2149 buf = save_edstate(es);
2150 del_range(start, end);
2151 es->cursor = start;
2152
2153 /* escape all shell-sensitive characters and put the result into
2154 * command buffer */
2155 rval = x_escape(match, match_len, x_vi_putbuf);
2156
2157 if (rval == 0 && is_unique) {
2158 /* If exact match, don't undo. Allows directory completions
2159 * to be used (ie, complete the next portion of the path).
2160 */
2161 expanded = NONE;
2162
2163 /* If not a directory, add a space to the end... */
2164 if (match_len > 0 && match[match_len - 1] != '/')
2165 rval = putbuf(" ", 1, 0);
2166 }
2167 x_free_words(nwords, words);
2168
2169 modified = 1; hnum = hlast;
2170 insert = INSERT;
2171 lastac = 0; /* prevent this from being redone... */
2172 refresh_line(0);
2173
2174 return rval;
2175 }
2176
2177 static int
print_expansions(struct edstate * e)2178 print_expansions(struct edstate *e)
2179 {
2180 int nwords;
2181 int start, end;
2182 char **words;
2183 int is_command;
2184
2185 nwords = x_cf_glob(XCF_COMMAND_FILE|XCF_FULLPATH,
2186 e->cbuf, e->linelen, e->cursor,
2187 &start, &end, &words, &is_command);
2188 if (nwords == 0) {
2189 vi_error();
2190 return -1;
2191 }
2192 x_print_expansions(nwords, words, is_command);
2193 x_free_words(nwords, words);
2194 redraw_line(0, 0);
2195 return 0;
2196 }
2197
2198 /*
2199 * The number of bytes needed to encode byte c.
2200 * Control bytes get "M-" or "^" prepended.
2201 * This function does not handle tabs.
2202 */
2203 static int
char_len(int c)2204 char_len(int c)
2205 {
2206 int len = 1;
2207
2208 if ((c & 0x80) && Flag(FVISHOW8)) {
2209 len += 2;
2210 c &= 0x7f;
2211 }
2212 if (c < ' ' || c == 0x7f)
2213 len++;
2214 return len;
2215 }
2216
2217 /* Similar to x_zotc(emacs.c), but no tab weirdness */
2218 static void
x_vi_zotc(int c)2219 x_vi_zotc(int c)
2220 {
2221 if (Flag(FVISHOW8) && (c & 0x80)) {
2222 x_puts("M-");
2223 c &= 0x7f;
2224 }
2225 if (c < ' ' || c == 0x7f) {
2226 x_putc('^');
2227 c ^= '@';
2228 }
2229 x_putc(c);
2230 }
2231
2232 static void
vi_pprompt(int full)2233 vi_pprompt(int full)
2234 {
2235 pprompt(prompt + (full ? 0 : prompt_skip), prompt_trunc);
2236 }
2237
2238 static void
vi_error(void)2239 vi_error(void)
2240 {
2241 /* Beem out of any macros as soon as an error occurs */
2242 vi_macro_reset();
2243 x_putc(BEL);
2244 x_flush();
2245 }
2246
2247 static void
vi_macro_reset(void)2248 vi_macro_reset(void)
2249 {
2250 if (macro.p) {
2251 afree(macro.buf, APERM);
2252 memset((char *) ¯o, 0, sizeof(macro));
2253 }
2254 }
2255
2256 static int
isu8cont(unsigned char c)2257 isu8cont(unsigned char c)
2258 {
2259 return !Flag(FVISHOW8) && (c & (0x80 | 0x40)) == 0x80;
2260 }
2261 #endif /* VI */
2262