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