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