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