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