etc/protocols - sync with NetBSD-8
[minix.git] / bin / ksh / vi.c
blobed3d06fc6271a83bf0b0888279051bc7b4a903dd
1 /* $NetBSD: vi.c,v 1.12 2011/06/22 03:56:17 mrg 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 <sys/cdefs.h>
11 #ifndef lint
12 __RCSID("$NetBSD: vi.c,v 1.12 2011/06/22 03:56:17 mrg Exp $");
13 #endif
15 #include "config.h"
16 #ifdef VI
18 #include "sh.h"
19 #include <ctype.h>
20 #include "ksh_stat.h" /* completion */
21 #include "edit.h"
23 #define CMDLEN 1024
24 #define Ctrl(c) (c&0x1f)
25 #define is_wordch(c) (letnum(c))
27 struct edstate {
28 int winleft;
29 char *cbuf;
30 int cbufsize;
31 int linelen;
32 int cursor;
36 static int vi_hook ARGS((int));
37 static void vi_reset ARGS((char *, size_t));
38 static int nextstate ARGS((int));
39 static int vi_insert ARGS((int));
40 static int vi_cmd ARGS((int, const char *));
41 static int domove ARGS((int, const char *, int));
42 static int redo_insert ARGS((int));
43 static void yank_range ARGS((int, int));
44 static int bracktype ARGS((int));
45 static void save_cbuf ARGS((void));
46 static void restore_cbuf ARGS((void));
47 static void edit_reset ARGS((char *, size_t));
48 static int putbuf ARGS((const char *, int, int));
49 static void del_range ARGS((int, int));
50 static int findch ARGS((int, int, int, int));
51 static int forwword ARGS((int));
52 static int backword ARGS((int));
53 static int endword ARGS((int));
54 static int Forwword ARGS((int));
55 static int Backword ARGS((int));
56 static int Endword ARGS((int));
57 static int grabhist ARGS((int, int));
58 static int grabsearch ARGS((int, int, int, char *));
59 static void redraw_line ARGS((int));
60 static void refresh ARGS((int));
61 static int outofwin ARGS((void));
62 static void rewindow ARGS((void));
63 static int newcol ARGS((int, int));
64 static void display ARGS((char *, char *, int));
65 static void ed_mov_opt ARGS((int, char *));
66 static int expand_word ARGS((int));
67 static int complete_word ARGS((int, int));
68 static int print_expansions ARGS((struct edstate *, int));
69 static int char_len ARGS((int));
70 static void x_vi_zotc ARGS((int));
71 static void vi_pprompt ARGS((int));
72 static void vi_error ARGS((void));
73 static void vi_macro_reset ARGS((void));
74 static int x_vi_putbuf ARGS((const char *, size_t));
76 #define C_ 0x1 /* a valid command that isn't a M_, E_, U_ */
77 #define M_ 0x2 /* movement command (h, l, etc.) */
78 #define E_ 0x4 /* extended command (c, d, y) */
79 #define X_ 0x8 /* long command (@, f, F, t, T, etc.) */
80 #define U_ 0x10 /* an UN-undoable command (that isn't a M_) */
81 #define B_ 0x20 /* bad command (^@) */
82 #define Z_ 0x40 /* repeat count defaults to 0 (not 1) */
83 #define S_ 0x80 /* search (/, ?) */
85 #define is_bad(c) (classify[(c)&0x7f]&B_)
86 #define is_cmd(c) (classify[(c)&0x7f]&(M_|E_|C_|U_))
87 #define is_move(c) (classify[(c)&0x7f]&M_)
88 #define is_extend(c) (classify[(c)&0x7f]&E_)
89 #define is_long(c) (classify[(c)&0x7f]&X_)
90 #define is_undoable(c) (!(classify[(c)&0x7f]&U_))
91 #define is_srch(c) (classify[(c)&0x7f]&S_)
92 #define is_zerocount(c) (classify[(c)&0x7f]&Z_)
94 const unsigned char classify[128] = {
95 /* 0 1 2 3 4 5 6 7 */
96 /* 0 ^@ ^A ^B ^C ^D ^E ^F ^G */
97 B_, 0, 0, 0, 0, C_|U_, C_|Z_, 0,
98 /* 01 ^H ^I ^J ^K ^L ^M ^N ^O */
99 M_, C_|Z_, 0, 0, C_|U_, 0, C_, 0,
100 /* 02 ^P ^Q ^R ^S ^T ^U ^V ^W */
101 C_, 0, C_|U_, 0, 0, 0, C_, 0,
102 /* 03 ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
103 C_, 0, 0, C_|Z_, 0, 0, 0, 0,
104 /* 04 <space> ! " # $ % & ' */
105 M_, 0, 0, C_, M_, M_, 0, 0,
106 /* 05 ( ) * + , - . / */
107 0, 0, C_, C_, M_, C_, 0, C_|S_,
108 /* 06 0 1 2 3 4 5 6 7 */
109 M_, 0, 0, 0, 0, 0, 0, 0,
110 /* 07 8 9 : ; < = > ? */
111 0, 0, 0, M_, 0, C_, 0, C_|S_,
112 /* 010 @ A B C D E F G */
113 C_|X_, C_, M_, C_, C_, M_, M_|X_, C_|U_|Z_,
114 /* 011 H I J K L M N O */
115 0, C_, 0, 0, 0, 0, C_|U_, 0,
116 /* 012 P Q R S T U V W */
117 C_, 0, C_, C_, M_|X_, C_, 0, M_,
118 /* 013 X Y Z [ \ ] ^ _ */
119 C_, C_|U_, 0, 0, C_|Z_, 0, M_, C_|Z_,
120 /* 014 ` a b c d e f g */
121 0, C_, M_, E_, E_, M_, M_|X_, C_|Z_,
122 /* 015 h i j k l m n o */
123 M_, C_, C_|U_, C_|U_, M_, 0, C_|U_, 0,
124 /* 016 p q r s t u v w */
125 C_, 0, X_, C_, M_|X_, C_|U_, C_|U_|Z_,M_,
126 /* 017 x y z { | } ~ ^? */
127 C_, E_|U_, 0, 0, M_|Z_, 0, C_, 0
130 #define MAXVICMD 3
131 #define SRCHLEN 40
133 #define INSERT 1
134 #define REPLACE 2
136 #define VNORMAL 0 /* command, insert or replace mode */
137 #define VARG1 1 /* digit prefix (first, eg, 5l) */
138 #define VEXTCMD 2 /* cmd + movement (eg, cl) */
139 #define VARG2 3 /* digit prefix (second, eg, 2c3l) */
140 #define VXCH 4 /* f, F, t, T, @ */
141 #define VFAIL 5 /* bad command */
142 #define VCMD 6 /* single char command (eg, X) */
143 #define VREDO 7 /* . */
144 #define VLIT 8 /* ^V */
145 #define VSEARCH 9 /* /, ? */
146 #define VVERSION 10 /* <ESC> ^V */
148 static char undocbuf[CMDLEN];
150 static struct edstate *save_edstate ARGS((struct edstate *old));
151 static void restore_edstate ARGS((struct edstate *old, struct edstate *new));
152 static void free_edstate ARGS((struct edstate *old));
154 static struct edstate ebuf;
155 static struct edstate undobuf = { 0, undocbuf, CMDLEN, 0, 0 };
157 static struct edstate *es; /* current editor state */
158 static struct edstate *undo;
160 static char ibuf[CMDLEN]; /* input buffer */
161 static int first_insert; /* set when starting in insert mode */
162 static int saved_inslen; /* saved inslen for first insert */
163 static int inslen; /* length of input buffer */
164 static int srchlen; /* length of current search pattern */
165 static char ybuf[CMDLEN]; /* yank buffer */
166 static int yanklen; /* length of yank buffer */
167 static int fsavecmd = ' '; /* last find command */
168 static int fsavech; /* character to find */
169 static char lastcmd[MAXVICMD]; /* last non-move command */
170 static int lastac; /* argcnt for lastcmd */
171 static int lastsearch = ' '; /* last search command */
172 static char srchpat[SRCHLEN]; /* last search pattern */
173 static int insert; /* non-zero in insert mode */
174 static int hnum; /* position in history */
175 static int ohnum; /* history line copied (after mod) */
176 static int hlast; /* 1 past last position in history */
177 static int modified; /* buffer has been "modified" */
178 static int state;
180 /* Information for keeping track of macros that are being expanded.
181 * The format of buf is the alias contents followed by a null byte followed
182 * by the name (letter) of the alias. The end of the buffer is marked by
183 * a double null. The name of the alias is stored so recursive macros can
184 * be detected.
186 struct macro_state {
187 unsigned char *p; /* current position in buf */
188 unsigned char *buf; /* pointer to macro(s) being expanded */
189 int len; /* how much data in buffer */
191 static struct macro_state macro;
193 enum expand_mode { NONE, EXPAND, COMPLETE, PRINT };
194 static enum expand_mode expanded = NONE;/* last input was expanded */
197 x_vi(buf, len)
198 char *buf;
199 size_t len;
201 int c;
203 vi_reset(buf, len > CMDLEN ? CMDLEN : len);
204 vi_pprompt(1);
205 x_flush();
206 while (1) {
207 if (macro.p) {
208 c = *macro.p++;
209 /* end of current macro? */
210 if (!c) {
211 /* more macros left to finish? */
212 if (*macro.p++)
213 continue;
214 /* must be the end of all the macros */
215 vi_macro_reset();
216 c = x_getc();
218 } else {
219 c = x_getc();
221 if (c == -1)
222 break;
223 if (state != VLIT) {
224 if (c == edchars.intr || c == edchars.quit) {
225 /* pretend we got an interrupt */
226 x_vi_zotc(c);
227 x_flush();
228 trapsig(c == edchars.intr ? SIGINT : SIGQUIT);
229 x_mode(FALSE);
230 unwind(LSHELL);
231 } else if (c == edchars.eof && state != VVERSION) {
232 if (es->linelen == 0) {
233 x_vi_zotc(edchars.eof);
234 c = -1;
235 break;
237 continue;
240 if (vi_hook(c))
241 break;
242 x_flush();
245 x_putc('\r'); x_putc('\n'); x_flush();
247 if (c == -1 || len <= (size_t)es->linelen)
248 return -1;
250 if (es->cbuf != buf)
251 memmove(buf, es->cbuf, es->linelen);
253 buf[es->linelen++] = '\n';
255 return es->linelen;
258 static int
259 vi_hook(ch)
260 int ch;
262 static char curcmd[MAXVICMD];
263 static char locpat[SRCHLEN];
264 static int cmdlen;
265 static int argc1, argc2;
267 switch (state) {
269 case VNORMAL:
270 if (insert != 0) {
271 if (ch == Ctrl('v')) {
272 state = VLIT;
273 ch = '^';
275 switch (vi_insert(ch)) {
276 case -1:
277 #ifdef OS2
278 /* Arrow keys generate 0xe0X, where X is H.. */
279 state = VCMD;
280 argc1 = 1;
281 switch (x_getc()) {
282 case 'H':
283 *curcmd='k';
284 break;
285 case 'K':
286 *curcmd='h';
287 break;
288 case 'P':
289 *curcmd='j';
290 break;
291 case 'M':
292 *curcmd='l';
293 break;
294 default:
295 vi_error();
296 state = VNORMAL;
298 break;
299 #else /* OS2 */
300 vi_error();
301 state = VNORMAL;
302 #endif /* OS2 */
303 break;
304 case 0:
305 if (state == VLIT) {
306 es->cursor--;
307 refresh(0);
308 } else
309 refresh(insert != 0);
310 break;
311 case 1:
312 return 1;
314 } else {
315 if (ch == '\r' || ch == '\n')
316 return 1;
317 cmdlen = 0;
318 argc1 = 0;
319 if (ch >= '1' && ch <= '9') {
320 argc1 = ch - '0';
321 state = VARG1;
322 } else {
323 curcmd[cmdlen++] = ch;
324 state = nextstate(ch);
325 if (state == VSEARCH) {
326 save_cbuf();
327 es->cursor = 0;
328 es->linelen = 0;
329 if (ch == '/') {
330 if (putbuf("/", 1, 0) != 0) {
331 return -1;
333 } else if (putbuf("?", 1, 0) != 0)
334 return -1;
335 refresh(0);
337 if (state == VVERSION) {
338 save_cbuf();
339 es->cursor = 0;
340 es->linelen = 0;
341 putbuf(ksh_version + 4,
342 strlen(ksh_version + 4), 0);
343 refresh(0);
347 break;
349 case VLIT:
350 if (is_bad(ch)) {
351 del_range(es->cursor, es->cursor + 1);
352 vi_error();
353 } else
354 es->cbuf[es->cursor++] = ch;
355 refresh(1);
356 state = VNORMAL;
357 break;
359 case VVERSION:
360 restore_cbuf();
361 state = VNORMAL;
362 refresh(0);
363 break;
365 case VARG1:
366 if (isdigit(ch))
367 argc1 = argc1 * 10 + ch - '0';
368 else {
369 curcmd[cmdlen++] = ch;
370 state = nextstate(ch);
372 break;
374 case VEXTCMD:
375 argc2 = 0;
376 if (ch >= '1' && ch <= '9') {
377 argc2 = ch - '0';
378 state = VARG2;
379 return 0;
380 } else {
381 curcmd[cmdlen++] = ch;
382 if (ch == curcmd[0])
383 state = VCMD;
384 else if (is_move(ch))
385 state = nextstate(ch);
386 else
387 state = VFAIL;
389 break;
391 case VARG2:
392 if (isdigit(ch))
393 argc2 = argc2 * 10 + ch - '0';
394 else {
395 if (argc1 == 0)
396 argc1 = argc2;
397 else
398 argc1 *= argc2;
399 curcmd[cmdlen++] = ch;
400 if (ch == curcmd[0])
401 state = VCMD;
402 else if (is_move(ch))
403 state = nextstate(ch);
404 else
405 state = VFAIL;
407 break;
409 case VXCH:
410 if (ch == Ctrl('['))
411 state = VNORMAL;
412 else {
413 curcmd[cmdlen++] = ch;
414 state = VCMD;
416 break;
418 case VSEARCH:
419 if (ch == '\r' || ch == '\n' /*|| ch == Ctrl('[')*/ ) {
420 restore_cbuf();
421 /* Repeat last search? */
422 if (srchlen == 0) {
423 if (!srchpat[0]) {
424 vi_error();
425 state = VNORMAL;
426 refresh(0);
427 return 0;
429 } else {
430 locpat[srchlen] = '\0';
431 (void) strlcpy(srchpat, locpat, sizeof srchpat);
433 state = VCMD;
434 } else if (ch == edchars.erase || ch == Ctrl('h')) {
435 if (srchlen != 0) {
436 srchlen--;
437 es->linelen -= char_len((unsigned char) locpat[srchlen]);
438 es->cursor = es->linelen;
439 refresh(0);
440 return 0;
442 restore_cbuf();
443 state = VNORMAL;
444 refresh(0);
445 } else if (ch == edchars.kill) {
446 srchlen = 0;
447 es->linelen = 1;
448 es->cursor = 1;
449 refresh(0);
450 return 0;
451 } else if (ch == edchars.werase) {
452 int i;
453 int n = srchlen;
455 while (n > 0 && isspace((unsigned char)locpat[n - 1]))
456 n--;
457 while (n > 0 && !isspace((unsigned char)locpat[n - 1]))
458 n--;
459 for (i = srchlen; --i >= n; )
460 es->linelen -= char_len((unsigned char) locpat[i]);
461 srchlen = n;
462 es->cursor = es->linelen;
463 refresh(0);
464 return 0;
465 } else {
466 if (srchlen == SRCHLEN - 1)
467 vi_error();
468 else {
469 locpat[srchlen++] = ch;
470 if ((ch & 0x80) && Flag(FVISHOW8)) {
471 if (es->linelen + 2 > es->cbufsize)
472 vi_error();
473 es->cbuf[es->linelen++] = 'M';
474 es->cbuf[es->linelen++] = '-';
475 ch &= 0x7f;
477 if (ch < ' ' || ch == 0x7f) {
478 if (es->linelen + 2 > es->cbufsize)
479 vi_error();
480 es->cbuf[es->linelen++] = '^';
481 es->cbuf[es->linelen++] = ch ^ '@';
482 } else {
483 if (es->linelen >= es->cbufsize)
484 vi_error();
485 es->cbuf[es->linelen++] = ch;
487 es->cursor = es->linelen;
488 refresh(0);
490 return 0;
492 break;
495 switch (state) {
496 case VCMD:
497 state = VNORMAL;
498 switch (vi_cmd(argc1, curcmd)) {
499 case -1:
500 vi_error();
501 refresh(0);
502 break;
503 case 0:
504 if (insert != 0)
505 inslen = 0;
506 refresh(insert != 0);
507 break;
508 case 1:
509 refresh(0);
510 return 1;
511 case 2:
512 /* back from a 'v' command - don't redraw the screen */
513 return 1;
515 break;
517 case VREDO:
518 state = VNORMAL;
519 if (argc1 != 0)
520 lastac = argc1;
521 switch (vi_cmd(lastac, lastcmd)) {
522 case -1:
523 vi_error();
524 refresh(0);
525 break;
526 case 0:
527 if (insert != 0) {
528 if (lastcmd[0] == 's' || lastcmd[0] == 'c' ||
529 lastcmd[0] == 'C') {
530 if (redo_insert(1) != 0)
531 vi_error();
532 } else {
533 if (redo_insert(lastac) != 0)
534 vi_error();
537 refresh(0);
538 break;
539 case 1:
540 refresh(0);
541 return 1;
542 case 2:
543 /* back from a 'v' command - can't happen */
544 break;
546 break;
548 case VFAIL:
549 state = VNORMAL;
550 vi_error();
551 break;
553 return 0;
556 static void
557 vi_reset(buf, len)
558 char *buf;
559 size_t len;
561 state = VNORMAL;
562 ohnum = hnum = hlast = histnum(-1) + 1;
563 insert = INSERT;
564 saved_inslen = inslen;
565 first_insert = 1;
566 inslen = 0;
567 modified = 1;
568 vi_macro_reset();
569 edit_reset(buf, len);
572 static int
573 nextstate(ch)
574 int ch;
576 if (is_extend(ch))
577 return VEXTCMD;
578 else if (is_srch(ch))
579 return VSEARCH;
580 else if (is_long(ch))
581 return VXCH;
582 else if (ch == '.')
583 return VREDO;
584 else if (ch == Ctrl('v'))
585 return VVERSION;
586 else if (is_cmd(ch))
587 return VCMD;
588 else
589 return VFAIL;
592 static int
593 vi_insert(ch)
594 int ch;
596 int tcursor;
598 if (ch == edchars.erase || ch == Ctrl('h')) {
599 if (insert == REPLACE) {
600 if (es->cursor == undo->cursor) {
601 vi_error();
602 return 0;
604 if (inslen > 0)
605 inslen--;
606 es->cursor--;
607 if (es->cursor >= undo->linelen)
608 es->linelen--;
609 else
610 es->cbuf[es->cursor] = undo->cbuf[es->cursor];
611 } else {
612 if (es->cursor == 0) {
613 /* x_putc(BEL); no annoying bell here */
614 return 0;
616 if (inslen > 0)
617 inslen--;
618 es->cursor--;
619 es->linelen--;
620 memmove(&es->cbuf[es->cursor], &es->cbuf[es->cursor+1],
621 es->linelen - es->cursor + 1);
623 expanded = NONE;
624 return 0;
626 if (ch == edchars.kill) {
627 if (es->cursor != 0) {
628 inslen = 0;
629 memmove(es->cbuf, &es->cbuf[es->cursor],
630 es->linelen - es->cursor);
631 es->linelen -= es->cursor;
632 es->cursor = 0;
634 expanded = NONE;
635 return 0;
637 if (ch == edchars.werase) {
638 if (es->cursor != 0) {
639 tcursor = Backword(1);
640 memmove(&es->cbuf[tcursor], &es->cbuf[es->cursor],
641 es->linelen - es->cursor);
642 es->linelen -= es->cursor - tcursor;
643 if (inslen < es->cursor - tcursor)
644 inslen = 0;
645 else
646 inslen -= es->cursor - tcursor;
647 es->cursor = tcursor;
649 expanded = NONE;
650 return 0;
652 /* If any chars are entered before escape, trash the saved insert
653 * buffer (if user inserts & deletes char, ibuf gets trashed and
654 * we don't want to use it)
656 if (first_insert && ch != Ctrl('['))
657 saved_inslen = 0;
658 switch (ch) {
660 #ifdef OS2
661 case 224: /* function key prefix */
662 #endif /* OS2 */
663 case '\0':
664 return -1;
666 case '\r':
667 case '\n':
668 return 1;
670 case Ctrl('['):
671 expanded = NONE;
672 if (first_insert) {
673 first_insert = 0;
674 if (inslen == 0) {
675 inslen = saved_inslen;
676 return redo_insert(0);
678 lastcmd[0] = 'a';
679 lastac = 1;
681 if (lastcmd[0] == 's' || lastcmd[0] == 'c' ||
682 lastcmd[0] == 'C')
683 return redo_insert(0);
684 else
685 return redo_insert(lastac - 1);
687 /* { Begin nonstandard vi commands */
688 case Ctrl('x'):
689 expand_word(0);
690 break;
692 case Ctrl('f'):
693 complete_word(0, 0);
694 break;
696 case Ctrl('e'):
697 print_expansions(es, 0);
698 break;
700 case Ctrl('i'):
701 if (Flag(FVITABCOMPLETE)) {
702 complete_word(0, 0);
703 break;
705 /* FALLTHROUGH */
706 /* End nonstandard vi commands } */
708 default:
709 if (es->linelen >= es->cbufsize - 1)
710 return -1;
711 ibuf[inslen++] = ch;
712 if (insert == INSERT) {
713 memmove(&es->cbuf[es->cursor+1], &es->cbuf[es->cursor],
714 es->linelen - es->cursor);
715 es->linelen++;
717 es->cbuf[es->cursor++] = ch;
718 if (insert == REPLACE && es->cursor > es->linelen)
719 es->linelen++;
720 expanded = NONE;
722 return 0;
725 static int
726 vi_cmd(argcnt, cmd)
727 int argcnt;
728 const char *cmd;
730 int ncursor;
731 int cur, c1, c2, c3 = 0;
732 int any;
733 struct edstate *t;
735 if (argcnt == 0 && !is_zerocount(*cmd))
736 argcnt = 1;
738 if (is_move(*cmd)) {
739 if ((cur = domove(argcnt, cmd, 0)) >= 0) {
740 if (cur == es->linelen && cur != 0)
741 cur--;
742 es->cursor = cur;
743 } else
744 return -1;
745 } else {
746 /* Don't save state in middle of macro.. */
747 if (is_undoable(*cmd) && !macro.p) {
748 undo->winleft = es->winleft;
749 memmove(undo->cbuf, es->cbuf, es->linelen);
750 undo->linelen = es->linelen;
751 undo->cursor = es->cursor;
752 lastac = argcnt;
753 memmove(lastcmd, cmd, MAXVICMD);
755 switch (*cmd) {
757 case Ctrl('l'):
758 case Ctrl('r'):
759 redraw_line(1);
760 break;
762 case '@':
764 static char alias[] = "_\0";
765 struct tbl *ap;
766 int olen, nlen;
767 char *p, *nbuf;
769 /* lookup letter in alias list... */
770 alias[1] = cmd[1];
771 ap = tsearch(&aliases, alias, hash(alias));
772 if (!cmd[1] || !ap || !(ap->flag & ISSET))
773 return -1;
774 /* check if this is a recursive call... */
775 if ((p = (char *) macro.p))
776 while ((p = strchr(p, '\0')) && p[1])
777 if (*++p == cmd[1])
778 return -1;
779 /* insert alias into macro buffer */
780 nlen = strlen(ap->val.s) + 1;
781 olen = !macro.p ? 2
782 : macro.len - (macro.p - macro.buf);
783 nbuf = alloc(nlen + 1 + olen, APERM);
784 memcpy(nbuf, ap->val.s, nlen);
785 nbuf[nlen++] = cmd[1];
786 if (macro.p) {
787 memcpy(nbuf + nlen, macro.p, olen);
788 afree(macro.buf, APERM);
789 nlen += olen;
790 } else {
791 nbuf[nlen++] = '\0';
792 nbuf[nlen++] = '\0';
794 macro.p = macro.buf = (unsigned char *) nbuf;
795 macro.len = nlen;
797 break;
799 case 'a':
800 modified = 1; hnum = hlast;
801 if (es->linelen != 0)
802 es->cursor++;
803 insert = INSERT;
804 break;
806 case 'A':
807 modified = 1; hnum = hlast;
808 del_range(0, 0);
809 es->cursor = es->linelen;
810 insert = INSERT;
811 break;
813 case 'S':
814 es->cursor = domove(1, "^", 1);
815 del_range(es->cursor, es->linelen);
816 modified = 1; hnum = hlast;
817 insert = INSERT;
818 break;
820 case 'Y':
821 cmd = "y$";
822 /* ahhhhhh... */
823 case 'c':
824 case 'd':
825 case 'y':
826 if (*cmd == cmd[1]) {
827 c1 = *cmd == 'c' ? domove(1, "^", 1) : 0;
828 c2 = es->linelen;
829 } else if (!is_move(cmd[1]))
830 return -1;
831 else {
832 if ((ncursor = domove(argcnt, &cmd[1], 1)) < 0)
833 return -1;
834 if (*cmd == 'c' &&
835 (cmd[1]=='w' || cmd[1]=='W') &&
836 !isspace((unsigned char)es->cbuf[es->cursor])) {
837 while (isspace((unsigned char)es->cbuf[--ncursor]))
839 ncursor++;
841 if (ncursor > es->cursor) {
842 c1 = es->cursor;
843 c2 = ncursor;
844 } else {
845 c1 = ncursor;
846 c2 = es->cursor;
847 if (cmd[1] == '%')
848 c2++;
851 if (*cmd != 'c' && c1 != c2)
852 yank_range(c1, c2);
853 if (*cmd != 'y') {
854 del_range(c1, c2);
855 es->cursor = c1;
857 if (*cmd == 'c') {
858 modified = 1; hnum = hlast;
859 insert = INSERT;
861 break;
863 case 'p':
864 modified = 1; hnum = hlast;
865 if (es->linelen != 0)
866 es->cursor++;
867 while (putbuf(ybuf, yanklen, 0) == 0 && --argcnt > 0)
869 if (es->cursor != 0)
870 es->cursor--;
871 if (argcnt != 0)
872 return -1;
873 break;
875 case 'P':
876 modified = 1; hnum = hlast;
877 any = 0;
878 while (putbuf(ybuf, yanklen, 0) == 0 && --argcnt > 0)
879 any = 1;
880 if (any && es->cursor != 0)
881 es->cursor--;
882 if (argcnt != 0)
883 return -1;
884 break;
886 case 'C':
887 modified = 1; hnum = hlast;
888 del_range(es->cursor, es->linelen);
889 insert = INSERT;
890 break;
892 case 'D':
893 yank_range(es->cursor, es->linelen);
894 del_range(es->cursor, es->linelen);
895 if (es->cursor != 0)
896 es->cursor--;
897 break;
899 case 'g':
900 if (!argcnt)
901 argcnt = hlast + 1;
902 /* fall through */
903 case 'G':
904 if (!argcnt)
905 argcnt = 1;
906 else
907 argcnt = hlast - (source->line - argcnt);
908 if (grabhist(modified, argcnt - 1) < 0)
909 return -1;
910 else {
911 modified = 0;
912 hnum = argcnt - 1;
914 break;
916 case 'i':
917 modified = 1; hnum = hlast;
918 insert = INSERT;
919 break;
921 case 'I':
922 modified = 1; hnum = hlast;
923 es->cursor = domove(1, "^", 1);
924 insert = INSERT;
925 break;
927 case 'j':
928 case '+':
929 case Ctrl('n'):
930 if (grabhist(modified, hnum + argcnt) < 0)
931 return -1;
932 else {
933 modified = 0;
934 hnum += argcnt;
936 break;
938 case 'k':
939 case '-':
940 case Ctrl('p'):
941 if (grabhist(modified, hnum - argcnt) < 0)
942 return -1;
943 else {
944 modified = 0;
945 hnum -= argcnt;
947 break;
949 case 'r':
950 if (es->linelen == 0)
951 return -1;
952 modified = 1; hnum = hlast;
953 if (cmd[1] == 0)
954 vi_error();
955 else
956 es->cbuf[es->cursor] = cmd[1];
957 break;
959 case 'R':
960 modified = 1; hnum = hlast;
961 insert = REPLACE;
962 break;
964 case 's':
965 if (es->linelen == 0)
966 return -1;
967 modified = 1; hnum = hlast;
968 if (es->cursor + argcnt > es->linelen)
969 argcnt = es->linelen - es->cursor;
970 del_range(es->cursor, es->cursor + argcnt);
971 insert = INSERT;
972 break;
974 case 'v':
975 if (es->linelen == 0)
976 return -1;
977 if (!argcnt) {
978 if (modified) {
979 es->cbuf[es->linelen] = '\0';
980 source->line++;
981 histsave(source->line, es->cbuf, 1);
982 } else
983 argcnt = source->line + 1
984 - (hlast - hnum);
986 shf_snprintf(es->cbuf, es->cbufsize,
987 argcnt ? "%s %d" : "%s",
988 "fc -e ${VISUAL:-${EDITOR:-vi}} --",
989 argcnt);
990 es->linelen = strlen(es->cbuf);
991 return 2;
993 case 'x':
994 if (es->linelen == 0)
995 return -1;
996 modified = 1; hnum = hlast;
997 if (es->cursor + argcnt > es->linelen)
998 argcnt = es->linelen - es->cursor;
999 yank_range(es->cursor, es->cursor + argcnt);
1000 del_range(es->cursor, es->cursor + argcnt);
1001 break;
1003 case 'X':
1004 if (es->cursor > 0) {
1005 modified = 1; hnum = hlast;
1006 if (es->cursor < argcnt)
1007 argcnt = es->cursor;
1008 yank_range(es->cursor - argcnt, es->cursor);
1009 del_range(es->cursor - argcnt, es->cursor);
1010 es->cursor -= argcnt;
1011 } else
1012 return -1;
1013 break;
1015 case 'u':
1016 t = es;
1017 es = undo;
1018 undo = t;
1019 break;
1021 case 'U':
1022 if (!modified)
1023 return -1;
1024 if (grabhist(modified, ohnum) < 0)
1025 return -1;
1026 modified = 0;
1027 hnum = ohnum;
1028 break;
1030 case '?':
1031 if (hnum == hlast)
1032 hnum = -1;
1033 /* ahhh */
1034 case '/':
1035 c3 = 1;
1036 srchlen = 0;
1037 lastsearch = *cmd;
1038 /* fall through */
1039 case 'n':
1040 case 'N':
1041 if (lastsearch == ' ')
1042 return -1;
1043 if (lastsearch == '?')
1044 c1 = 1;
1045 else
1046 c1 = 0;
1047 if (*cmd == 'N')
1048 c1 = !c1;
1049 if ((c2 = grabsearch(modified, hnum,
1050 c1, srchpat)) < 0) {
1051 if (c3) {
1052 restore_cbuf();
1053 refresh(0);
1055 return -1;
1056 } else {
1057 modified = 0;
1058 hnum = c2;
1059 ohnum = hnum;
1061 break;
1062 case '_': {
1063 int inspace;
1064 char *p, *sp;
1066 if (histnum(-1) < 0)
1067 return -1;
1068 p = *histpos();
1069 #define issp(c) (isspace((unsigned char)(c)) || (c) == '\n')
1070 if (argcnt) {
1071 while (*p && issp(*p))
1072 p++;
1073 while (*p && --argcnt) {
1074 while (*p && !issp(*p))
1075 p++;
1076 while (*p && issp(*p))
1077 p++;
1079 if (!*p)
1080 return -1;
1081 sp = p;
1082 } else {
1083 sp = p;
1084 inspace = 0;
1085 while (*p) {
1086 if (issp(*p))
1087 inspace = 1;
1088 else if (inspace) {
1089 inspace = 0;
1090 sp = p;
1092 p++;
1094 p = sp;
1096 modified = 1; hnum = hlast;
1097 if (es->cursor != es->linelen)
1098 es->cursor++;
1099 while (*p && !issp(*p)) {
1100 argcnt++;
1101 p++;
1103 if (putbuf(space, 1, 0) != 0)
1104 argcnt = -1;
1105 else if (putbuf(sp, argcnt, 0) != 0)
1106 argcnt = -1;
1107 if (argcnt < 0) {
1108 if (es->cursor != 0)
1109 es->cursor--;
1110 return -1;
1112 insert = INSERT;
1114 break;
1116 case '~': {
1117 char *p;
1118 int i;
1120 if (es->linelen == 0)
1121 return -1;
1122 for (i = 0; i < argcnt; i++) {
1123 p = &es->cbuf[es->cursor];
1124 if (islower((unsigned char)*p)) {
1125 modified = 1; hnum = hlast;
1126 *p = toupper((unsigned char)*p);
1127 } else if (isupper((unsigned char)*p)) {
1128 modified = 1; hnum = hlast;
1129 *p = tolower((unsigned char)*p);
1131 if (es->cursor < es->linelen - 1)
1132 es->cursor++;
1134 break;
1137 case '#':
1139 int ret = x_do_comment(es->cbuf, es->cbufsize,
1140 &es->linelen);
1141 if (ret >= 0)
1142 es->cursor = 0;
1143 return ret;
1146 case '=': /* at&t ksh */
1147 case Ctrl('e'): /* Nonstandard vi/ksh */
1148 print_expansions(es, 1);
1149 break;
1152 case Ctrl('i'): /* Nonstandard vi/ksh */
1153 if (!Flag(FVITABCOMPLETE))
1154 return -1;
1155 complete_word(1, argcnt);
1156 break;
1158 case Ctrl('['): /* some annoying at&t ksh's */
1159 if (!Flag(FVIESCCOMPLETE))
1160 return -1;
1161 case '\\': /* at&t ksh */
1162 case Ctrl('f'): /* Nonstandard vi/ksh */
1163 complete_word(1, argcnt);
1164 break;
1167 case '*': /* at&t ksh */
1168 case Ctrl('x'): /* Nonstandard vi/ksh */
1169 expand_word(1);
1170 break;
1172 if (insert == 0 && es->cursor != 0 && es->cursor >= es->linelen)
1173 es->cursor--;
1175 return 0;
1178 static int
1179 domove(argcnt, cmd, sub)
1180 int argcnt;
1181 const char *cmd;
1182 int sub;
1184 int bcount, UNINITIALIZED(i), t;
1185 int UNINITIALIZED(ncursor);
1187 switch (*cmd) {
1189 case 'b':
1190 if (!sub && es->cursor == 0)
1191 return -1;
1192 ncursor = backword(argcnt);
1193 break;
1195 case 'B':
1196 if (!sub && es->cursor == 0)
1197 return -1;
1198 ncursor = Backword(argcnt);
1199 break;
1201 case 'e':
1202 if (!sub && es->cursor + 1 >= es->linelen)
1203 return -1;
1204 ncursor = endword(argcnt);
1205 if (sub && ncursor < es->linelen)
1206 ncursor++;
1207 break;
1209 case 'E':
1210 if (!sub && es->cursor + 1 >= es->linelen)
1211 return -1;
1212 ncursor = Endword(argcnt);
1213 if (sub && ncursor < es->linelen)
1214 ncursor++;
1215 break;
1217 case 'f':
1218 case 'F':
1219 case 't':
1220 case 'T':
1221 fsavecmd = *cmd;
1222 fsavech = cmd[1];
1223 /* drop through */
1225 case ',':
1226 case ';':
1227 if (fsavecmd == ' ')
1228 return -1;
1229 i = fsavecmd == 'f' || fsavecmd == 'F';
1230 t = fsavecmd > 'a';
1231 if (*cmd == ',')
1232 t = !t;
1233 if ((ncursor = findch(fsavech, argcnt, t, i)) < 0)
1234 return -1;
1235 if (sub && t)
1236 ncursor++;
1237 break;
1239 case 'h':
1240 case Ctrl('h'):
1241 if (!sub && es->cursor == 0)
1242 return -1;
1243 ncursor = es->cursor - argcnt;
1244 if (ncursor < 0)
1245 ncursor = 0;
1246 break;
1248 case ' ':
1249 case 'l':
1250 if (!sub && es->cursor + 1 >= es->linelen)
1251 return -1;
1252 if (es->linelen != 0) {
1253 ncursor = es->cursor + argcnt;
1254 if (ncursor > es->linelen)
1255 ncursor = es->linelen;
1257 break;
1259 case 'w':
1260 if (!sub && es->cursor + 1 >= es->linelen)
1261 return -1;
1262 ncursor = forwword(argcnt);
1263 break;
1265 case 'W':
1266 if (!sub && es->cursor + 1 >= es->linelen)
1267 return -1;
1268 ncursor = Forwword(argcnt);
1269 break;
1271 case '0':
1272 ncursor = 0;
1273 break;
1275 case '^':
1276 ncursor = 0;
1277 while (ncursor < es->linelen - 1 && isspace((unsigned char)es->cbuf[ncursor]))
1278 ncursor++;
1279 break;
1281 case '|':
1282 ncursor = argcnt;
1283 if (ncursor > es->linelen)
1284 ncursor = es->linelen;
1285 if (ncursor)
1286 ncursor--;
1287 break;
1289 case '$':
1290 if (es->linelen != 0)
1291 ncursor = es->linelen;
1292 else
1293 ncursor = 0;
1294 break;
1296 case '%':
1297 ncursor = es->cursor;
1298 while (ncursor < es->linelen &&
1299 (i = bracktype(es->cbuf[ncursor])) == 0)
1300 ncursor++;
1301 if (ncursor == es->linelen)
1302 return -1;
1303 bcount = 1;
1304 do {
1305 if (i > 0) {
1306 if (++ncursor >= es->linelen)
1307 return -1;
1308 } else {
1309 if (--ncursor < 0)
1310 return -1;
1312 t = bracktype(es->cbuf[ncursor]);
1313 if (t == i)
1314 bcount++;
1315 else if (t == -i)
1316 bcount--;
1317 } while (bcount != 0);
1318 if (sub && i > 0)
1319 ncursor++;
1320 break;
1322 default:
1323 return -1;
1325 return ncursor;
1328 static int
1329 redo_insert(count)
1330 int count;
1332 while (count-- > 0)
1333 if (putbuf(ibuf, inslen, insert==REPLACE) != 0)
1334 return -1;
1335 if (es->cursor > 0)
1336 es->cursor--;
1337 insert = 0;
1338 return 0;
1341 static void
1342 yank_range(a, b)
1343 int a, b;
1345 yanklen = b - a;
1346 if (yanklen != 0)
1347 memmove(ybuf, &es->cbuf[a], yanklen);
1350 static int
1351 bracktype(ch)
1352 int ch;
1354 switch (ch) {
1356 case '(':
1357 return 1;
1359 case '[':
1360 return 2;
1362 case '{':
1363 return 3;
1365 case ')':
1366 return -1;
1368 case ']':
1369 return -2;
1371 case '}':
1372 return -3;
1374 default:
1375 return 0;
1380 * Non user interface editor routines below here
1383 static int cur_col; /* current column on line */
1384 static int pwidth; /* width of prompt */
1385 static int prompt_trunc; /* how much of prompt to truncate */
1386 static int prompt_skip; /* how much of prompt to skip */
1387 static int winwidth; /* width of window */
1388 static char *wbuf[2]; /* window buffers */
1389 static int wbuf_len; /* length of window buffers (x_cols-3)*/
1390 static int win; /* window buffer in use */
1391 static char morec; /* more character at right of window */
1392 static int lastref; /* argument to last refresh() */
1393 static char holdbuf[CMDLEN]; /* place to hold last edit buffer */
1394 static int holdlen; /* length of holdbuf */
1396 static void
1397 save_cbuf()
1399 memmove(holdbuf, es->cbuf, es->linelen);
1400 holdlen = es->linelen;
1401 holdbuf[holdlen] = '\0';
1404 static void
1405 restore_cbuf()
1407 es->cursor = 0;
1408 es->linelen = holdlen;
1409 memmove(es->cbuf, holdbuf, holdlen);
1412 /* return a new edstate */
1413 static struct edstate *
1414 save_edstate(old)
1415 struct edstate *old;
1417 struct edstate *new;
1419 new = (struct edstate *)alloc(sizeof(struct edstate), APERM);
1420 new->cbuf = alloc(old->cbufsize, APERM);
1421 memcpy(new->cbuf, old->cbuf, old->linelen);
1422 new->cbufsize = old->cbufsize;
1423 new->linelen = old->linelen;
1424 new->cursor = old->cursor;
1425 new->winleft = old->winleft;
1426 return new;
1429 static void
1430 restore_edstate(new, old)
1431 struct edstate *old, *new;
1433 memcpy(new->cbuf, old->cbuf, old->linelen);
1434 new->linelen = old->linelen;
1435 new->cursor = old->cursor;
1436 new->winleft = old->winleft;
1437 free_edstate(old);
1440 static void
1441 free_edstate(old)
1442 struct edstate *old;
1444 afree(old->cbuf, APERM);
1445 afree((char *)old, APERM);
1450 static void
1451 edit_reset(buf, len)
1452 char *buf;
1453 size_t len;
1455 const char *p;
1457 es = &ebuf;
1458 es->cbuf = buf;
1459 es->cbufsize = len;
1460 undo = &undobuf;
1461 undo->cbufsize = len;
1463 es->linelen = undo->linelen = 0;
1464 es->cursor = undo->cursor = 0;
1465 es->winleft = undo->winleft = 0;
1467 cur_col = pwidth = promptlen(prompt, &p);
1468 prompt_skip = p - prompt;
1469 if (pwidth > x_cols - 3 - MIN_EDIT_SPACE) {
1470 cur_col = x_cols - 3 - MIN_EDIT_SPACE;
1471 prompt_trunc = pwidth - cur_col;
1472 pwidth -= prompt_trunc;
1473 } else
1474 prompt_trunc = 0;
1475 if (!wbuf_len || wbuf_len != x_cols - 3) {
1476 wbuf_len = x_cols - 3;
1477 wbuf[0] = aresize(wbuf[0], wbuf_len, APERM);
1478 wbuf[1] = aresize(wbuf[1], wbuf_len, APERM);
1480 (void) memset(wbuf[0], ' ', wbuf_len);
1481 (void) memset(wbuf[1], ' ', wbuf_len);
1482 winwidth = x_cols - pwidth - 3;
1483 win = 0;
1484 morec = ' ';
1485 lastref = 1;
1486 holdlen = 0;
1490 * this is used for calling x_escape() in complete_word()
1492 static int
1493 x_vi_putbuf(s, len)
1494 const char *s;
1495 size_t len;
1497 return putbuf(s, len, 0);
1500 static int
1501 putbuf(buf, len, repl)
1502 const char *buf;
1503 int len;
1504 int repl;
1506 if (len == 0)
1507 return 0;
1508 if (repl) {
1509 if (es->cursor + len >= es->cbufsize)
1510 return -1;
1511 if (es->cursor + len > es->linelen)
1512 es->linelen = es->cursor + len;
1513 } else {
1514 if (es->linelen + len >= es->cbufsize)
1515 return -1;
1516 memmove(&es->cbuf[es->cursor + len], &es->cbuf[es->cursor],
1517 es->linelen - es->cursor);
1518 es->linelen += len;
1520 memmove(&es->cbuf[es->cursor], buf, len);
1521 es->cursor += len;
1522 return 0;
1525 static void
1526 del_range(a, b)
1527 int a, b;
1529 if (es->linelen != b)
1530 memmove(&es->cbuf[a], &es->cbuf[b], es->linelen - b);
1531 es->linelen -= b - a;
1534 static int
1535 findch(ch, cnt, forw, incl)
1536 int ch;
1537 int cnt;
1538 int forw;
1539 int incl;
1541 int ncursor;
1543 if (es->linelen == 0)
1544 return -1;
1545 ncursor = es->cursor;
1546 while (cnt--) {
1547 do {
1548 if (forw) {
1549 if (++ncursor == es->linelen)
1550 return -1;
1551 } else {
1552 if (--ncursor < 0)
1553 return -1;
1555 } while (es->cbuf[ncursor] != ch);
1557 if (!incl) {
1558 if (forw)
1559 ncursor--;
1560 else
1561 ncursor++;
1563 return ncursor;
1566 static int
1567 forwword(argcnt)
1568 int argcnt;
1570 int ncursor;
1572 ncursor = es->cursor;
1573 while (ncursor < es->linelen && argcnt--) {
1574 if (is_wordch(es->cbuf[ncursor]))
1575 while (is_wordch(es->cbuf[ncursor]) &&
1576 ncursor < es->linelen)
1577 ncursor++;
1578 else if (!isspace((unsigned char)es->cbuf[ncursor]))
1579 while (!is_wordch(es->cbuf[ncursor]) &&
1580 !isspace((unsigned char)es->cbuf[ncursor]) &&
1581 ncursor < es->linelen)
1582 ncursor++;
1583 while (isspace((unsigned char)es->cbuf[ncursor]) && ncursor < es->linelen)
1584 ncursor++;
1586 return ncursor;
1589 static int
1590 backword(argcnt)
1591 int argcnt;
1593 int ncursor;
1595 ncursor = es->cursor;
1596 while (ncursor > 0 && argcnt--) {
1597 while (--ncursor > 0 && isspace((unsigned char)es->cbuf[ncursor]))
1599 if (ncursor > 0) {
1600 if (is_wordch(es->cbuf[ncursor]))
1601 while (--ncursor >= 0 &&
1602 is_wordch(es->cbuf[ncursor]))
1604 else
1605 while (--ncursor >= 0 &&
1606 !is_wordch(es->cbuf[ncursor]) &&
1607 !isspace((unsigned char)es->cbuf[ncursor]))
1609 ncursor++;
1612 return ncursor;
1615 static int
1616 endword(argcnt)
1617 int argcnt;
1619 int ncursor;
1621 ncursor = es->cursor;
1622 while (ncursor < es->linelen && argcnt--) {
1623 while (++ncursor < es->linelen - 1 &&
1624 isspace((unsigned char)es->cbuf[ncursor]))
1626 if (ncursor < es->linelen - 1) {
1627 if (is_wordch(es->cbuf[ncursor]))
1628 while (++ncursor < es->linelen &&
1629 is_wordch(es->cbuf[ncursor]))
1631 else
1632 while (++ncursor < es->linelen &&
1633 !is_wordch(es->cbuf[ncursor]) &&
1634 !isspace((unsigned char)es->cbuf[ncursor]))
1636 ncursor--;
1639 return ncursor;
1642 static int
1643 Forwword(argcnt)
1644 int argcnt;
1646 int ncursor;
1648 ncursor = es->cursor;
1649 while (ncursor < es->linelen && argcnt--) {
1650 while (!isspace((unsigned char)es->cbuf[ncursor]) && ncursor < es->linelen)
1651 ncursor++;
1652 while (isspace((unsigned char)es->cbuf[ncursor]) && ncursor < es->linelen)
1653 ncursor++;
1655 return ncursor;
1658 static int
1659 Backword(argcnt)
1660 int argcnt;
1662 int ncursor;
1664 ncursor = es->cursor;
1665 while (ncursor > 0 && argcnt--) {
1666 while (--ncursor >= 0 && isspace((unsigned char)es->cbuf[ncursor]))
1668 while (ncursor >= 0 && !isspace((unsigned char)es->cbuf[ncursor]))
1669 ncursor--;
1670 ncursor++;
1672 return ncursor;
1675 static int
1676 Endword(argcnt)
1677 int argcnt;
1679 int ncursor;
1681 ncursor = es->cursor;
1682 while (ncursor < es->linelen - 1 && argcnt--) {
1683 while (++ncursor < es->linelen - 1 &&
1684 isspace((unsigned char)es->cbuf[ncursor]))
1686 if (ncursor < es->linelen - 1) {
1687 while (++ncursor < es->linelen &&
1688 !isspace((unsigned char)es->cbuf[ncursor]))
1690 ncursor--;
1693 return ncursor;
1696 static int
1697 grabhist(save, n)
1698 int save;
1699 int n;
1701 char *hptr;
1703 if (n < 0 || n > hlast)
1704 return -1;
1705 if (n == hlast) {
1706 restore_cbuf();
1707 ohnum = n;
1708 return 0;
1710 (void) histnum(n);
1711 if ((hptr = *histpos()) == NULL) {
1712 internal_errorf(0, "grabhist: bad history array");
1713 return -1;
1715 if (save)
1716 save_cbuf();
1717 if ((es->linelen = strlen(hptr)) >= es->cbufsize)
1718 es->linelen = es->cbufsize - 1;
1719 memmove(es->cbuf, hptr, es->linelen);
1720 es->cursor = 0;
1721 ohnum = n;
1722 return 0;
1725 static int
1726 grabsearch(save, start, fwd, pat)
1727 int save, start, fwd;
1728 char *pat;
1730 char *hptr;
1731 int hist;
1732 int anchored;
1734 if ((start == 0 && fwd == 0) || (start >= hlast-1 && fwd == 1))
1735 return -1;
1736 if (fwd)
1737 start++;
1738 else
1739 start--;
1740 anchored = *pat == '^' ? (++pat, 1) : 0;
1741 if ((hist = findhist(start, fwd, pat, anchored)) < 0) {
1742 /* if (start != 0 && fwd && match(holdbuf, pat) >= 0) { */
1743 /* XXX should FILECMP be strncmp? */
1744 if (start != 0 && fwd && FILECMP(holdbuf, pat) >= 0) {
1745 restore_cbuf();
1746 return 0;
1747 } else
1748 return -1;
1750 if (save)
1751 save_cbuf();
1752 histnum(hist);
1753 hptr = *histpos();
1754 if ((es->linelen = strlen(hptr)) >= es->cbufsize)
1755 es->linelen = es->cbufsize - 1;
1756 memmove(es->cbuf, hptr, es->linelen);
1757 es->cursor = 0;
1758 return hist;
1761 static void
1762 redraw_line(newlinex)
1763 int newlinex;
1765 (void) memset(wbuf[win], ' ', wbuf_len);
1766 if (newlinex) {
1767 x_putc('\r');
1768 x_putc('\n');
1770 vi_pprompt(0);
1771 cur_col = pwidth;
1772 morec = ' ';
1775 static void
1776 refresh(leftside)
1777 int leftside;
1779 if (leftside < 0)
1780 leftside = lastref;
1781 else
1782 lastref = leftside;
1783 if (outofwin())
1784 rewindow();
1785 display(wbuf[1 - win], wbuf[win], leftside);
1786 win = 1 - win;
1789 static int
1790 outofwin()
1792 int cur, col;
1794 if (es->cursor < es->winleft)
1795 return 1;
1796 col = 0;
1797 cur = es->winleft;
1798 while (cur < es->cursor)
1799 col = newcol((unsigned char) es->cbuf[cur++], col);
1800 if (col >= winwidth)
1801 return 1;
1802 return 0;
1805 static void
1806 rewindow()
1808 register int tcur, tcol;
1809 int holdcur1, holdcol1;
1810 int holdcur2, holdcol2;
1812 holdcur1 = holdcur2 = tcur = 0;
1813 holdcol1 = holdcol2 = tcol = 0;
1814 while (tcur < es->cursor) {
1815 if (tcol - holdcol2 > winwidth / 2) {
1816 holdcur1 = holdcur2;
1817 holdcol1 = holdcol2;
1818 holdcur2 = tcur;
1819 holdcol2 = tcol;
1821 tcol = newcol((unsigned char) es->cbuf[tcur++], tcol);
1823 while (tcol - holdcol1 > winwidth / 2)
1824 holdcol1 = newcol((unsigned char) es->cbuf[holdcur1++],
1825 holdcol1);
1826 es->winleft = holdcur1;
1829 static int
1830 newcol(ch, col)
1831 int ch, col;
1833 if (ch == '\t')
1834 return (col | 7) + 1;
1835 return col + char_len(ch);
1838 static void
1839 display(wb1, wb2, leftside)
1840 char *wb1, *wb2;
1841 int leftside;
1843 unsigned char ch;
1844 char *twb1, *twb2, mc;
1845 int cur, col, cnt;
1846 int UNINITIALIZED(ncol);
1847 int moreright;
1849 col = 0;
1850 cur = es->winleft;
1851 moreright = 0;
1852 twb1 = wb1;
1853 while (col < winwidth && cur < es->linelen) {
1854 if (cur == es->cursor && leftside)
1855 ncol = col + pwidth;
1856 if ((ch = es->cbuf[cur]) == '\t') {
1857 do {
1858 *twb1++ = ' ';
1859 } while (++col < winwidth && (col & 7) != 0);
1860 } else {
1861 if ((ch & 0x80) && Flag(FVISHOW8)) {
1862 *twb1++ = 'M';
1863 if (++col < winwidth) {
1864 *twb1++ = '-';
1865 col++;
1867 ch &= 0x7f;
1869 if (col < winwidth) {
1870 if (ch < ' ' || ch == 0x7f) {
1871 *twb1++ = '^';
1872 if (++col < winwidth) {
1873 *twb1++ = ch ^ '@';
1874 col++;
1876 } else {
1877 *twb1++ = ch;
1878 col++;
1882 if (cur == es->cursor && !leftside)
1883 ncol = col + pwidth - 1;
1884 cur++;
1886 if (cur == es->cursor)
1887 ncol = col + pwidth;
1888 if (col < winwidth) {
1889 while (col < winwidth) {
1890 *twb1++ = ' ';
1891 col++;
1893 } else
1894 moreright++;
1895 *twb1 = ' ';
1897 col = pwidth;
1898 cnt = winwidth;
1899 twb1 = wb1;
1900 twb2 = wb2;
1901 while (cnt--) {
1902 if (*twb1 != *twb2) {
1903 if (cur_col != col)
1904 ed_mov_opt(col, wb1);
1905 x_putc(*twb1);
1906 cur_col++;
1908 twb1++;
1909 twb2++;
1910 col++;
1912 if (es->winleft > 0 && moreright)
1913 /* POSIX says to use * for this but that is a globbing
1914 * character and may confuse people; + is more innocuous
1916 mc = '+';
1917 else if (es->winleft > 0)
1918 mc = '<';
1919 else if (moreright)
1920 mc = '>';
1921 else
1922 mc = ' ';
1923 if (mc != morec) {
1924 ed_mov_opt(pwidth + winwidth + 1, wb1);
1925 x_putc(mc);
1926 cur_col++;
1927 morec = mc;
1929 if (cur_col != ncol)
1930 ed_mov_opt(ncol, wb1);
1933 static void
1934 ed_mov_opt(col, wb)
1935 int col;
1936 char *wb;
1938 if (col < cur_col) {
1939 if (col + 1 < cur_col - col) {
1940 x_putc('\r');
1941 vi_pprompt(0);
1942 cur_col = pwidth;
1943 while (cur_col++ < col)
1944 x_putc(*wb++);
1945 } else {
1946 while (cur_col-- > col)
1947 x_putc('\b');
1949 } else {
1950 wb = &wb[cur_col - pwidth];
1951 while (cur_col++ < col)
1952 x_putc(*wb++);
1954 cur_col = col;
1958 /* replace word with all expansions (ie, expand word*) */
1959 static int
1960 expand_word(commandx)
1961 int commandx;
1963 static struct edstate *buf;
1964 int rval = 0;
1965 int nwords;
1966 int start, end;
1967 char **words;
1968 int i;
1970 /* Undo previous expansion */
1971 if (commandx == 0 && expanded == EXPAND && buf) {
1972 restore_edstate(es, buf);
1973 buf = 0;
1974 expanded = NONE;
1975 return 0;
1977 if (buf) {
1978 free_edstate(buf);
1979 buf = 0;
1982 nwords = x_cf_glob(XCF_COMMAND_FILE|XCF_FULLPATH,
1983 es->cbuf, es->linelen, es->cursor,
1984 &start, &end, &words, (int *) 0);
1985 if (nwords == 0) {
1986 vi_error();
1987 return -1;
1990 buf = save_edstate(es);
1991 expanded = EXPAND;
1992 del_range(start, end);
1993 es->cursor = start;
1994 for (i = 0; i < nwords; ) {
1995 if (x_escape(words[i], strlen(words[i]), x_vi_putbuf) != 0) {
1996 rval = -1;
1997 break;
1999 if (++i < nwords && putbuf(space, 1, 0) != 0) {
2000 rval = -1;
2001 break;
2004 i = buf->cursor - end;
2005 if (rval == 0 && i > 0)
2006 es->cursor += i;
2007 modified = 1; hnum = hlast;
2008 insert = INSERT;
2009 lastac = 0;
2010 refresh(0);
2011 return rval;
2014 static int
2015 complete_word(commandx, count)
2016 int commandx;
2017 int count;
2019 static struct edstate *buf;
2020 int rval = 0;
2021 int nwords;
2022 int start, end;
2023 char **words;
2024 char *match;
2025 int match_len;
2026 int is_unique;
2027 int is_command;
2029 /* Undo previous completion */
2030 if (commandx == 0 && expanded == COMPLETE && buf) {
2031 print_expansions(buf, 0);
2032 expanded = PRINT;
2033 return 0;
2035 if (commandx == 0 && expanded == PRINT && buf) {
2036 restore_edstate(es, buf);
2037 buf = 0;
2038 expanded = NONE;
2039 return 0;
2041 if (buf) {
2042 free_edstate(buf);
2043 buf = 0;
2046 /* XCF_FULLPATH for count 'cause the menu printed by print_expansions()
2047 * was done this way.
2049 nwords = x_cf_glob(XCF_COMMAND_FILE | (count ? XCF_FULLPATH : 0),
2050 es->cbuf, es->linelen, es->cursor,
2051 &start, &end, &words, &is_command);
2052 if (nwords == 0) {
2053 vi_error();
2054 return -1;
2056 if (count) {
2057 int i;
2059 count--;
2060 if (count >= nwords) {
2061 vi_error();
2062 x_print_expansions(nwords, words, is_command);
2063 x_free_words(nwords, words);
2064 redraw_line(0);
2065 return -1;
2068 * Expand the count'th word to its basename
2070 if (is_command) {
2071 match = words[count]
2072 + x_basename(words[count], (char *) 0);
2073 /* If more than one possible match, use full path */
2074 for (i = 0; i < nwords; i++)
2075 if (i != count &&
2076 FILECMP(words[i]
2077 + x_basename(words[i], (char *) 0),
2078 match) == 0)
2080 match = words[count];
2081 break;
2083 } else
2084 match = words[count];
2085 match_len = strlen(match);
2086 is_unique = 1;
2087 /* expanded = PRINT; next call undo */
2088 } else {
2089 match = words[0];
2090 match_len = x_longest_prefix(nwords, words);
2091 expanded = COMPLETE; /* next call will list completions */
2092 is_unique = nwords == 1;
2095 buf = save_edstate(es);
2096 del_range(start, end);
2097 es->cursor = start;
2099 /* escape all shell-sensitive characters and put the result into
2100 * command buffer */
2101 rval = x_escape(match, match_len, x_vi_putbuf);
2103 if (rval == 0 && is_unique) {
2104 /* If exact match, don't undo. Allows directory completions
2105 * to be used (ie, complete the next portion of the path).
2107 expanded = NONE;
2109 /* If not a directory, add a space to the end... */
2110 if (match_len > 0 && !ISDIRSEP(match[match_len - 1]))
2111 rval = putbuf(space, 1, 0);
2113 x_free_words(nwords, words);
2115 modified = 1; hnum = hlast;
2116 insert = INSERT;
2117 lastac = 0; /* prevent this from being redone... */
2118 refresh(0);
2120 return rval;
2123 static int
2124 print_expansions(ex, commandx)
2125 struct edstate *ex;
2126 int commandx;
2128 int nwords;
2129 int start, end;
2130 char **words;
2131 int is_command;
2133 nwords = x_cf_glob(XCF_COMMAND_FILE|XCF_FULLPATH,
2134 ex->cbuf, ex->linelen, ex->cursor,
2135 &start, &end, &words, &is_command);
2136 if (nwords == 0) {
2137 vi_error();
2138 return -1;
2140 x_print_expansions(nwords, words, is_command);
2141 x_free_words(nwords, words);
2142 redraw_line(0);
2143 return 0;
2146 /* How long is char when displayed (not counting tabs) */
2147 static int
2148 char_len(c)
2149 int c;
2151 int len = 1;
2153 if ((c & 0x80) && Flag(FVISHOW8)) {
2154 len += 2;
2155 c &= 0x7f;
2157 if (c < ' ' || c == 0x7f)
2158 len++;
2159 return len;
2162 /* Similar to x_zotc(emacs.c), but no tab weirdness */
2163 static void
2164 x_vi_zotc(c)
2165 int c;
2167 if (Flag(FVISHOW8) && (c & 0x80)) {
2168 x_puts("M-");
2169 c &= 0x7f;
2171 if (c < ' ' || c == 0x7f) {
2172 x_putc('^');
2173 c ^= '@';
2175 x_putc(c);
2178 static void
2179 vi_pprompt(full)
2180 int full;
2182 pprompt(prompt + (full ? 0 : prompt_skip), prompt_trunc);
2185 static void
2186 vi_error()
2188 /* Beem out of any macros as soon as an error occurs */
2189 vi_macro_reset();
2190 x_putc(BEL);
2191 x_flush();
2194 static void
2195 vi_macro_reset()
2197 if (macro.p) {
2198 afree(macro.buf, APERM);
2199 memset((char *) &macro, 0, sizeof(macro));
2203 #endif /* VI */