sync
[bitrig.git] / bin / ksh / vi.c
blob889b35a8e6fe212d38f83750a3ab94878f17e7ce
1 /* $OpenBSD: vi.c,v 1.26 2009/06/29 22:50:19 martynas 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 "sh.h"
13 #include <ctype.h>
14 #include <sys/stat.h> /* completion */
15 #include "edit.h"
17 #define CMDLEN 2048
18 #define Ctrl(c) (c&0x1f)
19 #define is_wordch(c) (letnum(c))
21 struct edstate {
22 int winleft;
23 char *cbuf;
24 int cbufsize;
25 int linelen;
26 int cursor;
30 static int vi_hook(int);
31 static void vi_reset(char *, size_t);
32 static int nextstate(int);
33 static int vi_insert(int);
34 static int vi_cmd(int, const char *);
35 static int domove(int, const char *, int);
36 static int redo_insert(int);
37 static void yank_range(int, int);
38 static int bracktype(int);
39 static void save_cbuf(void);
40 static void restore_cbuf(void);
41 static void edit_reset(char *, size_t);
42 static int putbuf(const char *, int, int);
43 static void del_range(int, int);
44 static int findch(int, int, int, int);
45 static int forwword(int);
46 static int backword(int);
47 static int endword(int);
48 static int Forwword(int);
49 static int Backword(int);
50 static int Endword(int);
51 static int grabhist(int, int);
52 static int grabsearch(int, int, int, char *);
53 static void redraw_line(int);
54 static void refresh(int);
55 static int outofwin(void);
56 static void rewindow(void);
57 static int newcol(int, int);
58 static void display(char *, char *, int);
59 static void ed_mov_opt(int, char *);
60 static int expand_word(int);
61 static int complete_word(int, int);
62 static int print_expansions(struct edstate *, int);
63 static int char_len(int);
64 static void x_vi_zotc(int);
65 static void vi_pprompt(int);
66 static void vi_error(void);
67 static void vi_macro_reset(void);
68 static int x_vi_putbuf(const char *, size_t);
70 #define C_ 0x1 /* a valid command that isn't a M_, E_, U_ */
71 #define M_ 0x2 /* movement command (h, l, etc.) */
72 #define E_ 0x4 /* extended command (c, d, y) */
73 #define X_ 0x8 /* long command (@, f, F, t, T, etc.) */
74 #define U_ 0x10 /* an UN-undoable command (that isn't a M_) */
75 #define B_ 0x20 /* bad command (^@) */
76 #define Z_ 0x40 /* repeat count defaults to 0 (not 1) */
77 #define S_ 0x80 /* search (/, ?) */
79 #define is_bad(c) (classify[(c)&0x7f]&B_)
80 #define is_cmd(c) (classify[(c)&0x7f]&(M_|E_|C_|U_))
81 #define is_move(c) (classify[(c)&0x7f]&M_)
82 #define is_extend(c) (classify[(c)&0x7f]&E_)
83 #define is_long(c) (classify[(c)&0x7f]&X_)
84 #define is_undoable(c) (!(classify[(c)&0x7f]&U_))
85 #define is_srch(c) (classify[(c)&0x7f]&S_)
86 #define is_zerocount(c) (classify[(c)&0x7f]&Z_)
88 const unsigned char classify[128] = {
89 /* 0 1 2 3 4 5 6 7 */
90 /* 0 ^@ ^A ^B ^C ^D ^E ^F ^G */
91 B_, 0, 0, 0, 0, C_|U_, C_|Z_, 0,
92 /* 01 ^H ^I ^J ^K ^L ^M ^N ^O */
93 M_, C_|Z_, 0, 0, C_|U_, 0, C_, 0,
94 /* 02 ^P ^Q ^R ^S ^T ^U ^V ^W */
95 C_, 0, C_|U_, 0, 0, 0, C_, 0,
96 /* 03 ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
97 C_, 0, 0, C_|Z_, 0, 0, 0, 0,
98 /* 04 <space> ! " # $ % & ' */
99 M_, 0, 0, C_, M_, M_, 0, 0,
100 /* 05 ( ) * + , - . / */
101 0, 0, C_, C_, M_, C_, 0, C_|S_,
102 /* 06 0 1 2 3 4 5 6 7 */
103 M_, 0, 0, 0, 0, 0, 0, 0,
104 /* 07 8 9 : ; < = > ? */
105 0, 0, 0, M_, 0, C_, 0, C_|S_,
106 /* 010 @ A B C D E F G */
107 C_|X_, C_, M_, C_, C_, M_, M_|X_, C_|U_|Z_,
108 /* 011 H I J K L M N O */
109 0, C_, 0, 0, 0, 0, C_|U_, 0,
110 /* 012 P Q R S T U V W */
111 C_, 0, C_, C_, M_|X_, C_, 0, M_,
112 /* 013 X Y Z [ \ ] ^ _ */
113 C_, C_|U_, 0, 0, C_|Z_, 0, M_, C_|Z_,
114 /* 014 ` a b c d e f g */
115 0, C_, M_, E_, E_, M_, M_|X_, C_|Z_,
116 /* 015 h i j k l m n o */
117 M_, C_, C_|U_, C_|U_, M_, 0, C_|U_, 0,
118 /* 016 p q r s t u v w */
119 C_, 0, X_, C_, M_|X_, C_|U_, C_|U_|Z_,M_,
120 /* 017 x y z { | } ~ ^? */
121 C_, E_|U_, 0, 0, M_|Z_, 0, C_, 0
124 #define MAXVICMD 3
125 #define SRCHLEN 40
127 #define INSERT 1
128 #define REPLACE 2
130 #define VNORMAL 0 /* command, insert or replace mode */
131 #define VARG1 1 /* digit prefix (first, eg, 5l) */
132 #define VEXTCMD 2 /* cmd + movement (eg, cl) */
133 #define VARG2 3 /* digit prefix (second, eg, 2c3l) */
134 #define VXCH 4 /* f, F, t, T, @ */
135 #define VFAIL 5 /* bad command */
136 #define VCMD 6 /* single char command (eg, X) */
137 #define VREDO 7 /* . */
138 #define VLIT 8 /* ^V */
139 #define VSEARCH 9 /* /, ? */
140 #define VVERSION 10 /* <ESC> ^V */
142 static char undocbuf[CMDLEN];
144 static struct edstate *save_edstate(struct edstate *old);
145 static void restore_edstate(struct edstate *old, struct edstate *new);
146 static void free_edstate(struct edstate *old);
148 static struct edstate ebuf;
149 static struct edstate undobuf = { 0, undocbuf, CMDLEN, 0, 0 };
151 static struct edstate *es; /* current editor state */
152 static struct edstate *undo;
154 static char ibuf[CMDLEN]; /* input buffer */
155 static int first_insert; /* set when starting in insert mode */
156 static int saved_inslen; /* saved inslen for first insert */
157 static int inslen; /* length of input buffer */
158 static int srchlen; /* length of current search pattern */
159 static char ybuf[CMDLEN]; /* yank buffer */
160 static int yanklen; /* length of yank buffer */
161 static int fsavecmd = ' '; /* last find command */
162 static int fsavech; /* character to find */
163 static char lastcmd[MAXVICMD]; /* last non-move command */
164 static int lastac; /* argcnt for lastcmd */
165 static int lastsearch = ' '; /* last search command */
166 static char srchpat[SRCHLEN]; /* last search pattern */
167 static int insert; /* non-zero in insert mode */
168 static int hnum; /* position in history */
169 static int ohnum; /* history line copied (after mod) */
170 static int hlast; /* 1 past last position in history */
171 static int modified; /* buffer has been "modified" */
172 static int state;
174 /* Information for keeping track of macros that are being expanded.
175 * The format of buf is the alias contents followed by a null byte followed
176 * by the name (letter) of the alias. The end of the buffer is marked by
177 * a double null. The name of the alias is stored so recursive macros can
178 * be detected.
180 struct macro_state {
181 unsigned char *p; /* current position in buf */
182 unsigned char *buf; /* pointer to macro(s) being expanded */
183 int len; /* how much data in buffer */
185 static struct macro_state macro;
187 enum expand_mode { NONE, EXPAND, COMPLETE, PRINT };
188 static enum expand_mode expanded = NONE;/* last input was expanded */
191 x_vi(char *buf, size_t len)
193 int c;
195 vi_reset(buf, len > CMDLEN ? CMDLEN : len);
196 vi_pprompt(1);
197 x_flush();
198 while (1) {
199 if (macro.p) {
200 c = *macro.p++;
201 /* end of current macro? */
202 if (!c) {
203 /* more macros left to finish? */
204 if (*macro.p++)
205 continue;
206 /* must be the end of all the macros */
207 vi_macro_reset();
208 c = x_getc();
210 } else
211 c = x_getc();
213 if (c == -1)
214 break;
215 if (state != VLIT) {
216 if (c == edchars.intr || c == edchars.quit) {
217 /* pretend we got an interrupt */
218 x_vi_zotc(c);
219 x_flush();
220 trapsig(c == edchars.intr ? SIGINT : SIGQUIT);
221 x_mode(false);
222 unwind(LSHELL);
223 } else if (c == edchars.eof && state != VVERSION) {
224 if (es->linelen == 0) {
225 x_vi_zotc(edchars.eof);
226 c = -1;
227 break;
229 continue;
232 if (vi_hook(c))
233 break;
234 x_flush();
237 x_putc('\r'); x_putc('\n'); x_flush();
239 if (c == -1 || len <= es->linelen)
240 return -1;
242 if (es->cbuf != buf)
243 memmove(buf, es->cbuf, es->linelen);
245 buf[es->linelen++] = '\n';
247 return es->linelen;
250 static int
251 vi_hook(int ch)
253 static char curcmd[MAXVICMD], locpat[SRCHLEN];
254 static int cmdlen, argc1, argc2;
256 switch (state) {
258 case VNORMAL:
259 if (insert != 0) {
260 if (ch == Ctrl('v')) {
261 state = VLIT;
262 ch = '^';
264 switch (vi_insert(ch)) {
265 case -1:
266 vi_error();
267 state = VNORMAL;
268 break;
269 case 0:
270 if (state == VLIT) {
271 es->cursor--;
272 refresh(0);
273 } else
274 refresh(insert != 0);
275 break;
276 case 1:
277 return 1;
279 } else {
280 if (ch == '\r' || ch == '\n')
281 return 1;
282 cmdlen = 0;
283 argc1 = 0;
284 if (ch >= '1' && ch <= '9') {
285 argc1 = ch - '0';
286 state = VARG1;
287 } else {
288 curcmd[cmdlen++] = ch;
289 state = nextstate(ch);
290 if (state == VSEARCH) {
291 save_cbuf();
292 es->cursor = 0;
293 es->linelen = 0;
294 if (ch == '/') {
295 if (putbuf("/", 1, 0) != 0)
296 return -1;
297 } else if (putbuf("?", 1, 0) != 0)
298 return -1;
299 refresh(0);
301 if (state == VVERSION) {
302 save_cbuf();
303 es->cursor = 0;
304 es->linelen = 0;
305 putbuf(ksh_version + 4,
306 strlen(ksh_version + 4), 0);
307 refresh(0);
311 break;
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(1);
320 state = VNORMAL;
321 break;
323 case VVERSION:
324 restore_cbuf();
325 state = VNORMAL;
326 refresh(0);
327 break;
329 case VARG1:
330 if (isdigit(ch))
331 argc1 = argc1 * 10 + ch - '0';
332 else {
333 curcmd[cmdlen++] = ch;
334 state = nextstate(ch);
336 break;
338 case VEXTCMD:
339 argc2 = 0;
340 if (ch >= '1' && ch <= '9') {
341 argc2 = ch - '0';
342 state = VARG2;
343 return 0;
344 } else {
345 curcmd[cmdlen++] = ch;
346 if (ch == curcmd[0])
347 state = VCMD;
348 else if (is_move(ch))
349 state = nextstate(ch);
350 else
351 state = VFAIL;
353 break;
355 case VARG2:
356 if (isdigit(ch))
357 argc2 = argc2 * 10 + ch - '0';
358 else {
359 if (argc1 == 0)
360 argc1 = argc2;
361 else
362 argc1 *= argc2;
363 curcmd[cmdlen++] = ch;
364 if (ch == curcmd[0])
365 state = VCMD;
366 else if (is_move(ch))
367 state = nextstate(ch);
368 else
369 state = VFAIL;
371 break;
373 case VXCH:
374 if (ch == Ctrl('['))
375 state = VNORMAL;
376 else {
377 curcmd[cmdlen++] = ch;
378 state = VCMD;
380 break;
382 case VSEARCH:
383 if (ch == '\r' || ch == '\n' /*|| ch == Ctrl('[')*/ ) {
384 restore_cbuf();
385 /* Repeat last search? */
386 if (srchlen == 0) {
387 if (!srchpat[0]) {
388 vi_error();
389 state = VNORMAL;
390 refresh(0);
391 return 0;
393 } else {
394 locpat[srchlen] = '\0';
395 (void) strlcpy(srchpat, locpat, sizeof srchpat);
397 state = VCMD;
398 } else if (ch == edchars.erase || ch == Ctrl('h')) {
399 if (srchlen != 0) {
400 srchlen--;
401 es->linelen -= char_len((unsigned char)locpat[srchlen]);
402 es->cursor = es->linelen;
403 refresh(0);
404 return 0;
406 restore_cbuf();
407 state = VNORMAL;
408 refresh(0);
409 } else if (ch == edchars.kill) {
410 srchlen = 0;
411 es->linelen = 1;
412 es->cursor = 1;
413 refresh(0);
414 return 0;
415 } else if (ch == edchars.werase) {
416 struct edstate new_es, *save_es;
417 int i;
418 int n = srchlen;
420 new_es.cursor = n;
421 new_es.cbuf = locpat;
423 save_es = es;
424 es = &new_es;
425 n = backword(1);
426 es = save_es;
428 for (i = srchlen; --i >= n; )
429 es->linelen -= char_len((unsigned char)locpat[i]);
430 srchlen = n;
431 es->cursor = es->linelen;
432 refresh(0);
433 return 0;
434 } else {
435 if (srchlen == SRCHLEN - 1)
436 vi_error();
437 else {
438 locpat[srchlen++] = ch;
439 if ((ch & 0x80) && Flag(FVISHOW8)) {
440 if (es->linelen + 2 > es->cbufsize)
441 vi_error();
442 es->cbuf[es->linelen++] = 'M';
443 es->cbuf[es->linelen++] = '-';
444 ch &= 0x7f;
446 if (ch < ' ' || ch == 0x7f) {
447 if (es->linelen + 2 > es->cbufsize)
448 vi_error();
449 es->cbuf[es->linelen++] = '^';
450 es->cbuf[es->linelen++] = ch ^ '@';
451 } else {
452 if (es->linelen >= es->cbufsize)
453 vi_error();
454 es->cbuf[es->linelen++] = ch;
456 es->cursor = es->linelen;
457 refresh(0);
459 return 0;
461 break;
464 switch (state) {
465 case VCMD:
466 state = VNORMAL;
467 switch (vi_cmd(argc1, curcmd)) {
468 case -1:
469 vi_error();
470 refresh(0);
471 break;
472 case 0:
473 if (insert != 0)
474 inslen = 0;
475 refresh(insert != 0);
476 break;
477 case 1:
478 refresh(0);
479 return 1;
480 case 2:
481 /* back from a 'v' command - don't redraw the screen */
482 return 1;
484 break;
486 case VREDO:
487 state = VNORMAL;
488 if (argc1 != 0)
489 lastac = argc1;
490 switch (vi_cmd(lastac, lastcmd)) {
491 case -1:
492 vi_error();
493 refresh(0);
494 break;
495 case 0:
496 if (insert != 0) {
497 if (lastcmd[0] == 's' || lastcmd[0] == 'c' ||
498 lastcmd[0] == 'C') {
499 if (redo_insert(1) != 0)
500 vi_error();
501 } else {
502 if (redo_insert(lastac) != 0)
503 vi_error();
506 refresh(0);
507 break;
508 case 1:
509 refresh(0);
510 return 1;
511 case 2:
512 /* back from a 'v' command - can't happen */
513 break;
515 break;
517 case VFAIL:
518 state = VNORMAL;
519 vi_error();
520 break;
522 return 0;
525 static void
526 vi_reset(char *buf, size_t len)
528 state = VNORMAL;
529 ohnum = hnum = hlast = histnum(-1) + 1;
530 insert = INSERT;
531 saved_inslen = inslen;
532 first_insert = 1;
533 inslen = 0;
534 modified = 1;
535 vi_macro_reset();
536 edit_reset(buf, len);
539 static int
540 nextstate(int ch)
542 if (is_extend(ch))
543 return VEXTCMD;
544 else if (is_srch(ch))
545 return VSEARCH;
546 else if (is_long(ch))
547 return VXCH;
548 else if (ch == '.')
549 return VREDO;
550 else if (ch == Ctrl('v'))
551 return VVERSION;
552 else if (is_cmd(ch))
553 return VCMD;
554 else
555 return VFAIL;
558 static int
559 vi_insert(int ch)
561 int tcursor;
563 if (ch == edchars.erase || ch == Ctrl('h')) {
564 if (insert == REPLACE) {
565 if (es->cursor == undo->cursor) {
566 vi_error();
567 return 0;
569 if (inslen > 0)
570 inslen--;
571 es->cursor--;
572 if (es->cursor >= undo->linelen)
573 es->linelen--;
574 else
575 es->cbuf[es->cursor] = undo->cbuf[es->cursor];
576 } else {
577 if (es->cursor == 0) {
578 /* x_putc(BEL); no annoying bell here */
579 return 0;
581 if (inslen > 0)
582 inslen--;
583 es->cursor--;
584 es->linelen--;
585 memmove(&es->cbuf[es->cursor], &es->cbuf[es->cursor+1],
586 es->linelen - es->cursor + 1);
588 expanded = NONE;
589 return 0;
591 if (ch == edchars.kill) {
592 if (es->cursor != 0) {
593 inslen = 0;
594 memmove(es->cbuf, &es->cbuf[es->cursor],
595 es->linelen - es->cursor);
596 es->linelen -= es->cursor;
597 es->cursor = 0;
599 expanded = NONE;
600 return 0;
602 if (ch == edchars.werase) {
603 if (es->cursor != 0) {
604 tcursor = backword(1);
605 memmove(&es->cbuf[tcursor], &es->cbuf[es->cursor],
606 es->linelen - es->cursor);
607 es->linelen -= es->cursor - tcursor;
608 if (inslen < es->cursor - tcursor)
609 inslen = 0;
610 else
611 inslen -= es->cursor - tcursor;
612 es->cursor = tcursor;
614 expanded = NONE;
615 return 0;
617 /* If any chars are entered before escape, trash the saved insert
618 * buffer (if user inserts & deletes char, ibuf gets trashed and
619 * we don't want to use it)
621 if (first_insert && ch != Ctrl('['))
622 saved_inslen = 0;
623 switch (ch) {
624 case '\0':
625 return -1;
627 case '\r':
628 case '\n':
629 return 1;
631 case Ctrl('['):
632 expanded = NONE;
633 if (first_insert) {
634 first_insert = 0;
635 if (inslen == 0) {
636 inslen = saved_inslen;
637 return redo_insert(0);
639 lastcmd[0] = 'a';
640 lastac = 1;
642 if (lastcmd[0] == 's' || lastcmd[0] == 'c' ||
643 lastcmd[0] == 'C')
644 return redo_insert(0);
645 else
646 return redo_insert(lastac - 1);
648 /* { Begin nonstandard vi commands */
649 case Ctrl('x'):
650 expand_word(0);
651 break;
653 case Ctrl('f'):
654 complete_word(0, 0);
655 break;
657 case Ctrl('e'):
658 print_expansions(es, 0);
659 break;
661 case Ctrl('i'):
662 if (Flag(FVITABCOMPLETE)) {
663 complete_word(0, 0);
664 break;
666 /* FALLTHROUGH */
667 /* End nonstandard vi commands } */
669 default:
670 if (es->linelen >= es->cbufsize - 1)
671 return -1;
672 ibuf[inslen++] = ch;
673 if (insert == INSERT) {
674 memmove(&es->cbuf[es->cursor+1], &es->cbuf[es->cursor],
675 es->linelen - es->cursor);
676 es->linelen++;
678 es->cbuf[es->cursor++] = ch;
679 if (insert == REPLACE && es->cursor > es->linelen)
680 es->linelen++;
681 expanded = NONE;
683 return 0;
686 static int
687 vi_cmd(int argcnt, const char *cmd)
689 int ncursor;
690 int cur, c1, c2, c3 = 0;
691 int any;
692 struct edstate *t;
694 if (argcnt == 0 && !is_zerocount(*cmd))
695 argcnt = 1;
697 if (is_move(*cmd)) {
698 if ((cur = domove(argcnt, cmd, 0)) >= 0) {
699 if (cur == es->linelen && cur != 0)
700 cur--;
701 es->cursor = cur;
702 } else
703 return -1;
704 } else {
705 /* Don't save state in middle of macro.. */
706 if (is_undoable(*cmd) && !macro.p) {
707 undo->winleft = es->winleft;
708 memmove(undo->cbuf, es->cbuf, es->linelen);
709 undo->linelen = es->linelen;
710 undo->cursor = es->cursor;
711 lastac = argcnt;
712 memmove(lastcmd, cmd, MAXVICMD);
714 switch (*cmd) {
716 case Ctrl('l'):
717 case Ctrl('r'):
718 redraw_line(1);
719 break;
721 case '@':
723 static char alias[] = "_\0";
724 struct tbl *ap;
725 int olen, nlen;
726 char *p, *nbuf;
728 /* lookup letter in alias list... */
729 alias[1] = cmd[1];
730 ap = ktsearch(&aliases, alias, hash(alias));
731 if (!cmd[1] || !ap || !(ap->flag & ISSET))
732 return -1;
733 /* check if this is a recursive call... */
734 if ((p = (char *) macro.p))
735 while ((p = strchr(p, '\0')) && p[1])
736 if (*++p == cmd[1])
737 return -1;
738 /* insert alias into macro buffer */
739 nlen = strlen(ap->val.s) + 1;
740 olen = !macro.p ? 2 :
741 macro.len - (macro.p - macro.buf);
742 nbuf = alloc(nlen + 1 + olen, APERM);
743 memcpy(nbuf, ap->val.s, nlen);
744 nbuf[nlen++] = cmd[1];
745 if (macro.p) {
746 memcpy(nbuf + nlen, macro.p, olen);
747 afree(macro.buf, APERM);
748 nlen += olen;
749 } else {
750 nbuf[nlen++] = '\0';
751 nbuf[nlen++] = '\0';
753 macro.p = macro.buf = (unsigned char *) nbuf;
754 macro.len = nlen;
756 break;
758 case 'a':
759 modified = 1; hnum = hlast;
760 if (es->linelen != 0)
761 es->cursor++;
762 insert = INSERT;
763 break;
765 case 'A':
766 modified = 1; hnum = hlast;
767 del_range(0, 0);
768 es->cursor = es->linelen;
769 insert = INSERT;
770 break;
772 case 'S':
773 es->cursor = domove(1, "^", 1);
774 del_range(es->cursor, es->linelen);
775 modified = 1; hnum = hlast;
776 insert = INSERT;
777 break;
779 case 'Y':
780 cmd = "y$";
781 /* ahhhhhh... */
782 case 'c':
783 case 'd':
784 case 'y':
785 if (*cmd == cmd[1]) {
786 c1 = *cmd == 'c' ? domove(1, "^", 1) : 0;
787 c2 = es->linelen;
788 } else if (!is_move(cmd[1]))
789 return -1;
790 else {
791 if ((ncursor = domove(argcnt, &cmd[1], 1)) < 0)
792 return -1;
793 if (*cmd == 'c' &&
794 (cmd[1]=='w' || cmd[1]=='W') &&
795 !isspace(es->cbuf[es->cursor])) {
796 while (isspace(es->cbuf[--ncursor]))
798 ncursor++;
800 if (ncursor > es->cursor) {
801 c1 = es->cursor;
802 c2 = ncursor;
803 } else {
804 c1 = ncursor;
805 c2 = es->cursor;
806 if (cmd[1] == '%')
807 c2++;
810 if (*cmd != 'c' && c1 != c2)
811 yank_range(c1, c2);
812 if (*cmd != 'y') {
813 del_range(c1, c2);
814 es->cursor = c1;
816 if (*cmd == 'c') {
817 modified = 1; hnum = hlast;
818 insert = INSERT;
820 break;
822 case 'p':
823 modified = 1; hnum = hlast;
824 if (es->linelen != 0)
825 es->cursor++;
826 while (putbuf(ybuf, yanklen, 0) == 0 && --argcnt > 0)
828 if (es->cursor != 0)
829 es->cursor--;
830 if (argcnt != 0)
831 return -1;
832 break;
834 case 'P':
835 modified = 1; hnum = hlast;
836 any = 0;
837 while (putbuf(ybuf, yanklen, 0) == 0 && --argcnt > 0)
838 any = 1;
839 if (any && es->cursor != 0)
840 es->cursor--;
841 if (argcnt != 0)
842 return -1;
843 break;
845 case 'C':
846 modified = 1; hnum = hlast;
847 del_range(es->cursor, es->linelen);
848 insert = INSERT;
849 break;
851 case 'D':
852 yank_range(es->cursor, es->linelen);
853 del_range(es->cursor, es->linelen);
854 if (es->cursor != 0)
855 es->cursor--;
856 break;
858 case 'g':
859 if (!argcnt)
860 argcnt = hlast;
861 /* FALLTHROUGH */
862 case 'G':
863 if (!argcnt)
864 argcnt = 1;
865 else
866 argcnt = hlast - (source->line - argcnt);
867 if (grabhist(modified, argcnt - 1) < 0)
868 return -1;
869 else {
870 modified = 0;
871 hnum = argcnt - 1;
873 break;
875 case 'i':
876 modified = 1; hnum = hlast;
877 insert = INSERT;
878 break;
880 case 'I':
881 modified = 1; hnum = hlast;
882 es->cursor = domove(1, "^", 1);
883 insert = INSERT;
884 break;
886 case 'j':
887 case '+':
888 case Ctrl('n'):
889 if (grabhist(modified, hnum + argcnt) < 0)
890 return -1;
891 else {
892 modified = 0;
893 hnum += argcnt;
895 break;
897 case 'k':
898 case '-':
899 case Ctrl('p'):
900 if (grabhist(modified, hnum - argcnt) < 0)
901 return -1;
902 else {
903 modified = 0;
904 hnum -= argcnt;
906 break;
908 case 'r':
909 if (es->linelen == 0)
910 return -1;
911 modified = 1; hnum = hlast;
912 if (cmd[1] == 0)
913 vi_error();
914 else {
915 int n;
917 if (es->cursor + argcnt > es->linelen)
918 return -1;
919 for (n = 0; n < argcnt; ++n)
920 es->cbuf[es->cursor + n] = cmd[1];
921 es->cursor += n - 1;
923 break;
925 case 'R':
926 modified = 1; hnum = hlast;
927 insert = REPLACE;
928 break;
930 case 's':
931 if (es->linelen == 0)
932 return -1;
933 modified = 1; hnum = hlast;
934 if (es->cursor + argcnt > es->linelen)
935 argcnt = es->linelen - es->cursor;
936 del_range(es->cursor, es->cursor + argcnt);
937 insert = INSERT;
938 break;
940 case 'v':
941 if (es->linelen == 0 && argcnt == 0)
942 return -1;
943 if (!argcnt) {
944 if (modified) {
945 es->cbuf[es->linelen] = '\0';
946 source->line++;
947 histsave(source->line, es->cbuf, 1);
948 } else
949 argcnt = source->line + 1
950 - (hlast - hnum);
952 shf_snprintf(es->cbuf, es->cbufsize,
953 argcnt ? "%s %d" : "%s",
954 "fc -e ${VISUAL:-${EDITOR:-vi}} --",
955 argcnt);
956 es->linelen = strlen(es->cbuf);
957 return 2;
959 case 'x':
960 if (es->linelen == 0)
961 return -1;
962 modified = 1; hnum = hlast;
963 if (es->cursor + argcnt > es->linelen)
964 argcnt = es->linelen - es->cursor;
965 yank_range(es->cursor, es->cursor + argcnt);
966 del_range(es->cursor, es->cursor + argcnt);
967 break;
969 case 'X':
970 if (es->cursor > 0) {
971 modified = 1; hnum = hlast;
972 if (es->cursor < argcnt)
973 argcnt = es->cursor;
974 yank_range(es->cursor - argcnt, es->cursor);
975 del_range(es->cursor - argcnt, es->cursor);
976 es->cursor -= argcnt;
977 } else
978 return -1;
979 break;
981 case 'u':
982 t = es;
983 es = undo;
984 undo = t;
985 break;
987 case 'U':
988 if (!modified)
989 return -1;
990 if (grabhist(modified, ohnum) < 0)
991 return -1;
992 modified = 0;
993 hnum = ohnum;
994 break;
996 case '?':
997 if (hnum == hlast)
998 hnum = -1;
999 /* ahhh */
1000 case '/':
1001 c3 = 1;
1002 srchlen = 0;
1003 lastsearch = *cmd;
1004 /* FALLTHROUGH */
1005 case 'n':
1006 case 'N':
1007 if (lastsearch == ' ')
1008 return -1;
1009 if (lastsearch == '?')
1010 c1 = 1;
1011 else
1012 c1 = 0;
1013 if (*cmd == 'N')
1014 c1 = !c1;
1015 if ((c2 = grabsearch(modified, hnum,
1016 c1, srchpat)) < 0) {
1017 if (c3) {
1018 restore_cbuf();
1019 refresh(0);
1021 return -1;
1022 } else {
1023 modified = 0;
1024 hnum = c2;
1025 ohnum = hnum;
1027 break;
1028 case '_': {
1029 int inspace;
1030 char *p, *sp;
1032 if (histnum(-1) < 0)
1033 return -1;
1034 p = *histpos();
1035 #define issp(c) (isspace((c)) || (c) == '\n')
1036 if (argcnt) {
1037 while (*p && issp(*p))
1038 p++;
1039 while (*p && --argcnt) {
1040 while (*p && !issp(*p))
1041 p++;
1042 while (*p && issp(*p))
1043 p++;
1045 if (!*p)
1046 return -1;
1047 sp = p;
1048 } else {
1049 sp = p;
1050 inspace = 0;
1051 while (*p) {
1052 if (issp(*p))
1053 inspace = 1;
1054 else if (inspace) {
1055 inspace = 0;
1056 sp = p;
1058 p++;
1060 p = sp;
1062 modified = 1; hnum = hlast;
1063 if (es->cursor != es->linelen)
1064 es->cursor++;
1065 while (*p && !issp(*p)) {
1066 argcnt++;
1067 p++;
1069 if (putbuf(space, 1, 0) != 0)
1070 argcnt = -1;
1071 else if (putbuf(sp, argcnt, 0) != 0)
1072 argcnt = -1;
1073 if (argcnt < 0) {
1074 if (es->cursor != 0)
1075 es->cursor--;
1076 return -1;
1078 insert = INSERT;
1080 break;
1082 case '~': {
1083 char *p;
1084 int i;
1086 if (es->linelen == 0)
1087 return -1;
1088 for (i = 0; i < argcnt; i++) {
1089 p = &es->cbuf[es->cursor];
1090 if (islower(*p)) {
1091 modified = 1; hnum = hlast;
1092 *p = toupper(*p);
1093 } else if (isupper(*p)) {
1094 modified = 1; hnum = hlast;
1095 *p = tolower(*p);
1097 if (es->cursor < es->linelen - 1)
1098 es->cursor++;
1100 break;
1103 case '#':
1105 int ret = x_do_comment(es->cbuf, es->cbufsize,
1106 &es->linelen);
1107 if (ret >= 0)
1108 es->cursor = 0;
1109 return ret;
1112 case '=': /* at&t ksh */
1113 case Ctrl('e'): /* Nonstandard vi/ksh */
1114 print_expansions(es, 1);
1115 break;
1118 case Ctrl('i'): /* Nonstandard vi/ksh */
1119 if (!Flag(FVITABCOMPLETE))
1120 return -1;
1121 complete_word(1, argcnt);
1122 break;
1124 case Ctrl('['): /* some annoying at&t ksh's */
1125 if (!Flag(FVIESCCOMPLETE))
1126 return -1;
1127 case '\\': /* at&t ksh */
1128 case Ctrl('f'): /* Nonstandard vi/ksh */
1129 complete_word(1, argcnt);
1130 break;
1133 case '*': /* at&t ksh */
1134 case Ctrl('x'): /* Nonstandard vi/ksh */
1135 expand_word(1);
1136 break;
1138 if (insert == 0 && es->cursor != 0 && es->cursor >= es->linelen)
1139 es->cursor--;
1141 return 0;
1144 static int
1145 domove(int argcnt, const char *cmd, int sub)
1147 int bcount, i = 0, t;
1148 int ncursor = 0;
1150 switch (*cmd) {
1152 case 'b':
1153 if (!sub && es->cursor == 0)
1154 return -1;
1155 ncursor = backword(argcnt);
1156 break;
1158 case 'B':
1159 if (!sub && es->cursor == 0)
1160 return -1;
1161 ncursor = Backword(argcnt);
1162 break;
1164 case 'e':
1165 if (!sub && es->cursor + 1 >= es->linelen)
1166 return -1;
1167 ncursor = endword(argcnt);
1168 if (sub && ncursor < es->linelen)
1169 ncursor++;
1170 break;
1172 case 'E':
1173 if (!sub && es->cursor + 1 >= es->linelen)
1174 return -1;
1175 ncursor = Endword(argcnt);
1176 if (sub && ncursor < es->linelen)
1177 ncursor++;
1178 break;
1180 case 'f':
1181 case 'F':
1182 case 't':
1183 case 'T':
1184 fsavecmd = *cmd;
1185 fsavech = cmd[1];
1186 /* drop through */
1188 case ',':
1189 case ';':
1190 if (fsavecmd == ' ')
1191 return -1;
1192 i = fsavecmd == 'f' || fsavecmd == 'F';
1193 t = fsavecmd > 'a';
1194 if (*cmd == ',')
1195 t = !t;
1196 if ((ncursor = findch(fsavech, argcnt, t, i)) < 0)
1197 return -1;
1198 if (sub && t)
1199 ncursor++;
1200 break;
1202 case 'h':
1203 case Ctrl('h'):
1204 if (!sub && es->cursor == 0)
1205 return -1;
1206 ncursor = es->cursor - argcnt;
1207 if (ncursor < 0)
1208 ncursor = 0;
1209 break;
1211 case ' ':
1212 case 'l':
1213 if (!sub && es->cursor + 1 >= es->linelen)
1214 return -1;
1215 if (es->linelen != 0) {
1216 ncursor = es->cursor + argcnt;
1217 if (ncursor > es->linelen)
1218 ncursor = es->linelen;
1220 break;
1222 case 'w':
1223 if (!sub && es->cursor + 1 >= es->linelen)
1224 return -1;
1225 ncursor = forwword(argcnt);
1226 break;
1228 case 'W':
1229 if (!sub && es->cursor + 1 >= es->linelen)
1230 return -1;
1231 ncursor = Forwword(argcnt);
1232 break;
1234 case '0':
1235 ncursor = 0;
1236 break;
1238 case '^':
1239 ncursor = 0;
1240 while (ncursor < es->linelen - 1 && isspace(es->cbuf[ncursor]))
1241 ncursor++;
1242 break;
1244 case '|':
1245 ncursor = argcnt;
1246 if (ncursor > es->linelen)
1247 ncursor = es->linelen;
1248 if (ncursor)
1249 ncursor--;
1250 break;
1252 case '$':
1253 if (es->linelen != 0)
1254 ncursor = es->linelen;
1255 else
1256 ncursor = 0;
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 es->cursor--;
1299 insert = 0;
1300 return 0;
1303 static void
1304 yank_range(int a, int b)
1306 yanklen = b - a;
1307 if (yanklen != 0)
1308 memmove(ybuf, &es->cbuf[a], yanklen);
1311 static int
1312 bracktype(int ch)
1314 switch (ch) {
1316 case '(':
1317 return 1;
1319 case '[':
1320 return 2;
1322 case '{':
1323 return 3;
1325 case ')':
1326 return -1;
1328 case ']':
1329 return -2;
1331 case '}':
1332 return -3;
1334 default:
1335 return 0;
1340 * Non user interface editor routines below here
1343 static int cur_col; /* current column on line */
1344 static int pwidth; /* width of prompt */
1345 static int prompt_trunc; /* how much of prompt to truncate */
1346 static int prompt_skip; /* how much of prompt to skip */
1347 static int winwidth; /* width of window */
1348 static char *wbuf[2]; /* window buffers */
1349 static int wbuf_len; /* length of window buffers (x_cols-3)*/
1350 static int win; /* window buffer in use */
1351 static char morec; /* more character at right of window */
1352 static int lastref; /* argument to last refresh() */
1353 static char holdbuf[CMDLEN]; /* 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 = (struct edstate *)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((char *)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 lastref = 1;
1441 holdlen = 0;
1445 * this is used for calling x_escape() in complete_word()
1447 static int
1448 x_vi_putbuf(const char *s, size_t len)
1450 return putbuf(s, len, 0);
1453 static int
1454 putbuf(const char *buf, int len, int repl)
1456 if (len == 0)
1457 return 0;
1458 if (repl) {
1459 if (es->cursor + len >= es->cbufsize)
1460 return -1;
1461 if (es->cursor + len > es->linelen)
1462 es->linelen = es->cursor + len;
1463 } else {
1464 if (es->linelen + len >= es->cbufsize)
1465 return -1;
1466 memmove(&es->cbuf[es->cursor + len], &es->cbuf[es->cursor],
1467 es->linelen - es->cursor);
1468 es->linelen += len;
1470 memmove(&es->cbuf[es->cursor], buf, len);
1471 es->cursor += len;
1472 return 0;
1475 static void
1476 del_range(int a, int b)
1478 if (es->linelen != b)
1479 memmove(&es->cbuf[a], &es->cbuf[b], es->linelen - b);
1480 es->linelen -= b - a;
1483 static int
1484 findch(int ch, int cnt, int forw, int incl)
1486 int ncursor;
1488 if (es->linelen == 0)
1489 return -1;
1490 ncursor = es->cursor;
1491 while (cnt--) {
1492 do {
1493 if (forw) {
1494 if (++ncursor == es->linelen)
1495 return -1;
1496 } else {
1497 if (--ncursor < 0)
1498 return -1;
1500 } while (es->cbuf[ncursor] != ch);
1502 if (!incl) {
1503 if (forw)
1504 ncursor--;
1505 else
1506 ncursor++;
1508 return ncursor;
1511 static int
1512 forwword(int argcnt)
1514 int ncursor;
1516 ncursor = es->cursor;
1517 while (ncursor < es->linelen && argcnt--) {
1518 if (is_wordch(es->cbuf[ncursor]))
1519 while (is_wordch(es->cbuf[ncursor]) &&
1520 ncursor < es->linelen)
1521 ncursor++;
1522 else if (!isspace(es->cbuf[ncursor]))
1523 while (!is_wordch(es->cbuf[ncursor]) &&
1524 !isspace(es->cbuf[ncursor]) &&
1525 ncursor < es->linelen)
1526 ncursor++;
1527 while (isspace(es->cbuf[ncursor]) && ncursor < es->linelen)
1528 ncursor++;
1530 return ncursor;
1533 static int
1534 backword(int argcnt)
1536 int ncursor;
1538 ncursor = es->cursor;
1539 while (ncursor > 0 && argcnt--) {
1540 while (--ncursor > 0 && isspace(es->cbuf[ncursor]))
1542 if (ncursor > 0) {
1543 if (is_wordch(es->cbuf[ncursor]))
1544 while (--ncursor >= 0 &&
1545 is_wordch(es->cbuf[ncursor]))
1547 else
1548 while (--ncursor >= 0 &&
1549 !is_wordch(es->cbuf[ncursor]) &&
1550 !isspace(es->cbuf[ncursor]))
1552 ncursor++;
1555 return ncursor;
1558 static int
1559 endword(int argcnt)
1561 int ncursor;
1563 ncursor = es->cursor;
1564 while (ncursor < es->linelen && argcnt--) {
1565 while (++ncursor < es->linelen - 1 &&
1566 isspace(es->cbuf[ncursor]))
1568 if (ncursor < es->linelen - 1) {
1569 if (is_wordch(es->cbuf[ncursor]))
1570 while (++ncursor < es->linelen &&
1571 is_wordch(es->cbuf[ncursor]))
1573 else
1574 while (++ncursor < es->linelen &&
1575 !is_wordch(es->cbuf[ncursor]) &&
1576 !isspace(es->cbuf[ncursor]))
1578 ncursor--;
1581 return ncursor;
1584 static int
1585 Forwword(int argcnt)
1587 int ncursor;
1589 ncursor = es->cursor;
1590 while (ncursor < es->linelen && argcnt--) {
1591 while (!isspace(es->cbuf[ncursor]) && ncursor < es->linelen)
1592 ncursor++;
1593 while (isspace(es->cbuf[ncursor]) && ncursor < es->linelen)
1594 ncursor++;
1596 return ncursor;
1599 static int
1600 Backword(int argcnt)
1602 int ncursor;
1604 ncursor = es->cursor;
1605 while (ncursor > 0 && argcnt--) {
1606 while (--ncursor >= 0 && isspace(es->cbuf[ncursor]))
1608 while (ncursor >= 0 && !isspace(es->cbuf[ncursor]))
1609 ncursor--;
1610 ncursor++;
1612 return ncursor;
1615 static int
1616 Endword(int argcnt)
1618 int ncursor;
1620 ncursor = es->cursor;
1621 while (ncursor < es->linelen - 1 && argcnt--) {
1622 while (++ncursor < es->linelen - 1 &&
1623 isspace(es->cbuf[ncursor]))
1625 if (ncursor < es->linelen - 1) {
1626 while (++ncursor < es->linelen &&
1627 !isspace(es->cbuf[ncursor]))
1629 ncursor--;
1632 return ncursor;
1635 static int
1636 grabhist(int save, int n)
1638 char *hptr;
1640 if (n < 0 || n > hlast)
1641 return -1;
1642 if (n == hlast) {
1643 restore_cbuf();
1644 ohnum = n;
1645 return 0;
1647 (void) histnum(n);
1648 if ((hptr = *histpos()) == NULL) {
1649 internal_errorf(0, "grabhist: bad history array");
1650 return -1;
1652 if (save)
1653 save_cbuf();
1654 if ((es->linelen = strlen(hptr)) >= es->cbufsize)
1655 es->linelen = es->cbufsize - 1;
1656 memmove(es->cbuf, hptr, es->linelen);
1657 es->cursor = 0;
1658 ohnum = n;
1659 return 0;
1662 static int
1663 grabsearch(int save, int start, int fwd, char *pat)
1665 char *hptr;
1666 int hist;
1667 int anchored;
1669 if ((start == 0 && fwd == 0) || (start >= hlast-1 && fwd == 1))
1670 return -1;
1671 if (fwd)
1672 start++;
1673 else
1674 start--;
1675 anchored = *pat == '^' ? (++pat, 1) : 0;
1676 if ((hist = findhist(start, fwd, pat, anchored)) < 0) {
1677 /* if (start != 0 && fwd && match(holdbuf, pat) >= 0) { */
1678 /* XXX should strcmp be strncmp? */
1679 if (start != 0 && fwd && strcmp(holdbuf, pat) >= 0) {
1680 restore_cbuf();
1681 return 0;
1682 } else
1683 return -1;
1685 if (save)
1686 save_cbuf();
1687 histnum(hist);
1688 hptr = *histpos();
1689 if ((es->linelen = strlen(hptr)) >= es->cbufsize)
1690 es->linelen = es->cbufsize - 1;
1691 memmove(es->cbuf, hptr, es->linelen);
1692 es->cursor = 0;
1693 return hist;
1696 static void
1697 redraw_line(int newline)
1699 (void) memset(wbuf[win], ' ', wbuf_len);
1700 if (newline) {
1701 x_putc('\r');
1702 x_putc('\n');
1704 vi_pprompt(0);
1705 cur_col = pwidth;
1706 morec = ' ';
1709 static void
1710 refresh(int leftside)
1712 if (leftside < 0)
1713 leftside = lastref;
1714 else
1715 lastref = leftside;
1716 if (outofwin())
1717 rewindow();
1718 display(wbuf[1 - win], wbuf[win], leftside);
1719 win = 1 - win;
1722 static int
1723 outofwin(void)
1725 int cur, col;
1727 if (es->cursor < es->winleft)
1728 return 1;
1729 col = 0;
1730 cur = es->winleft;
1731 while (cur < es->cursor)
1732 col = newcol((unsigned char) es->cbuf[cur++], col);
1733 if (col >= winwidth)
1734 return 1;
1735 return 0;
1738 static void
1739 rewindow(void)
1741 int tcur, tcol;
1742 int holdcur1, holdcol1;
1743 int holdcur2, holdcol2;
1745 holdcur1 = holdcur2 = tcur = 0;
1746 holdcol1 = holdcol2 = tcol = 0;
1747 while (tcur < es->cursor) {
1748 if (tcol - holdcol2 > winwidth / 2) {
1749 holdcur1 = holdcur2;
1750 holdcol1 = holdcol2;
1751 holdcur2 = tcur;
1752 holdcol2 = tcol;
1754 tcol = newcol((unsigned char) es->cbuf[tcur++], tcol);
1756 while (tcol - holdcol1 > winwidth / 2)
1757 holdcol1 = newcol((unsigned char) es->cbuf[holdcur1++],
1758 holdcol1);
1759 es->winleft = holdcur1;
1762 static int
1763 newcol(int ch, int col)
1765 if (ch == '\t')
1766 return (col | 7) + 1;
1767 return col + char_len(ch);
1770 static void
1771 display(char *wb1, char *wb2, int leftside)
1773 unsigned char ch;
1774 char *twb1, *twb2, mc;
1775 int cur, col, cnt;
1776 int ncol = 0;
1777 int moreright;
1779 col = 0;
1780 cur = es->winleft;
1781 moreright = 0;
1782 twb1 = wb1;
1783 while (col < winwidth && cur < es->linelen) {
1784 if (cur == es->cursor && leftside)
1785 ncol = col + pwidth;
1786 if ((ch = es->cbuf[cur]) == '\t') {
1787 do {
1788 *twb1++ = ' ';
1789 } while (++col < winwidth && (col & 7) != 0);
1790 } else {
1791 if ((ch & 0x80) && Flag(FVISHOW8)) {
1792 *twb1++ = 'M';
1793 if (++col < winwidth) {
1794 *twb1++ = '-';
1795 col++;
1797 ch &= 0x7f;
1799 if (col < winwidth) {
1800 if (ch < ' ' || ch == 0x7f) {
1801 *twb1++ = '^';
1802 if (++col < winwidth) {
1803 *twb1++ = ch ^ '@';
1804 col++;
1806 } else {
1807 *twb1++ = ch;
1808 col++;
1812 if (cur == es->cursor && !leftside)
1813 ncol = col + pwidth - 1;
1814 cur++;
1816 if (cur == es->cursor)
1817 ncol = col + pwidth;
1818 if (col < winwidth) {
1819 while (col < winwidth) {
1820 *twb1++ = ' ';
1821 col++;
1823 } else
1824 moreright++;
1825 *twb1 = ' ';
1827 col = pwidth;
1828 cnt = winwidth;
1829 twb1 = wb1;
1830 twb2 = wb2;
1831 while (cnt--) {
1832 if (*twb1 != *twb2) {
1833 if (cur_col != col)
1834 ed_mov_opt(col, wb1);
1835 x_putc(*twb1);
1836 cur_col++;
1838 twb1++;
1839 twb2++;
1840 col++;
1842 if (es->winleft > 0 && moreright)
1843 /* POSIX says to use * for this but that is a globbing
1844 * character and may confuse people; + is more innocuous
1846 mc = '+';
1847 else if (es->winleft > 0)
1848 mc = '<';
1849 else if (moreright)
1850 mc = '>';
1851 else
1852 mc = ' ';
1853 if (mc != morec) {
1854 ed_mov_opt(pwidth + winwidth + 1, wb1);
1855 x_putc(mc);
1856 cur_col++;
1857 morec = mc;
1859 if (cur_col != ncol)
1860 ed_mov_opt(ncol, wb1);
1863 static void
1864 ed_mov_opt(int col, char *wb)
1866 if (col < cur_col) {
1867 if (col + 1 < cur_col - col) {
1868 x_putc('\r');
1869 vi_pprompt(0);
1870 cur_col = pwidth;
1871 while (cur_col++ < col)
1872 x_putc(*wb++);
1873 } else {
1874 while (cur_col-- > col)
1875 x_putc('\b');
1877 } else {
1878 wb = &wb[cur_col - pwidth];
1879 while (cur_col++ < col)
1880 x_putc(*wb++);
1882 cur_col = col;
1886 /* replace word with all expansions (ie, expand word*) */
1887 static int
1888 expand_word(int command)
1890 static struct edstate *buf;
1891 int rval = 0;
1892 int nwords;
1893 int start, end;
1894 char **words;
1895 int i;
1897 /* Undo previous expansion */
1898 if (command == 0 && expanded == EXPAND && buf) {
1899 restore_edstate(es, buf);
1900 buf = 0;
1901 expanded = NONE;
1902 return 0;
1904 if (buf) {
1905 free_edstate(buf);
1906 buf = 0;
1909 nwords = x_cf_glob(XCF_COMMAND_FILE|XCF_FULLPATH,
1910 es->cbuf, es->linelen, es->cursor,
1911 &start, &end, &words, (int *) 0);
1912 if (nwords == 0) {
1913 vi_error();
1914 return -1;
1917 buf = save_edstate(es);
1918 expanded = EXPAND;
1919 del_range(start, end);
1920 es->cursor = start;
1921 for (i = 0; i < nwords; ) {
1922 if (x_escape(words[i], strlen(words[i]), x_vi_putbuf) != 0) {
1923 rval = -1;
1924 break;
1926 if (++i < nwords && putbuf(space, 1, 0) != 0) {
1927 rval = -1;
1928 break;
1931 i = buf->cursor - end;
1932 if (rval == 0 && i > 0)
1933 es->cursor += i;
1934 modified = 1; hnum = hlast;
1935 insert = INSERT;
1936 lastac = 0;
1937 refresh(0);
1938 return rval;
1941 static int
1942 complete_word(int command, int count)
1944 static struct edstate *buf;
1945 int rval = 0;
1946 int nwords;
1947 int start, end;
1948 char **words;
1949 char *match;
1950 int match_len;
1951 int is_unique;
1952 int is_command;
1954 /* Undo previous completion */
1955 if (command == 0 && expanded == COMPLETE && buf) {
1956 print_expansions(buf, 0);
1957 expanded = PRINT;
1958 return 0;
1960 if (command == 0 && expanded == PRINT && buf) {
1961 restore_edstate(es, buf);
1962 buf = 0;
1963 expanded = NONE;
1964 return 0;
1966 if (buf) {
1967 free_edstate(buf);
1968 buf = 0;
1971 /* XCF_FULLPATH for count 'cause the menu printed by print_expansions()
1972 * was done this way.
1974 nwords = x_cf_glob(XCF_COMMAND_FILE | (count ? XCF_FULLPATH : 0),
1975 es->cbuf, es->linelen, es->cursor,
1976 &start, &end, &words, &is_command);
1977 if (nwords == 0) {
1978 vi_error();
1979 return -1;
1981 if (count) {
1982 int i;
1984 count--;
1985 if (count >= nwords) {
1986 vi_error();
1987 x_print_expansions(nwords, words, is_command);
1988 x_free_words(nwords, words);
1989 redraw_line(0);
1990 return -1;
1993 * Expand the count'th word to its basename
1995 if (is_command) {
1996 match = words[count] +
1997 x_basename(words[count], (char *) 0);
1998 /* If more than one possible match, use full path */
1999 for (i = 0; i < nwords; i++)
2000 if (i != count &&
2001 strcmp(words[i] + x_basename(words[i],
2002 (char *) 0), match) == 0) {
2003 match = words[count];
2004 break;
2006 } else
2007 match = words[count];
2008 match_len = strlen(match);
2009 is_unique = 1;
2010 /* expanded = PRINT; next call undo */
2011 } else {
2012 match = words[0];
2013 match_len = x_longest_prefix(nwords, words);
2014 expanded = COMPLETE; /* next call will list completions */
2015 is_unique = nwords == 1;
2018 buf = save_edstate(es);
2019 del_range(start, end);
2020 es->cursor = start;
2022 /* escape all shell-sensitive characters and put the result into
2023 * command buffer */
2024 rval = x_escape(match, match_len, x_vi_putbuf);
2026 if (rval == 0 && is_unique) {
2027 /* If exact match, don't undo. Allows directory completions
2028 * to be used (ie, complete the next portion of the path).
2030 expanded = NONE;
2032 /* If not a directory, add a space to the end... */
2033 if (match_len > 0 && match[match_len - 1] != '/')
2034 rval = putbuf(space, 1, 0);
2036 x_free_words(nwords, words);
2038 modified = 1; hnum = hlast;
2039 insert = INSERT;
2040 lastac = 0; /* prevent this from being redone... */
2041 refresh(0);
2043 return rval;
2046 static int
2047 print_expansions(struct edstate *e, int command)
2049 int nwords;
2050 int start, end;
2051 char **words;
2052 int is_command;
2054 nwords = x_cf_glob(XCF_COMMAND_FILE|XCF_FULLPATH,
2055 e->cbuf, e->linelen, e->cursor,
2056 &start, &end, &words, &is_command);
2057 if (nwords == 0) {
2058 vi_error();
2059 return -1;
2061 x_print_expansions(nwords, words, is_command);
2062 x_free_words(nwords, words);
2063 redraw_line(0);
2064 return 0;
2067 /* How long is char when displayed (not counting tabs) */
2068 static int
2069 char_len(int c)
2071 int len = 1;
2073 if ((c & 0x80) && Flag(FVISHOW8)) {
2074 len += 2;
2075 c &= 0x7f;
2077 if (c < ' ' || c == 0x7f)
2078 len++;
2079 return len;
2082 /* Similar to x_zotc(emacs.c), but no tab weirdness */
2083 static void
2084 x_vi_zotc(int c)
2086 if (Flag(FVISHOW8) && (c & 0x80)) {
2087 x_puts("M-");
2088 c &= 0x7f;
2090 if (c < ' ' || c == 0x7f) {
2091 x_putc('^');
2092 c ^= '@';
2094 x_putc(c);
2097 static void
2098 vi_pprompt(int full)
2100 pprompt(prompt + (full ? 0 : prompt_skip), prompt_trunc);
2103 static void
2104 vi_error(void)
2106 /* Beem out of any macros as soon as an error occurs */
2107 vi_macro_reset();
2108 x_putc(BEL);
2109 x_flush();
2112 static void
2113 vi_macro_reset(void)
2115 if (macro.p) {
2116 afree(macro.buf, APERM);
2117 memset((char *) &macro, 0, sizeof(macro));
2121 #endif /* VI */