1 /* $OpenBSD: vi.c,v 1.56 2018/03/15 16:51:29 anton Exp $ */
5 * written by John Rochester (initially for nsh)
6 * bludgeoned to fit pdksh by Larry Bouzane, Jeff Sparkes & Eric Gisin
12 #include <sys/stat.h> /* completion */
21 #define CTRL(c) (c & 0x1f)
24 char *cbuf
; /* main buffer to build the command line */
25 int cbufsize
; /* number of bytes allocated for cbuf */
26 int linelen
; /* current number of bytes in cbuf */
27 int winleft
; /* first byte# in cbuf to be displayed */
28 int cursor
; /* byte# in cbuf having the cursor */
32 static int vi_hook(int);
33 static void vi_reset(char *, size_t);
34 static int nextstate(int);
35 static int vi_insert(int);
36 static int vi_cmd(int, const char *);
37 static int domove(int, const char *, int);
38 static int redo_insert(int);
39 static void yank_range(int, int);
40 static int bracktype(int);
41 static void save_cbuf(void);
42 static void restore_cbuf(void);
43 static void edit_reset(char *, size_t);
44 static int putbuf(const char *, int, int);
45 static void del_range(int, int);
46 static int findch(int, int, int, int);
47 static int forwword(int);
48 static int backword(int);
49 static int endword(int);
50 static int Forwword(int);
51 static int Backword(int);
52 static int Endword(int);
53 static int grabhist(int, int);
54 static int grabsearch(int, int, int, char *);
55 static void redraw_line(int);
56 static void refresh(int);
57 static int outofwin(void);
58 static void rewindow(void);
59 static int newcol(int, int);
60 static void display(char *, char *, int);
61 static void ed_mov_opt(int, char *);
62 static int expand_word(int);
63 static int complete_word(int, int);
64 static int print_expansions(struct edstate
*);
65 static int char_len(int);
66 static void x_vi_zotc(int);
67 static void vi_pprompt(int);
68 static void vi_error(void);
69 static void vi_macro_reset(void);
70 static int x_vi_putbuf(const char *, size_t);
71 static int isu8cont(unsigned char);
73 #define C_ 0x1 /* a valid command that isn't a M_, E_, U_ */
74 #define M_ 0x2 /* movement command (h, l, etc.) */
75 #define E_ 0x4 /* extended command (c, d, y) */
76 #define X_ 0x8 /* long command (@, f, F, t, T, etc.) */
77 #define U_ 0x10 /* an UN-undoable command (that isn't a M_) */
78 #define B_ 0x20 /* bad command (^@) */
79 #define Z_ 0x40 /* repeat count defaults to 0 (not 1) */
80 #define S_ 0x80 /* search (/, ?) */
82 #define is_bad(c) (classify[(c)&0x7f]&B_)
83 #define is_cmd(c) (classify[(c)&0x7f]&(M_|E_|C_|U_))
84 #define is_move(c) (classify[(c)&0x7f]&M_)
85 #define is_extend(c) (classify[(c)&0x7f]&E_)
86 #define is_long(c) (classify[(c)&0x7f]&X_)
87 #define is_undoable(c) (!(classify[(c)&0x7f]&U_))
88 #define is_srch(c) (classify[(c)&0x7f]&S_)
89 #define is_zerocount(c) (classify[(c)&0x7f]&Z_)
91 const unsigned char classify
[128] = {
93 /* 0 ^@ ^A ^B ^C ^D ^E ^F ^G */
94 B_
, 0, 0, 0, 0, C_
|U_
, C_
|Z_
, 0,
95 /* 01 ^H ^I ^J ^K ^L ^M ^N ^O */
96 M_
, C_
|Z_
, 0, 0, C_
|U_
, 0, C_
, 0,
97 /* 02 ^P ^Q ^R ^S ^T ^U ^V ^W */
98 C_
, 0, C_
|U_
, 0, 0, 0, C_
, 0,
99 /* 03 ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
100 C_
, 0, 0, C_
|Z_
, 0, 0, 0, 0,
101 /* 04 <space> ! " # $ % & ' */
102 M_
, 0, 0, C_
, M_
, M_
, 0, 0,
103 /* 05 ( ) * + , - . / */
104 0, 0, C_
, C_
, M_
, C_
, 0, C_
|S_
,
105 /* 06 0 1 2 3 4 5 6 7 */
106 M_
, 0, 0, 0, 0, 0, 0, 0,
107 /* 07 8 9 : ; < = > ? */
108 0, 0, 0, M_
, 0, C_
, 0, C_
|S_
,
109 /* 010 @ A B C D E F G */
110 C_
|X_
, C_
, M_
, C_
, C_
, M_
, M_
|X_
, C_
|U_
|Z_
,
111 /* 011 H I J K L M N O */
112 0, C_
, 0, 0, 0, 0, C_
|U_
, 0,
113 /* 012 P Q R S T U V W */
114 C_
, 0, C_
, C_
, M_
|X_
, C_
, 0, M_
,
115 /* 013 X Y Z [ \ ] ^ _ */
116 C_
, C_
|U_
, 0, 0, C_
|Z_
, 0, M_
, C_
|Z_
,
117 /* 014 ` a b c d e f g */
118 0, C_
, M_
, E_
, E_
, M_
, M_
|X_
, C_
|Z_
,
119 /* 015 h i j k l m n o */
120 M_
, C_
, C_
|U_
, C_
|U_
, M_
, 0, C_
|U_
, 0,
121 /* 016 p q r s t u v w */
122 C_
, 0, X_
, C_
, M_
|X_
, C_
|U_
, C_
|U_
|Z_
,M_
,
123 /* 017 x y z { | } ~ ^? */
124 C_
, E_
|U_
, 0, 0, M_
|Z_
, 0, C_
, 0
133 #define VNORMAL 0 /* command, insert or replace mode */
134 #define VARG1 1 /* digit prefix (first, eg, 5l) */
135 #define VEXTCMD 2 /* cmd + movement (eg, cl) */
136 #define VARG2 3 /* digit prefix (second, eg, 2c3l) */
137 #define VXCH 4 /* f, F, t, T, @ */
138 #define VFAIL 5 /* bad command */
139 #define VCMD 6 /* single char command (eg, X) */
140 #define VREDO 7 /* . */
141 #define VLIT 8 /* ^V */
142 #define VSEARCH 9 /* /, ? */
144 static char undocbuf
[LINE
];
146 static struct edstate
*save_edstate(struct edstate
*old
);
147 static void restore_edstate(struct edstate
*old
, struct edstate
*new);
148 static void free_edstate(struct edstate
*old
);
150 static struct edstate ebuf
;
151 static struct edstate undobuf
= { undocbuf
, LINE
, 0, 0, 0 };
153 static struct edstate
*es
; /* current editor state */
154 static struct edstate
*undo
;
156 static char ibuf
[LINE
]; /* input buffer */
157 static int first_insert
; /* set when starting in insert mode */
158 static int saved_inslen
; /* saved inslen for first insert */
159 static int inslen
; /* length of input buffer */
160 static int srchlen
; /* number of bytes in search pattern */
161 static char ybuf
[LINE
]; /* yank buffer */
162 static int yanklen
; /* length of yank buffer */
163 static int fsavecmd
= ' '; /* last find command */
164 static int fsavech
; /* character to find */
165 static char lastcmd
[MAXVICMD
]; /* last non-move command */
166 static int lastac
; /* argcnt for lastcmd */
167 static int lastsearch
= ' '; /* last search command */
168 static char srchpat
[SRCHLEN
]; /* last search pattern */
169 static int insert
; /* mode: INSERT, REPLACE, or 0 */
170 static int hnum
; /* position in history */
171 static int ohnum
; /* history line copied (after mod) */
172 static int hlast
; /* 1 past last position in history */
173 static int modified
; /* buffer has been "modified" */
176 /* Information for keeping track of macros that are being expanded.
177 * The format of buf is the alias contents followed by a null byte followed
178 * by the name (letter) of the alias. The end of the buffer is marked by
179 * a double null. The name of the alias is stored so recursive macros can
183 unsigned char *p
; /* current position in buf */
184 unsigned char *buf
; /* pointer to macro(s) being expanded */
185 int len
; /* how much data in buffer */
187 static struct macro_state macro
;
189 enum expand_mode
{ NONE
, EXPAND
, COMPLETE
, PRINT
};
190 static enum expand_mode expanded
= NONE
;/* last input was expanded */
193 x_vi(char *buf
, size_t len
)
197 vi_reset(buf
, len
> LINE
? LINE
: len
);
202 c
= (unsigned char)*macro
.p
++;
203 /* end of current macro? */
205 /* more macros left to finish? */
208 /* must be the end of all the macros */
218 if (c
== edchars
.intr
|| c
== edchars
.quit
) {
219 /* pretend we got an interrupt */
222 trapsig(c
== edchars
.intr
? SIGINT
: SIGQUIT
);
225 } else if (c
== edchars
.eof
) {
226 if (es
->linelen
== 0) {
227 x_vi_zotc(edchars
.eof
);
239 x_putc('\r'); x_putc('\n'); x_flush();
241 if (c
== -1 || len
<= (size_t)es
->linelen
)
245 memmove(buf
, es
->cbuf
, es
->linelen
);
247 buf
[es
->linelen
++] = '\n';
255 static char curcmd
[MAXVICMD
], locpat
[SRCHLEN
];
256 static int cmdlen
, argc1
, argc2
;
262 if (ch
== CTRL('v')) {
266 switch (vi_insert(ch
)) {
276 refresh(insert
!= 0);
282 if (ch
== '\r' || ch
== '\n')
286 if (ch
>= '1' && ch
<= '9') {
290 curcmd
[cmdlen
++] = ch
;
291 state
= nextstate(ch
);
292 if (state
== VSEARCH
) {
297 if (putbuf("/", 1, 0) != 0)
299 } else if (putbuf("?", 1, 0) != 0)
309 del_range(es
->cursor
, es
->cursor
+ 1);
312 es
->cbuf
[es
->cursor
++] = ch
;
319 argc1
= argc1
* 10 + ch
- '0';
321 curcmd
[cmdlen
++] = ch
;
322 state
= nextstate(ch
);
328 if (ch
>= '1' && ch
<= '9') {
333 curcmd
[cmdlen
++] = ch
;
336 else if (is_move(ch
))
337 state
= nextstate(ch
);
345 argc2
= argc2
* 10 + ch
- '0';
351 curcmd
[cmdlen
++] = ch
;
354 else if (is_move(ch
))
355 state
= nextstate(ch
);
365 curcmd
[cmdlen
++] = ch
;
371 if (ch
== '\r' || ch
== '\n' /*|| ch == CTRL('[')*/ ) {
373 /* Repeat last search? */
382 locpat
[srchlen
] = '\0';
383 (void) strlcpy(srchpat
, locpat
, sizeof srchpat
);
386 } else if (ch
== edchars
.erase
|| ch
== CTRL('h')) {
390 es
->linelen
-= char_len(
391 (unsigned char)locpat
[srchlen
]);
392 } while (srchlen
> 0 &&
393 isu8cont(locpat
[srchlen
]));
394 es
->cursor
= es
->linelen
;
401 } else if (ch
== edchars
.kill
) {
407 } else if (ch
== edchars
.werase
) {
408 struct edstate new_es
, *save_es
;
413 new_es
.cbuf
= locpat
;
420 for (i
= srchlen
; --i
>= n
; )
421 es
->linelen
-= char_len((unsigned char)locpat
[i
]);
423 es
->cursor
= es
->linelen
;
427 if (srchlen
== SRCHLEN
- 1)
430 locpat
[srchlen
++] = ch
;
431 if ((ch
& 0x80) && Flag(FVISHOW8
)) {
432 if (es
->linelen
+ 2 > es
->cbufsize
)
434 es
->cbuf
[es
->linelen
++] = 'M';
435 es
->cbuf
[es
->linelen
++] = '-';
438 if (ch
< ' ' || ch
== 0x7f) {
439 if (es
->linelen
+ 2 > es
->cbufsize
)
441 es
->cbuf
[es
->linelen
++] = '^';
442 es
->cbuf
[es
->linelen
++] = ch
^ '@';
444 if (es
->linelen
>= es
->cbufsize
)
446 es
->cbuf
[es
->linelen
++] = ch
;
448 es
->cursor
= es
->linelen
;
459 switch (vi_cmd(argc1
, curcmd
)) {
467 refresh(insert
!= 0);
473 /* back from a 'v' command - don't redraw the screen */
482 switch (vi_cmd(lastac
, lastcmd
)) {
489 if (lastcmd
[0] == 's' || lastcmd
[0] == 'c' ||
491 if (redo_insert(1) != 0)
494 if (redo_insert(lastac
) != 0)
504 /* back from a 'v' command - can't happen */
518 vi_reset(char *buf
, size_t len
)
521 ohnum
= hnum
= hlast
= histnum(-1) + 1;
523 saved_inslen
= inslen
;
528 edit_reset(buf
, len
);
536 else if (is_srch(ch
))
538 else if (is_long(ch
))
553 if (ch
== edchars
.erase
|| ch
== CTRL('h')) {
554 if (insert
== REPLACE
) {
555 if (es
->cursor
== undo
->cursor
) {
560 if (es
->cursor
== 0) {
561 /* x_putc(BEL); no annoying bell here */
565 tcursor
= es
->cursor
- 1;
566 while(tcursor
> 0 && isu8cont(es
->cbuf
[tcursor
]))
568 if (insert
== INSERT
)
569 memmove(es
->cbuf
+ tcursor
, es
->cbuf
+ es
->cursor
,
570 es
->linelen
- es
->cursor
);
571 if (insert
== REPLACE
&& es
->cursor
< undo
->linelen
)
572 memcpy(es
->cbuf
+ tcursor
, undo
->cbuf
+ tcursor
,
573 es
->cursor
- tcursor
);
575 es
->linelen
-= es
->cursor
- tcursor
;
576 if (inslen
< es
->cursor
- tcursor
)
579 inslen
-= es
->cursor
- tcursor
;
580 es
->cursor
= tcursor
;
584 if (ch
== edchars
.kill
) {
585 if (es
->cursor
!= 0) {
587 memmove(es
->cbuf
, &es
->cbuf
[es
->cursor
],
588 es
->linelen
- es
->cursor
);
589 es
->linelen
-= es
->cursor
;
595 if (ch
== edchars
.werase
) {
596 if (es
->cursor
!= 0) {
597 tcursor
= backword(1);
598 memmove(&es
->cbuf
[tcursor
], &es
->cbuf
[es
->cursor
],
599 es
->linelen
- es
->cursor
);
600 es
->linelen
-= es
->cursor
- tcursor
;
601 if (inslen
< es
->cursor
- tcursor
)
604 inslen
-= es
->cursor
- tcursor
;
605 es
->cursor
= tcursor
;
610 /* If any chars are entered before escape, trash the saved insert
611 * buffer (if user inserts & deletes char, ibuf gets trashed and
612 * we don't want to use it)
614 if (first_insert
&& ch
!= CTRL('['))
629 inslen
= saved_inslen
;
630 return redo_insert(0);
635 if (lastcmd
[0] == 's' || lastcmd
[0] == 'c' ||
637 return redo_insert(0);
639 return redo_insert(lastac
- 1);
641 /* { Begin nonstandard vi commands */
651 print_expansions(es
);
655 if (Flag(FVITABCOMPLETE
)) {
660 /* End nonstandard vi commands } */
663 if (es
->linelen
>= es
->cbufsize
- 1)
666 if (insert
== INSERT
) {
667 memmove(&es
->cbuf
[es
->cursor
+1], &es
->cbuf
[es
->cursor
],
668 es
->linelen
- es
->cursor
);
671 es
->cbuf
[es
->cursor
++] = ch
;
672 if (insert
== REPLACE
&& es
->cursor
> es
->linelen
)
680 vi_cmd(int argcnt
, const char *cmd
)
683 int cur
, c1
, c2
, c3
= 0;
687 if (argcnt
== 0 && !is_zerocount(*cmd
))
691 if ((cur
= domove(argcnt
, cmd
, 0)) >= 0) {
692 if (cur
== es
->linelen
&& cur
!= 0)
693 while (isu8cont(es
->cbuf
[--cur
]))
699 /* Don't save state in middle of macro.. */
700 if (is_undoable(*cmd
) && !macro
.p
) {
701 undo
->winleft
= es
->winleft
;
702 memmove(undo
->cbuf
, es
->cbuf
, es
->linelen
);
703 undo
->linelen
= es
->linelen
;
704 undo
->cursor
= es
->cursor
;
706 memmove(lastcmd
, cmd
, MAXVICMD
);
717 static char alias
[] = "_\0";
722 /* lookup letter in alias list... */
724 ap
= ktsearch(&aliases
, alias
, hash(alias
));
725 if (!cmd
[1] || !ap
|| !(ap
->flag
& ISSET
))
727 /* check if this is a recursive call... */
728 if ((p
= (char *) macro
.p
))
729 while ((p
= strchr(p
, '\0')) && p
[1])
732 /* insert alias into macro buffer */
733 nlen
= strlen(ap
->val
.s
) + 1;
734 olen
= !macro
.p
? 2 :
735 macro
.len
- (macro
.p
- macro
.buf
);
736 nbuf
= alloc(nlen
+ 1 + olen
, APERM
);
737 memcpy(nbuf
, ap
->val
.s
, nlen
);
738 nbuf
[nlen
++] = cmd
[1];
740 memcpy(nbuf
+ nlen
, macro
.p
, olen
);
741 afree(macro
.buf
, APERM
);
747 macro
.p
= macro
.buf
= (unsigned char *) nbuf
;
753 modified
= 1; hnum
= hlast
;
754 if (es
->linelen
!= 0)
755 while (isu8cont(es
->cbuf
[++es
->cursor
]))
761 modified
= 1; hnum
= hlast
;
763 es
->cursor
= es
->linelen
;
768 es
->cursor
= domove(1, "^", 1);
769 del_range(es
->cursor
, es
->linelen
);
770 modified
= 1; hnum
= hlast
;
780 if (*cmd
== cmd
[1]) {
781 c1
= *cmd
== 'c' ? domove(1, "^", 1) : 0;
783 } else if (!is_move(cmd
[1]))
786 if ((ncursor
= domove(argcnt
, &cmd
[1], 1)) < 0)
789 (cmd
[1]=='w' || cmd
[1]=='W') &&
790 !isspace((unsigned char)es
->cbuf
[es
->cursor
])) {
792 (unsigned char)es
->cbuf
[--ncursor
]))
796 if (ncursor
> es
->cursor
) {
806 if (*cmd
!= 'c' && c1
!= c2
)
813 modified
= 1; hnum
= hlast
;
819 modified
= 1; hnum
= hlast
;
820 if (es
->linelen
!= 0)
822 while (putbuf(ybuf
, yanklen
, 0) == 0 && --argcnt
> 0)
831 modified
= 1; hnum
= hlast
;
833 while (putbuf(ybuf
, yanklen
, 0) == 0 && --argcnt
> 0)
835 if (any
&& es
->cursor
!= 0)
842 modified
= 1; hnum
= hlast
;
843 del_range(es
->cursor
, es
->linelen
);
848 yank_range(es
->cursor
, es
->linelen
);
849 del_range(es
->cursor
, es
->linelen
);
862 argcnt
= hlast
- (source
->line
- argcnt
);
863 if (grabhist(modified
, argcnt
- 1) < 0)
872 modified
= 1; hnum
= hlast
;
877 modified
= 1; hnum
= hlast
;
878 es
->cursor
= domove(1, "^", 1);
885 if (grabhist(modified
, hnum
+ argcnt
) < 0)
896 if (grabhist(modified
, hnum
- argcnt
) < 0)
905 if (es
->linelen
== 0)
907 modified
= 1; hnum
= hlast
;
912 for (cur
= es
->cursor
;
913 cur
< es
->linelen
; cur
++) {
914 if (!isu8cont(es
->cbuf
[cur
]))
922 del_range(es
->cursor
, cur
);
924 putbuf(&cmd
[1], 1, 0);
925 while (es
->cursor
> 0)
926 if (!isu8cont(es
->cbuf
[--es
->cursor
]))
928 es
->cbuf
[es
->linelen
] = '\0';
933 modified
= 1; hnum
= hlast
;
938 if (es
->linelen
== 0)
940 modified
= 1; hnum
= hlast
;
941 for (cur
= es
->cursor
; cur
< es
->linelen
; cur
++)
942 if (!isu8cont(es
->cbuf
[cur
]))
945 del_range(es
->cursor
, cur
);
950 if (es
->linelen
== 0 && argcnt
== 0)
954 es
->cbuf
[es
->linelen
] = '\0';
956 histsave(source
->line
, es
->cbuf
, 1);
958 argcnt
= source
->line
+ 1
961 shf_snprintf(es
->cbuf
, es
->cbufsize
,
962 argcnt
? "%s %d" : "%s",
963 "fc -e ${VISUAL:-${EDITOR:-vi}} --",
965 es
->linelen
= strlen(es
->cbuf
);
969 if (es
->linelen
== 0)
971 modified
= 1; hnum
= hlast
;
972 for (cur
= es
->cursor
; cur
< es
->linelen
; cur
++)
973 if (!isu8cont(es
->cbuf
[cur
]))
976 yank_range(es
->cursor
, cur
);
977 del_range(es
->cursor
, cur
);
983 modified
= 1; hnum
= hlast
;
984 for (cur
= es
->cursor
; cur
> 0; cur
--)
985 if (!isu8cont(es
->cbuf
[cur
]))
988 yank_range(cur
, es
->cursor
);
989 del_range(cur
, es
->cursor
);
1002 if (grabhist(modified
, ohnum
) < 0)
1019 if (lastsearch
== ' ')
1021 if (lastsearch
== '?')
1027 if ((c2
= grabsearch(modified
, hnum
,
1028 c1
, srchpat
)) < 0) {
1044 if (histnum(-1) < 0)
1047 #define issp(c) (isspace((unsigned char)(c)) || (c) == '\n')
1049 while (*p
&& issp(*p
))
1051 while (*p
&& --argcnt
) {
1052 while (*p
&& !issp(*p
))
1054 while (*p
&& issp(*p
))
1074 modified
= 1; hnum
= hlast
;
1075 if (es
->cursor
!= es
->linelen
)
1077 while (*p
&& !issp(*p
)) {
1081 if (putbuf(" ", 1, 0) != 0)
1083 else if (putbuf(sp
, argcnt
, 0) != 0)
1086 if (es
->cursor
!= 0)
1099 if (es
->linelen
== 0)
1101 for (i
= 0; i
< argcnt
; i
++) {
1102 p
= &es
->cbuf
[es
->cursor
];
1103 c
= (unsigned char)*p
;
1105 modified
= 1; hnum
= hlast
;
1107 } else if (isupper(c
)) {
1108 modified
= 1; hnum
= hlast
;
1111 if (es
->cursor
< es
->linelen
- 1)
1119 int ret
= x_do_comment(es
->cbuf
, es
->cbufsize
,
1126 case '=': /* at&t ksh */
1127 case CTRL('e'): /* Nonstandard vi/ksh */
1128 print_expansions(es
);
1132 case CTRL('i'): /* Nonstandard vi/ksh */
1133 if (!Flag(FVITABCOMPLETE
))
1135 complete_word(1, argcnt
);
1138 case CTRL('['): /* some annoying at&t ksh's */
1139 if (!Flag(FVIESCCOMPLETE
))
1141 case '\\': /* at&t ksh */
1142 case CTRL('f'): /* Nonstandard vi/ksh */
1143 complete_word(1, argcnt
);
1147 case '*': /* at&t ksh */
1148 case CTRL('x'): /* Nonstandard vi/ksh */
1152 if (insert
== 0 && es
->cursor
>= es
->linelen
)
1153 while (es
->cursor
> 0)
1154 if (!isu8cont(es
->cbuf
[--es
->cursor
]))
1161 domove(int argcnt
, const char *cmd
, int sub
)
1163 int bcount
, i
= 0, t
;
1170 if (!sub
&& es
->cursor
== 0)
1172 ncursor
= (*cmd
== 'b' ? backword
: Backword
)(argcnt
);
1177 if (!sub
&& es
->cursor
+ 1 >= es
->linelen
)
1179 ncursor
= (*cmd
== 'e' ? endword
: Endword
)(argcnt
);
1181 while (isu8cont((unsigned char)es
->cbuf
[--ncursor
]))
1195 if (fsavecmd
== ' ')
1197 i
= fsavecmd
== 'f' || fsavecmd
== 'F';
1201 if ((ncursor
= findch(fsavech
, argcnt
, t
, i
)) < 0)
1209 if (!sub
&& es
->cursor
== 0)
1211 for (ncursor
= es
->cursor
; ncursor
> 0; ncursor
--)
1212 if (!isu8cont(es
->cbuf
[ncursor
]))
1219 if (!sub
&& es
->cursor
+ 1 >= es
->linelen
)
1221 for (ncursor
= es
->cursor
; ncursor
< es
->linelen
; ncursor
++)
1222 if (!isu8cont(es
->cbuf
[ncursor
]))
1229 if (!sub
&& es
->cursor
+ 1 >= es
->linelen
)
1231 ncursor
= (*cmd
== 'w' ? forwword
: Forwword
)(argcnt
);
1240 while (ncursor
< es
->linelen
- 1 &&
1241 isspace((unsigned char)es
->cbuf
[ncursor
]))
1247 if (ncursor
> es
->linelen
)
1248 ncursor
= es
->linelen
;
1251 while (isu8cont(es
->cbuf
[ncursor
]))
1256 ncursor
= es
->linelen
;
1260 ncursor
= es
->cursor
;
1261 while (ncursor
< es
->linelen
&&
1262 (i
= bracktype(es
->cbuf
[ncursor
])) == 0)
1264 if (ncursor
== es
->linelen
)
1269 if (++ncursor
>= es
->linelen
)
1275 t
= bracktype(es
->cbuf
[ncursor
]);
1280 } while (bcount
!= 0);
1292 redo_insert(int count
)
1295 if (putbuf(ibuf
, inslen
, insert
==REPLACE
) != 0)
1298 while (isu8cont(es
->cbuf
[--es
->cursor
]))
1305 yank_range(int a
, int b
)
1309 memmove(ybuf
, &es
->cbuf
[a
], yanklen
);
1341 * Non user interface editor routines below here
1344 static int cur_col
; /* current display column */
1345 static int pwidth
; /* display columns needed for prompt */
1346 static int prompt_trunc
; /* how much of prompt to truncate */
1347 static int prompt_skip
; /* how much of prompt to skip */
1348 static int winwidth
; /* available column positions */
1349 static char *wbuf
[2]; /* current & previous window buffer */
1350 static int wbuf_len
; /* length of window buffers (x_cols-3)*/
1351 static int win
; /* number of window buffer in use */
1352 static char morec
; /* more character at right of window */
1353 static char holdbuf
[LINE
]; /* place to hold last edit buffer */
1354 static int holdlen
; /* length of holdbuf */
1359 memmove(holdbuf
, es
->cbuf
, es
->linelen
);
1360 holdlen
= es
->linelen
;
1361 holdbuf
[holdlen
] = '\0';
1368 es
->linelen
= holdlen
;
1369 memmove(es
->cbuf
, holdbuf
, holdlen
);
1372 /* return a new edstate */
1373 static struct edstate
*
1374 save_edstate(struct edstate
*old
)
1376 struct edstate
*new;
1378 new = alloc(sizeof(struct edstate
), APERM
);
1379 new->cbuf
= alloc(old
->cbufsize
, APERM
);
1380 memcpy(new->cbuf
, old
->cbuf
, old
->linelen
);
1381 new->cbufsize
= old
->cbufsize
;
1382 new->linelen
= old
->linelen
;
1383 new->cursor
= old
->cursor
;
1384 new->winleft
= old
->winleft
;
1389 restore_edstate(struct edstate
*new, struct edstate
*old
)
1391 memcpy(new->cbuf
, old
->cbuf
, old
->linelen
);
1392 new->linelen
= old
->linelen
;
1393 new->cursor
= old
->cursor
;
1394 new->winleft
= old
->winleft
;
1399 free_edstate(struct edstate
*old
)
1401 afree(old
->cbuf
, APERM
);
1408 edit_reset(char *buf
, size_t len
)
1416 undo
->cbufsize
= len
;
1418 es
->linelen
= undo
->linelen
= 0;
1419 es
->cursor
= undo
->cursor
= 0;
1420 es
->winleft
= undo
->winleft
= 0;
1422 cur_col
= pwidth
= promptlen(prompt
, &p
);
1423 prompt_skip
= p
- prompt
;
1424 if (pwidth
> x_cols
- 3 - MIN_EDIT_SPACE
) {
1425 cur_col
= x_cols
- 3 - MIN_EDIT_SPACE
;
1426 prompt_trunc
= pwidth
- cur_col
;
1427 pwidth
-= prompt_trunc
;
1430 if (!wbuf_len
|| wbuf_len
!= x_cols
- 3) {
1431 wbuf_len
= x_cols
- 3;
1432 wbuf
[0] = aresize(wbuf
[0], wbuf_len
, APERM
);
1433 wbuf
[1] = aresize(wbuf
[1], wbuf_len
, APERM
);
1435 (void) memset(wbuf
[0], ' ', wbuf_len
);
1436 (void) memset(wbuf
[1], ' ', wbuf_len
);
1437 winwidth
= x_cols
- pwidth
- 3;
1444 * this is used for calling x_escape() in complete_word()
1447 x_vi_putbuf(const char *s
, size_t len
)
1449 return putbuf(s
, len
, 0);
1453 putbuf(const char *buf
, int len
, int repl
)
1458 if (es
->cursor
+ len
>= es
->cbufsize
)
1460 if (es
->cursor
+ len
> es
->linelen
)
1461 es
->linelen
= es
->cursor
+ len
;
1463 if (es
->linelen
+ len
>= es
->cbufsize
)
1465 memmove(&es
->cbuf
[es
->cursor
+ len
], &es
->cbuf
[es
->cursor
],
1466 es
->linelen
- es
->cursor
);
1469 memmove(&es
->cbuf
[es
->cursor
], buf
, len
);
1475 del_range(int a
, int b
)
1477 if (es
->linelen
!= b
)
1478 memmove(&es
->cbuf
[a
], &es
->cbuf
[b
], es
->linelen
- b
);
1479 es
->linelen
-= b
- a
;
1483 findch(int ch
, int cnt
, int forw
, int incl
)
1487 if (es
->linelen
== 0)
1489 ncursor
= es
->cursor
;
1493 if (++ncursor
== es
->linelen
)
1499 } while (es
->cbuf
[ncursor
] != ch
);
1510 /* Move right one character, and then to the beginning of the next word. */
1512 forwword(int argcnt
)
1514 int ncursor
, skip_space
, want_letnum
;
1517 ncursor
= es
->cursor
;
1518 while (ncursor
< es
->linelen
&& argcnt
--) {
1522 while (++ncursor
< es
->linelen
) {
1523 uc
= es
->cbuf
[ncursor
];
1527 } else if (skip_space
)
1531 if (want_letnum
== -1)
1532 want_letnum
= letnum(uc
);
1533 else if (want_letnum
!= letnum(uc
))
1540 /* Move left one character, and then to the beginning of the word. */
1542 backword(int argcnt
)
1544 int ncursor
, skip_space
, want_letnum
;
1547 ncursor
= es
->cursor
;
1548 while (ncursor
> 0 && argcnt
--) {
1551 while (ncursor
-- > 0) {
1552 uc
= es
->cbuf
[ncursor
];
1562 if (want_letnum
== -1)
1563 want_letnum
= letnum(uc
);
1564 else if (want_letnum
!= letnum(uc
))
1572 /* Move right one character, and then to the byte after the word. */
1576 int ncursor
, skip_space
, want_letnum
;
1579 ncursor
= es
->cursor
;
1580 while (ncursor
< es
->linelen
&& argcnt
--) {
1583 while (++ncursor
< es
->linelen
) {
1584 uc
= es
->cbuf
[ncursor
];
1594 if (want_letnum
== -1)
1595 want_letnum
= letnum(uc
);
1596 else if (want_letnum
!= letnum(uc
))
1603 /* Move right one character, and then to the beginning of the next big word. */
1605 Forwword(int argcnt
)
1609 ncursor
= es
->cursor
;
1610 while (ncursor
< es
->linelen
&& argcnt
--) {
1611 while (!isspace((unsigned char)es
->cbuf
[ncursor
]) &&
1612 ncursor
< es
->linelen
)
1614 while (isspace((unsigned char)es
->cbuf
[ncursor
]) &&
1615 ncursor
< es
->linelen
)
1621 /* Move left one character, and then to the beginning of the big word. */
1623 Backword(int argcnt
)
1627 ncursor
= es
->cursor
;
1628 while (ncursor
> 0 && argcnt
--) {
1629 while (--ncursor
>= 0 &&
1630 isspace((unsigned char)es
->cbuf
[ncursor
]))
1632 while (ncursor
>= 0 &&
1633 !isspace((unsigned char)es
->cbuf
[ncursor
]))
1640 /* Move right one character, and then to the byte after the big word. */
1646 ncursor
= es
->cursor
;
1647 while (ncursor
< es
->linelen
&& argcnt
--) {
1648 while (++ncursor
< es
->linelen
&&
1649 isspace((unsigned char)es
->cbuf
[ncursor
]))
1651 while (ncursor
< es
->linelen
&&
1652 !isspace((unsigned char)es
->cbuf
[ncursor
]))
1659 grabhist(int save
, int n
)
1663 if (n
< 0 || n
> hlast
)
1671 if ((hptr
= *histpos()) == NULL
) {
1672 internal_warningf("%s: bad history array", __func__
);
1677 if ((es
->linelen
= strlen(hptr
)) >= es
->cbufsize
)
1678 es
->linelen
= es
->cbufsize
- 1;
1679 memmove(es
->cbuf
, hptr
, es
->linelen
);
1686 grabsearch(int save
, int start
, int fwd
, char *pat
)
1692 if ((start
== 0 && fwd
== 0) || (start
>= hlast
-1 && fwd
== 1))
1698 anchored
= *pat
== '^' ? (++pat
, 1) : 0;
1699 if ((hist
= findhist(start
, fwd
, pat
, anchored
)) < 0) {
1700 /* if (start != 0 && fwd && match(holdbuf, pat) >= 0) { */
1701 /* XXX should strcmp be strncmp? */
1702 if (start
!= 0 && fwd
&& strcmp(holdbuf
, pat
) >= 0) {
1712 if ((es
->linelen
= strlen(hptr
)) >= es
->cbufsize
)
1713 es
->linelen
= es
->cbufsize
- 1;
1714 memmove(es
->cbuf
, hptr
, es
->linelen
);
1720 redraw_line(int newline
)
1722 (void) memset(wbuf
[win
], ' ', wbuf_len
);
1733 refresh(int leftside
)
1737 display(wbuf
[1 - win
], wbuf
[win
], leftside
);
1746 if (es
->cursor
< es
->winleft
)
1750 while (cur
< es
->cursor
)
1751 col
= newcol((unsigned char) es
->cbuf
[cur
++], col
);
1752 if (col
>= winwidth
)
1761 int holdcur1
, holdcol1
;
1762 int holdcur2
, holdcol2
;
1764 holdcur1
= holdcur2
= tcur
= 0;
1765 holdcol1
= holdcol2
= tcol
= 0;
1766 while (tcur
< es
->cursor
) {
1767 if (tcol
- holdcol2
> winwidth
/ 2) {
1768 holdcur1
= holdcur2
;
1769 holdcol1
= holdcol2
;
1773 tcol
= newcol((unsigned char) es
->cbuf
[tcur
++], tcol
);
1775 while (tcol
- holdcol1
> winwidth
/ 2)
1776 holdcol1
= newcol((unsigned char) es
->cbuf
[holdcur1
++],
1778 es
->winleft
= holdcur1
;
1781 /* Printing the byte ch at display column col moves to which column? */
1783 newcol(int ch
, int col
)
1786 return (col
| 7) + 1;
1789 return col
+ char_len(ch
);
1792 /* Display wb1 assuming that wb2 is currently displayed. */
1794 display(char *wb1
, char *wb2
, int leftside
)
1796 char *twb1
; /* pointer into the buffer to display */
1797 char *twb2
; /* pointer into the previous display buffer */
1798 static int lastb
= -1; /* last byte# written from wb1, if UTF-8 */
1799 int cur
; /* byte# in the main command line buffer */
1800 int col
; /* display column loop variable */
1801 int ncol
; /* display column of the cursor */
1802 int cnt
; /* remaining display columns to fill */
1804 char mc
; /* new "more character" at the right of window */
1808 * Fill the current display buffer with data from cbuf.
1809 * In this first loop, col does not include the prompt.
1816 while (col
< winwidth
&& cur
< es
->linelen
) {
1817 if (cur
== es
->cursor
&& leftside
)
1818 ncol
= col
+ pwidth
;
1819 if ((ch
= es
->cbuf
[cur
]) == '\t') {
1822 } while (++col
< winwidth
&& (col
& 7) != 0);
1824 if ((ch
& 0x80) && Flag(FVISHOW8
)) {
1826 if (++col
< winwidth
) {
1832 if (col
< winwidth
) {
1833 if (ch
< ' ' || ch
== 0x7f) {
1835 if (++col
< winwidth
) {
1846 if (cur
== es
->cursor
&& !leftside
)
1847 ncol
= col
+ pwidth
- 1;
1850 if (cur
== es
->cursor
)
1851 ncol
= col
+ pwidth
;
1853 /* Pad the current display buffer to the right margin. */
1855 if (col
< winwidth
) {
1856 while (col
< winwidth
) {
1865 * Update the terminal display with data from wb1.
1866 * In this final loop, col includes the prompt.
1871 for (twb1
= wb1
, twb2
= wb2
; cnt
; twb1
++, twb2
++) {
1872 if (*twb1
!= *twb2
) {
1875 * When a byte changes in the middle of a UTF-8
1876 * character, back up to the start byte, unless
1877 * the previous byte was the last one written.
1880 if (col
> 0 && isu8cont(*twb1
)) {
1882 if (lastb
>= 0 && twb1
== wb1
+ lastb
+ 1)
1884 else while (twb1
> wb1
&& isu8cont(*twb1
)) {
1891 ed_mov_opt(col
, wb1
);
1894 * Always write complete characters, and
1895 * advance all pointers accordingly.
1899 while (isu8cont(twb1
[1])) {
1903 lastb
= *twb1
& 0x80 ? twb1
- wb1
: -1;
1905 } else if (isu8cont(*twb1
))
1909 * For changed continuation bytes, we backed up.
1910 * For unchanged ones, we jumped to the next byte.
1911 * So, getting here, we had a real column.
1918 /* Update the "more character". */
1920 if (es
->winleft
> 0 && moreright
)
1921 /* POSIX says to use * for this but that is a globbing
1922 * character and may confuse people; + is more innocuous
1925 else if (es
->winleft
> 0)
1932 ed_mov_opt(pwidth
+ winwidth
+ 1, wb1
);
1939 /* Move the cursor to its new position. */
1941 if (cur_col
!= ncol
) {
1942 ed_mov_opt(ncol
, wb1
);
1947 /* Move the display cursor to display column number col. */
1949 ed_mov_opt(int col
, char *wb
)
1953 /* The cursor is already at the right place. */
1958 /* The cursor is too far right. */
1960 if (cur_col
> col
) {
1961 if (cur_col
> 2 * col
+ 1) {
1962 /* Much too far right, redraw from scratch. */
1967 /* Slightly too far right, back up. */
1970 } while (--cur_col
> col
);
1975 /* Advance the cursor. */
1977 for (ci
= pwidth
; ci
< col
|| isu8cont(*wb
);
1978 ci
= newcol((unsigned char)*wb
++, ci
))
1979 if (ci
> cur_col
|| (ci
== cur_col
&& !isu8cont(*wb
)))
1985 /* replace word with all expansions (ie, expand word*) */
1987 expand_word(int command
)
1989 static struct edstate
*buf
;
1996 /* Undo previous expansion */
1997 if (command
== 0 && expanded
== EXPAND
&& buf
) {
1998 restore_edstate(es
, buf
);
2008 nwords
= x_cf_glob(XCF_COMMAND_FILE
|XCF_FULLPATH
,
2009 es
->cbuf
, es
->linelen
, es
->cursor
,
2010 &start
, &end
, &words
, NULL
);
2016 buf
= save_edstate(es
);
2018 del_range(start
, end
);
2020 for (i
= 0; i
< nwords
; ) {
2021 if (x_escape(words
[i
], strlen(words
[i
]), x_vi_putbuf
) != 0) {
2025 if (++i
< nwords
&& putbuf(" ", 1, 0) != 0) {
2030 i
= buf
->cursor
- end
;
2031 if (rval
== 0 && i
> 0)
2033 modified
= 1; hnum
= hlast
;
2041 complete_word(int command
, int count
)
2043 static struct edstate
*buf
;
2053 /* Undo previous completion */
2054 if (command
== 0 && expanded
== COMPLETE
&& buf
) {
2055 print_expansions(buf
);
2059 if (command
== 0 && expanded
== PRINT
&& buf
) {
2060 restore_edstate(es
, buf
);
2070 /* XCF_FULLPATH for count 'cause the menu printed by print_expansions()
2071 * was done this way.
2073 nwords
= x_cf_glob(XCF_COMMAND_FILE
| (count
? XCF_FULLPATH
: 0),
2074 es
->cbuf
, es
->linelen
, es
->cursor
,
2075 &start
, &end
, &words
, &is_command
);
2084 if (count
>= nwords
) {
2086 x_print_expansions(nwords
, words
, is_command
);
2087 x_free_words(nwords
, words
);
2092 * Expand the count'th word to its basename
2095 match
= words
[count
] +
2096 x_basename(words
[count
], NULL
);
2097 /* If more than one possible match, use full path */
2098 for (i
= 0; i
< nwords
; i
++)
2100 strcmp(words
[i
] + x_basename(words
[i
],
2101 NULL
), match
) == 0) {
2102 match
= words
[count
];
2106 match
= words
[count
];
2107 match_len
= strlen(match
);
2109 /* expanded = PRINT; next call undo */
2112 match_len
= x_longest_prefix(nwords
, words
);
2113 expanded
= COMPLETE
; /* next call will list completions */
2114 is_unique
= nwords
== 1;
2117 buf
= save_edstate(es
);
2118 del_range(start
, end
);
2121 /* escape all shell-sensitive characters and put the result into
2123 rval
= x_escape(match
, match_len
, x_vi_putbuf
);
2125 if (rval
== 0 && is_unique
) {
2126 /* If exact match, don't undo. Allows directory completions
2127 * to be used (ie, complete the next portion of the path).
2131 /* If not a directory, add a space to the end... */
2132 if (match_len
> 0 && match
[match_len
- 1] != '/')
2133 rval
= putbuf(" ", 1, 0);
2135 x_free_words(nwords
, words
);
2137 modified
= 1; hnum
= hlast
;
2139 lastac
= 0; /* prevent this from being redone... */
2146 print_expansions(struct edstate
*e
)
2153 nwords
= x_cf_glob(XCF_COMMAND_FILE
|XCF_FULLPATH
,
2154 e
->cbuf
, e
->linelen
, e
->cursor
,
2155 &start
, &end
, &words
, &is_command
);
2160 x_print_expansions(nwords
, words
, is_command
);
2161 x_free_words(nwords
, words
);
2167 * The number of bytes needed to encode byte c.
2168 * Control bytes get "M-" or "^" prepended.
2169 * This function does not handle tabs.
2176 if ((c
& 0x80) && Flag(FVISHOW8
)) {
2180 if (c
< ' ' || c
== 0x7f)
2185 /* Similar to x_zotc(emacs.c), but no tab weirdness */
2189 if (Flag(FVISHOW8
) && (c
& 0x80)) {
2193 if (c
< ' ' || c
== 0x7f) {
2201 vi_pprompt(int full
)
2203 pprompt(prompt
+ (full
? 0 : prompt_skip
), prompt_trunc
);
2209 /* Beem out of any macros as soon as an error occurs */
2216 vi_macro_reset(void)
2219 afree(macro
.buf
, APERM
);
2220 memset((char *) ¯o
, 0, sizeof(macro
));
2225 isu8cont(unsigned char c
)
2227 return !Flag(FVISHOW8
) && (c
& (0x80 | 0x40)) == 0x80;