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