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