1 /* $NetBSD: vi.c,v 1.12 2011/06/22 03:56:17 mrg Exp $ */
5 * written by John Rochester (initially for nsh)
6 * bludgeoned to fit pdksh by Larry Bouzane, Jeff Sparkes & Eric Gisin
12 __RCSID("$NetBSD: vi.c,v 1.12 2011/06/22 03:56:17 mrg Exp $");
20 #include "ksh_stat.h" /* completion */
24 #define Ctrl(c) (c&0x1f)
25 #define is_wordch(c) (letnum(c))
36 static int vi_hook
ARGS((int));
37 static void vi_reset
ARGS((char *, size_t));
38 static int nextstate
ARGS((int));
39 static int vi_insert
ARGS((int));
40 static int vi_cmd
ARGS((int, const char *));
41 static int domove
ARGS((int, const char *, int));
42 static int redo_insert
ARGS((int));
43 static void yank_range
ARGS((int, int));
44 static int bracktype
ARGS((int));
45 static void save_cbuf
ARGS((void));
46 static void restore_cbuf
ARGS((void));
47 static void edit_reset
ARGS((char *, size_t));
48 static int putbuf
ARGS((const char *, int, int));
49 static void del_range
ARGS((int, int));
50 static int findch
ARGS((int, int, int, int));
51 static int forwword
ARGS((int));
52 static int backword
ARGS((int));
53 static int endword
ARGS((int));
54 static int Forwword
ARGS((int));
55 static int Backword
ARGS((int));
56 static int Endword
ARGS((int));
57 static int grabhist
ARGS((int, int));
58 static int grabsearch
ARGS((int, int, int, char *));
59 static void redraw_line
ARGS((int));
60 static void refresh
ARGS((int));
61 static int outofwin
ARGS((void));
62 static void rewindow
ARGS((void));
63 static int newcol
ARGS((int, int));
64 static void display
ARGS((char *, char *, int));
65 static void ed_mov_opt
ARGS((int, char *));
66 static int expand_word
ARGS((int));
67 static int complete_word
ARGS((int, int));
68 static int print_expansions
ARGS((struct edstate
*, int));
69 static int char_len
ARGS((int));
70 static void x_vi_zotc
ARGS((int));
71 static void vi_pprompt
ARGS((int));
72 static void vi_error
ARGS((void));
73 static void vi_macro_reset
ARGS((void));
74 static int x_vi_putbuf
ARGS((const char *, size_t));
76 #define C_ 0x1 /* a valid command that isn't a M_, E_, U_ */
77 #define M_ 0x2 /* movement command (h, l, etc.) */
78 #define E_ 0x4 /* extended command (c, d, y) */
79 #define X_ 0x8 /* long command (@, f, F, t, T, etc.) */
80 #define U_ 0x10 /* an UN-undoable command (that isn't a M_) */
81 #define B_ 0x20 /* bad command (^@) */
82 #define Z_ 0x40 /* repeat count defaults to 0 (not 1) */
83 #define S_ 0x80 /* search (/, ?) */
85 #define is_bad(c) (classify[(c)&0x7f]&B_)
86 #define is_cmd(c) (classify[(c)&0x7f]&(M_|E_|C_|U_))
87 #define is_move(c) (classify[(c)&0x7f]&M_)
88 #define is_extend(c) (classify[(c)&0x7f]&E_)
89 #define is_long(c) (classify[(c)&0x7f]&X_)
90 #define is_undoable(c) (!(classify[(c)&0x7f]&U_))
91 #define is_srch(c) (classify[(c)&0x7f]&S_)
92 #define is_zerocount(c) (classify[(c)&0x7f]&Z_)
94 const unsigned char classify
[128] = {
96 /* 0 ^@ ^A ^B ^C ^D ^E ^F ^G */
97 B_
, 0, 0, 0, 0, C_
|U_
, C_
|Z_
, 0,
98 /* 01 ^H ^I ^J ^K ^L ^M ^N ^O */
99 M_
, C_
|Z_
, 0, 0, C_
|U_
, 0, C_
, 0,
100 /* 02 ^P ^Q ^R ^S ^T ^U ^V ^W */
101 C_
, 0, C_
|U_
, 0, 0, 0, C_
, 0,
102 /* 03 ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
103 C_
, 0, 0, C_
|Z_
, 0, 0, 0, 0,
104 /* 04 <space> ! " # $ % & ' */
105 M_
, 0, 0, C_
, M_
, M_
, 0, 0,
106 /* 05 ( ) * + , - . / */
107 0, 0, C_
, C_
, M_
, C_
, 0, C_
|S_
,
108 /* 06 0 1 2 3 4 5 6 7 */
109 M_
, 0, 0, 0, 0, 0, 0, 0,
110 /* 07 8 9 : ; < = > ? */
111 0, 0, 0, M_
, 0, C_
, 0, C_
|S_
,
112 /* 010 @ A B C D E F G */
113 C_
|X_
, C_
, M_
, C_
, C_
, M_
, M_
|X_
, C_
|U_
|Z_
,
114 /* 011 H I J K L M N O */
115 0, C_
, 0, 0, 0, 0, C_
|U_
, 0,
116 /* 012 P Q R S T U V W */
117 C_
, 0, C_
, C_
, M_
|X_
, C_
, 0, M_
,
118 /* 013 X Y Z [ \ ] ^ _ */
119 C_
, C_
|U_
, 0, 0, C_
|Z_
, 0, M_
, C_
|Z_
,
120 /* 014 ` a b c d e f g */
121 0, C_
, M_
, E_
, E_
, M_
, M_
|X_
, C_
|Z_
,
122 /* 015 h i j k l m n o */
123 M_
, C_
, C_
|U_
, C_
|U_
, M_
, 0, C_
|U_
, 0,
124 /* 016 p q r s t u v w */
125 C_
, 0, X_
, C_
, M_
|X_
, C_
|U_
, C_
|U_
|Z_
,M_
,
126 /* 017 x y z { | } ~ ^? */
127 C_
, E_
|U_
, 0, 0, M_
|Z_
, 0, C_
, 0
136 #define VNORMAL 0 /* command, insert or replace mode */
137 #define VARG1 1 /* digit prefix (first, eg, 5l) */
138 #define VEXTCMD 2 /* cmd + movement (eg, cl) */
139 #define VARG2 3 /* digit prefix (second, eg, 2c3l) */
140 #define VXCH 4 /* f, F, t, T, @ */
141 #define VFAIL 5 /* bad command */
142 #define VCMD 6 /* single char command (eg, X) */
143 #define VREDO 7 /* . */
144 #define VLIT 8 /* ^V */
145 #define VSEARCH 9 /* /, ? */
146 #define VVERSION 10 /* <ESC> ^V */
148 static char undocbuf
[CMDLEN
];
150 static struct edstate
*save_edstate
ARGS((struct edstate
*old
));
151 static void restore_edstate
ARGS((struct edstate
*old
, struct edstate
*new));
152 static void free_edstate
ARGS((struct edstate
*old
));
154 static struct edstate ebuf
;
155 static struct edstate undobuf
= { 0, undocbuf
, CMDLEN
, 0, 0 };
157 static struct edstate
*es
; /* current editor state */
158 static struct edstate
*undo
;
160 static char ibuf
[CMDLEN
]; /* input buffer */
161 static int first_insert
; /* set when starting in insert mode */
162 static int saved_inslen
; /* saved inslen for first insert */
163 static int inslen
; /* length of input buffer */
164 static int srchlen
; /* length of current search pattern */
165 static char ybuf
[CMDLEN
]; /* yank buffer */
166 static int yanklen
; /* length of yank buffer */
167 static int fsavecmd
= ' '; /* last find command */
168 static int fsavech
; /* character to find */
169 static char lastcmd
[MAXVICMD
]; /* last non-move command */
170 static int lastac
; /* argcnt for lastcmd */
171 static int lastsearch
= ' '; /* last search command */
172 static char srchpat
[SRCHLEN
]; /* last search pattern */
173 static int insert
; /* non-zero in insert mode */
174 static int hnum
; /* position in history */
175 static int ohnum
; /* history line copied (after mod) */
176 static int hlast
; /* 1 past last position in history */
177 static int modified
; /* buffer has been "modified" */
180 /* Information for keeping track of macros that are being expanded.
181 * The format of buf is the alias contents followed by a null byte followed
182 * by the name (letter) of the alias. The end of the buffer is marked by
183 * a double null. The name of the alias is stored so recursive macros can
187 unsigned char *p
; /* current position in buf */
188 unsigned char *buf
; /* pointer to macro(s) being expanded */
189 int len
; /* how much data in buffer */
191 static struct macro_state macro
;
193 enum expand_mode
{ NONE
, EXPAND
, COMPLETE
, PRINT
};
194 static enum expand_mode expanded
= NONE
;/* last input was expanded */
203 vi_reset(buf
, len
> CMDLEN
? CMDLEN
: len
);
209 /* end of current macro? */
211 /* more macros left to finish? */
214 /* must be the end of all the macros */
224 if (c
== edchars
.intr
|| c
== edchars
.quit
) {
225 /* pretend we got an interrupt */
228 trapsig(c
== edchars
.intr
? SIGINT
: SIGQUIT
);
231 } else if (c
== edchars
.eof
&& state
!= VVERSION
) {
232 if (es
->linelen
== 0) {
233 x_vi_zotc(edchars
.eof
);
245 x_putc('\r'); x_putc('\n'); x_flush();
247 if (c
== -1 || len
<= (size_t)es
->linelen
)
251 memmove(buf
, es
->cbuf
, es
->linelen
);
253 buf
[es
->linelen
++] = '\n';
262 static char curcmd
[MAXVICMD
];
263 static char locpat
[SRCHLEN
];
265 static int argc1
, argc2
;
271 if (ch
== Ctrl('v')) {
275 switch (vi_insert(ch
)) {
278 /* Arrow keys generate 0xe0X, where X is H.. */
309 refresh(insert
!= 0);
315 if (ch
== '\r' || ch
== '\n')
319 if (ch
>= '1' && ch
<= '9') {
323 curcmd
[cmdlen
++] = ch
;
324 state
= nextstate(ch
);
325 if (state
== VSEARCH
) {
330 if (putbuf("/", 1, 0) != 0) {
333 } else if (putbuf("?", 1, 0) != 0)
337 if (state
== VVERSION
) {
341 putbuf(ksh_version
+ 4,
342 strlen(ksh_version
+ 4), 0);
351 del_range(es
->cursor
, es
->cursor
+ 1);
354 es
->cbuf
[es
->cursor
++] = ch
;
367 argc1
= argc1
* 10 + ch
- '0';
369 curcmd
[cmdlen
++] = ch
;
370 state
= nextstate(ch
);
376 if (ch
>= '1' && ch
<= '9') {
381 curcmd
[cmdlen
++] = ch
;
384 else if (is_move(ch
))
385 state
= nextstate(ch
);
393 argc2
= argc2
* 10 + ch
- '0';
399 curcmd
[cmdlen
++] = ch
;
402 else if (is_move(ch
))
403 state
= nextstate(ch
);
413 curcmd
[cmdlen
++] = ch
;
419 if (ch
== '\r' || ch
== '\n' /*|| ch == Ctrl('[')*/ ) {
421 /* Repeat last search? */
430 locpat
[srchlen
] = '\0';
431 (void) strlcpy(srchpat
, locpat
, sizeof srchpat
);
434 } else if (ch
== edchars
.erase
|| ch
== Ctrl('h')) {
437 es
->linelen
-= char_len((unsigned char) locpat
[srchlen
]);
438 es
->cursor
= es
->linelen
;
445 } else if (ch
== edchars
.kill
) {
451 } else if (ch
== edchars
.werase
) {
455 while (n
> 0 && isspace((unsigned char)locpat
[n
- 1]))
457 while (n
> 0 && !isspace((unsigned char)locpat
[n
- 1]))
459 for (i
= srchlen
; --i
>= n
; )
460 es
->linelen
-= char_len((unsigned char) locpat
[i
]);
462 es
->cursor
= es
->linelen
;
466 if (srchlen
== SRCHLEN
- 1)
469 locpat
[srchlen
++] = ch
;
470 if ((ch
& 0x80) && Flag(FVISHOW8
)) {
471 if (es
->linelen
+ 2 > es
->cbufsize
)
473 es
->cbuf
[es
->linelen
++] = 'M';
474 es
->cbuf
[es
->linelen
++] = '-';
477 if (ch
< ' ' || ch
== 0x7f) {
478 if (es
->linelen
+ 2 > es
->cbufsize
)
480 es
->cbuf
[es
->linelen
++] = '^';
481 es
->cbuf
[es
->linelen
++] = ch
^ '@';
483 if (es
->linelen
>= es
->cbufsize
)
485 es
->cbuf
[es
->linelen
++] = ch
;
487 es
->cursor
= es
->linelen
;
498 switch (vi_cmd(argc1
, curcmd
)) {
506 refresh(insert
!= 0);
512 /* back from a 'v' command - don't redraw the screen */
521 switch (vi_cmd(lastac
, lastcmd
)) {
528 if (lastcmd
[0] == 's' || lastcmd
[0] == 'c' ||
530 if (redo_insert(1) != 0)
533 if (redo_insert(lastac
) != 0)
543 /* back from a 'v' command - can't happen */
562 ohnum
= hnum
= hlast
= histnum(-1) + 1;
564 saved_inslen
= inslen
;
569 edit_reset(buf
, len
);
578 else if (is_srch(ch
))
580 else if (is_long(ch
))
584 else if (ch
== Ctrl('v'))
598 if (ch
== edchars
.erase
|| ch
== Ctrl('h')) {
599 if (insert
== REPLACE
) {
600 if (es
->cursor
== undo
->cursor
) {
607 if (es
->cursor
>= undo
->linelen
)
610 es
->cbuf
[es
->cursor
] = undo
->cbuf
[es
->cursor
];
612 if (es
->cursor
== 0) {
613 /* x_putc(BEL); no annoying bell here */
620 memmove(&es
->cbuf
[es
->cursor
], &es
->cbuf
[es
->cursor
+1],
621 es
->linelen
- es
->cursor
+ 1);
626 if (ch
== edchars
.kill
) {
627 if (es
->cursor
!= 0) {
629 memmove(es
->cbuf
, &es
->cbuf
[es
->cursor
],
630 es
->linelen
- es
->cursor
);
631 es
->linelen
-= es
->cursor
;
637 if (ch
== edchars
.werase
) {
638 if (es
->cursor
!= 0) {
639 tcursor
= Backword(1);
640 memmove(&es
->cbuf
[tcursor
], &es
->cbuf
[es
->cursor
],
641 es
->linelen
- es
->cursor
);
642 es
->linelen
-= es
->cursor
- tcursor
;
643 if (inslen
< es
->cursor
- tcursor
)
646 inslen
-= es
->cursor
- tcursor
;
647 es
->cursor
= tcursor
;
652 /* If any chars are entered before escape, trash the saved insert
653 * buffer (if user inserts & deletes char, ibuf gets trashed and
654 * we don't want to use it)
656 if (first_insert
&& ch
!= Ctrl('['))
661 case 224: /* function key prefix */
675 inslen
= saved_inslen
;
676 return redo_insert(0);
681 if (lastcmd
[0] == 's' || lastcmd
[0] == 'c' ||
683 return redo_insert(0);
685 return redo_insert(lastac
- 1);
687 /* { Begin nonstandard vi commands */
697 print_expansions(es
, 0);
701 if (Flag(FVITABCOMPLETE
)) {
706 /* End nonstandard vi commands } */
709 if (es
->linelen
>= es
->cbufsize
- 1)
712 if (insert
== INSERT
) {
713 memmove(&es
->cbuf
[es
->cursor
+1], &es
->cbuf
[es
->cursor
],
714 es
->linelen
- es
->cursor
);
717 es
->cbuf
[es
->cursor
++] = ch
;
718 if (insert
== REPLACE
&& es
->cursor
> es
->linelen
)
731 int cur
, c1
, c2
, c3
= 0;
735 if (argcnt
== 0 && !is_zerocount(*cmd
))
739 if ((cur
= domove(argcnt
, cmd
, 0)) >= 0) {
740 if (cur
== es
->linelen
&& cur
!= 0)
746 /* Don't save state in middle of macro.. */
747 if (is_undoable(*cmd
) && !macro
.p
) {
748 undo
->winleft
= es
->winleft
;
749 memmove(undo
->cbuf
, es
->cbuf
, es
->linelen
);
750 undo
->linelen
= es
->linelen
;
751 undo
->cursor
= es
->cursor
;
753 memmove(lastcmd
, cmd
, MAXVICMD
);
764 static char alias
[] = "_\0";
769 /* lookup letter in alias list... */
771 ap
= tsearch(&aliases
, alias
, hash(alias
));
772 if (!cmd
[1] || !ap
|| !(ap
->flag
& ISSET
))
774 /* check if this is a recursive call... */
775 if ((p
= (char *) macro
.p
))
776 while ((p
= strchr(p
, '\0')) && p
[1])
779 /* insert alias into macro buffer */
780 nlen
= strlen(ap
->val
.s
) + 1;
782 : macro
.len
- (macro
.p
- macro
.buf
);
783 nbuf
= alloc(nlen
+ 1 + olen
, APERM
);
784 memcpy(nbuf
, ap
->val
.s
, nlen
);
785 nbuf
[nlen
++] = cmd
[1];
787 memcpy(nbuf
+ nlen
, macro
.p
, olen
);
788 afree(macro
.buf
, APERM
);
794 macro
.p
= macro
.buf
= (unsigned char *) nbuf
;
800 modified
= 1; hnum
= hlast
;
801 if (es
->linelen
!= 0)
807 modified
= 1; hnum
= hlast
;
809 es
->cursor
= es
->linelen
;
814 es
->cursor
= domove(1, "^", 1);
815 del_range(es
->cursor
, es
->linelen
);
816 modified
= 1; hnum
= hlast
;
826 if (*cmd
== cmd
[1]) {
827 c1
= *cmd
== 'c' ? domove(1, "^", 1) : 0;
829 } else if (!is_move(cmd
[1]))
832 if ((ncursor
= domove(argcnt
, &cmd
[1], 1)) < 0)
835 (cmd
[1]=='w' || cmd
[1]=='W') &&
836 !isspace((unsigned char)es
->cbuf
[es
->cursor
])) {
837 while (isspace((unsigned char)es
->cbuf
[--ncursor
]))
841 if (ncursor
> es
->cursor
) {
851 if (*cmd
!= 'c' && c1
!= c2
)
858 modified
= 1; hnum
= hlast
;
864 modified
= 1; hnum
= hlast
;
865 if (es
->linelen
!= 0)
867 while (putbuf(ybuf
, yanklen
, 0) == 0 && --argcnt
> 0)
876 modified
= 1; hnum
= hlast
;
878 while (putbuf(ybuf
, yanklen
, 0) == 0 && --argcnt
> 0)
880 if (any
&& es
->cursor
!= 0)
887 modified
= 1; hnum
= hlast
;
888 del_range(es
->cursor
, es
->linelen
);
893 yank_range(es
->cursor
, es
->linelen
);
894 del_range(es
->cursor
, es
->linelen
);
907 argcnt
= hlast
- (source
->line
- argcnt
);
908 if (grabhist(modified
, argcnt
- 1) < 0)
917 modified
= 1; hnum
= hlast
;
922 modified
= 1; hnum
= hlast
;
923 es
->cursor
= domove(1, "^", 1);
930 if (grabhist(modified
, hnum
+ argcnt
) < 0)
941 if (grabhist(modified
, hnum
- argcnt
) < 0)
950 if (es
->linelen
== 0)
952 modified
= 1; hnum
= hlast
;
956 es
->cbuf
[es
->cursor
] = cmd
[1];
960 modified
= 1; hnum
= hlast
;
965 if (es
->linelen
== 0)
967 modified
= 1; hnum
= hlast
;
968 if (es
->cursor
+ argcnt
> es
->linelen
)
969 argcnt
= es
->linelen
- es
->cursor
;
970 del_range(es
->cursor
, es
->cursor
+ argcnt
);
975 if (es
->linelen
== 0)
979 es
->cbuf
[es
->linelen
] = '\0';
981 histsave(source
->line
, es
->cbuf
, 1);
983 argcnt
= source
->line
+ 1
986 shf_snprintf(es
->cbuf
, es
->cbufsize
,
987 argcnt
? "%s %d" : "%s",
988 "fc -e ${VISUAL:-${EDITOR:-vi}} --",
990 es
->linelen
= strlen(es
->cbuf
);
994 if (es
->linelen
== 0)
996 modified
= 1; hnum
= hlast
;
997 if (es
->cursor
+ argcnt
> es
->linelen
)
998 argcnt
= es
->linelen
- es
->cursor
;
999 yank_range(es
->cursor
, es
->cursor
+ argcnt
);
1000 del_range(es
->cursor
, es
->cursor
+ argcnt
);
1004 if (es
->cursor
> 0) {
1005 modified
= 1; hnum
= hlast
;
1006 if (es
->cursor
< argcnt
)
1007 argcnt
= es
->cursor
;
1008 yank_range(es
->cursor
- argcnt
, es
->cursor
);
1009 del_range(es
->cursor
- argcnt
, es
->cursor
);
1010 es
->cursor
-= argcnt
;
1024 if (grabhist(modified
, ohnum
) < 0)
1041 if (lastsearch
== ' ')
1043 if (lastsearch
== '?')
1049 if ((c2
= grabsearch(modified
, hnum
,
1050 c1
, srchpat
)) < 0) {
1066 if (histnum(-1) < 0)
1069 #define issp(c) (isspace((unsigned char)(c)) || (c) == '\n')
1071 while (*p
&& issp(*p
))
1073 while (*p
&& --argcnt
) {
1074 while (*p
&& !issp(*p
))
1076 while (*p
&& issp(*p
))
1096 modified
= 1; hnum
= hlast
;
1097 if (es
->cursor
!= es
->linelen
)
1099 while (*p
&& !issp(*p
)) {
1103 if (putbuf(space
, 1, 0) != 0)
1105 else if (putbuf(sp
, argcnt
, 0) != 0)
1108 if (es
->cursor
!= 0)
1120 if (es
->linelen
== 0)
1122 for (i
= 0; i
< argcnt
; i
++) {
1123 p
= &es
->cbuf
[es
->cursor
];
1124 if (islower((unsigned char)*p
)) {
1125 modified
= 1; hnum
= hlast
;
1126 *p
= toupper((unsigned char)*p
);
1127 } else if (isupper((unsigned char)*p
)) {
1128 modified
= 1; hnum
= hlast
;
1129 *p
= tolower((unsigned char)*p
);
1131 if (es
->cursor
< es
->linelen
- 1)
1139 int ret
= x_do_comment(es
->cbuf
, es
->cbufsize
,
1146 case '=': /* at&t ksh */
1147 case Ctrl('e'): /* Nonstandard vi/ksh */
1148 print_expansions(es
, 1);
1152 case Ctrl('i'): /* Nonstandard vi/ksh */
1153 if (!Flag(FVITABCOMPLETE
))
1155 complete_word(1, argcnt
);
1158 case Ctrl('['): /* some annoying at&t ksh's */
1159 if (!Flag(FVIESCCOMPLETE
))
1161 case '\\': /* at&t ksh */
1162 case Ctrl('f'): /* Nonstandard vi/ksh */
1163 complete_word(1, argcnt
);
1167 case '*': /* at&t ksh */
1168 case Ctrl('x'): /* Nonstandard vi/ksh */
1172 if (insert
== 0 && es
->cursor
!= 0 && es
->cursor
>= es
->linelen
)
1179 domove(argcnt
, cmd
, sub
)
1184 int bcount
, UNINITIALIZED(i
), t
;
1185 int UNINITIALIZED(ncursor
);
1190 if (!sub
&& es
->cursor
== 0)
1192 ncursor
= backword(argcnt
);
1196 if (!sub
&& es
->cursor
== 0)
1198 ncursor
= Backword(argcnt
);
1202 if (!sub
&& es
->cursor
+ 1 >= es
->linelen
)
1204 ncursor
= endword(argcnt
);
1205 if (sub
&& ncursor
< es
->linelen
)
1210 if (!sub
&& es
->cursor
+ 1 >= es
->linelen
)
1212 ncursor
= Endword(argcnt
);
1213 if (sub
&& ncursor
< es
->linelen
)
1227 if (fsavecmd
== ' ')
1229 i
= fsavecmd
== 'f' || fsavecmd
== 'F';
1233 if ((ncursor
= findch(fsavech
, argcnt
, t
, i
)) < 0)
1241 if (!sub
&& es
->cursor
== 0)
1243 ncursor
= es
->cursor
- argcnt
;
1250 if (!sub
&& es
->cursor
+ 1 >= es
->linelen
)
1252 if (es
->linelen
!= 0) {
1253 ncursor
= es
->cursor
+ argcnt
;
1254 if (ncursor
> es
->linelen
)
1255 ncursor
= es
->linelen
;
1260 if (!sub
&& es
->cursor
+ 1 >= es
->linelen
)
1262 ncursor
= forwword(argcnt
);
1266 if (!sub
&& es
->cursor
+ 1 >= es
->linelen
)
1268 ncursor
= Forwword(argcnt
);
1277 while (ncursor
< es
->linelen
- 1 && isspace((unsigned char)es
->cbuf
[ncursor
]))
1283 if (ncursor
> es
->linelen
)
1284 ncursor
= es
->linelen
;
1290 if (es
->linelen
!= 0)
1291 ncursor
= es
->linelen
;
1297 ncursor
= es
->cursor
;
1298 while (ncursor
< es
->linelen
&&
1299 (i
= bracktype(es
->cbuf
[ncursor
])) == 0)
1301 if (ncursor
== es
->linelen
)
1306 if (++ncursor
>= es
->linelen
)
1312 t
= bracktype(es
->cbuf
[ncursor
]);
1317 } while (bcount
!= 0);
1333 if (putbuf(ibuf
, inslen
, insert
==REPLACE
) != 0)
1347 memmove(ybuf
, &es
->cbuf
[a
], yanklen
);
1380 * Non user interface editor routines below here
1383 static int cur_col
; /* current column on line */
1384 static int pwidth
; /* width of prompt */
1385 static int prompt_trunc
; /* how much of prompt to truncate */
1386 static int prompt_skip
; /* how much of prompt to skip */
1387 static int winwidth
; /* width of window */
1388 static char *wbuf
[2]; /* window buffers */
1389 static int wbuf_len
; /* length of window buffers (x_cols-3)*/
1390 static int win
; /* window buffer in use */
1391 static char morec
; /* more character at right of window */
1392 static int lastref
; /* argument to last refresh() */
1393 static char holdbuf
[CMDLEN
]; /* place to hold last edit buffer */
1394 static int holdlen
; /* length of holdbuf */
1399 memmove(holdbuf
, es
->cbuf
, es
->linelen
);
1400 holdlen
= es
->linelen
;
1401 holdbuf
[holdlen
] = '\0';
1408 es
->linelen
= holdlen
;
1409 memmove(es
->cbuf
, holdbuf
, holdlen
);
1412 /* return a new edstate */
1413 static struct edstate
*
1415 struct edstate
*old
;
1417 struct edstate
*new;
1419 new = (struct edstate
*)alloc(sizeof(struct edstate
), APERM
);
1420 new->cbuf
= alloc(old
->cbufsize
, APERM
);
1421 memcpy(new->cbuf
, old
->cbuf
, old
->linelen
);
1422 new->cbufsize
= old
->cbufsize
;
1423 new->linelen
= old
->linelen
;
1424 new->cursor
= old
->cursor
;
1425 new->winleft
= old
->winleft
;
1430 restore_edstate(new, old
)
1431 struct edstate
*old
, *new;
1433 memcpy(new->cbuf
, old
->cbuf
, old
->linelen
);
1434 new->linelen
= old
->linelen
;
1435 new->cursor
= old
->cursor
;
1436 new->winleft
= old
->winleft
;
1442 struct edstate
*old
;
1444 afree(old
->cbuf
, APERM
);
1445 afree((char *)old
, APERM
);
1451 edit_reset(buf
, len
)
1461 undo
->cbufsize
= len
;
1463 es
->linelen
= undo
->linelen
= 0;
1464 es
->cursor
= undo
->cursor
= 0;
1465 es
->winleft
= undo
->winleft
= 0;
1467 cur_col
= pwidth
= promptlen(prompt
, &p
);
1468 prompt_skip
= p
- prompt
;
1469 if (pwidth
> x_cols
- 3 - MIN_EDIT_SPACE
) {
1470 cur_col
= x_cols
- 3 - MIN_EDIT_SPACE
;
1471 prompt_trunc
= pwidth
- cur_col
;
1472 pwidth
-= prompt_trunc
;
1475 if (!wbuf_len
|| wbuf_len
!= x_cols
- 3) {
1476 wbuf_len
= x_cols
- 3;
1477 wbuf
[0] = aresize(wbuf
[0], wbuf_len
, APERM
);
1478 wbuf
[1] = aresize(wbuf
[1], wbuf_len
, APERM
);
1480 (void) memset(wbuf
[0], ' ', wbuf_len
);
1481 (void) memset(wbuf
[1], ' ', wbuf_len
);
1482 winwidth
= x_cols
- pwidth
- 3;
1490 * this is used for calling x_escape() in complete_word()
1497 return putbuf(s
, len
, 0);
1501 putbuf(buf
, len
, repl
)
1509 if (es
->cursor
+ len
>= es
->cbufsize
)
1511 if (es
->cursor
+ len
> es
->linelen
)
1512 es
->linelen
= es
->cursor
+ len
;
1514 if (es
->linelen
+ len
>= es
->cbufsize
)
1516 memmove(&es
->cbuf
[es
->cursor
+ len
], &es
->cbuf
[es
->cursor
],
1517 es
->linelen
- es
->cursor
);
1520 memmove(&es
->cbuf
[es
->cursor
], buf
, len
);
1529 if (es
->linelen
!= b
)
1530 memmove(&es
->cbuf
[a
], &es
->cbuf
[b
], es
->linelen
- b
);
1531 es
->linelen
-= b
- a
;
1535 findch(ch
, cnt
, forw
, incl
)
1543 if (es
->linelen
== 0)
1545 ncursor
= es
->cursor
;
1549 if (++ncursor
== es
->linelen
)
1555 } while (es
->cbuf
[ncursor
] != ch
);
1572 ncursor
= es
->cursor
;
1573 while (ncursor
< es
->linelen
&& argcnt
--) {
1574 if (is_wordch(es
->cbuf
[ncursor
]))
1575 while (is_wordch(es
->cbuf
[ncursor
]) &&
1576 ncursor
< es
->linelen
)
1578 else if (!isspace((unsigned char)es
->cbuf
[ncursor
]))
1579 while (!is_wordch(es
->cbuf
[ncursor
]) &&
1580 !isspace((unsigned char)es
->cbuf
[ncursor
]) &&
1581 ncursor
< es
->linelen
)
1583 while (isspace((unsigned char)es
->cbuf
[ncursor
]) && ncursor
< es
->linelen
)
1595 ncursor
= es
->cursor
;
1596 while (ncursor
> 0 && argcnt
--) {
1597 while (--ncursor
> 0 && isspace((unsigned char)es
->cbuf
[ncursor
]))
1600 if (is_wordch(es
->cbuf
[ncursor
]))
1601 while (--ncursor
>= 0 &&
1602 is_wordch(es
->cbuf
[ncursor
]))
1605 while (--ncursor
>= 0 &&
1606 !is_wordch(es
->cbuf
[ncursor
]) &&
1607 !isspace((unsigned char)es
->cbuf
[ncursor
]))
1621 ncursor
= es
->cursor
;
1622 while (ncursor
< es
->linelen
&& argcnt
--) {
1623 while (++ncursor
< es
->linelen
- 1 &&
1624 isspace((unsigned char)es
->cbuf
[ncursor
]))
1626 if (ncursor
< es
->linelen
- 1) {
1627 if (is_wordch(es
->cbuf
[ncursor
]))
1628 while (++ncursor
< es
->linelen
&&
1629 is_wordch(es
->cbuf
[ncursor
]))
1632 while (++ncursor
< es
->linelen
&&
1633 !is_wordch(es
->cbuf
[ncursor
]) &&
1634 !isspace((unsigned char)es
->cbuf
[ncursor
]))
1648 ncursor
= es
->cursor
;
1649 while (ncursor
< es
->linelen
&& argcnt
--) {
1650 while (!isspace((unsigned char)es
->cbuf
[ncursor
]) && ncursor
< es
->linelen
)
1652 while (isspace((unsigned char)es
->cbuf
[ncursor
]) && ncursor
< es
->linelen
)
1664 ncursor
= es
->cursor
;
1665 while (ncursor
> 0 && argcnt
--) {
1666 while (--ncursor
>= 0 && isspace((unsigned char)es
->cbuf
[ncursor
]))
1668 while (ncursor
>= 0 && !isspace((unsigned char)es
->cbuf
[ncursor
]))
1681 ncursor
= es
->cursor
;
1682 while (ncursor
< es
->linelen
- 1 && argcnt
--) {
1683 while (++ncursor
< es
->linelen
- 1 &&
1684 isspace((unsigned char)es
->cbuf
[ncursor
]))
1686 if (ncursor
< es
->linelen
- 1) {
1687 while (++ncursor
< es
->linelen
&&
1688 !isspace((unsigned char)es
->cbuf
[ncursor
]))
1703 if (n
< 0 || n
> hlast
)
1711 if ((hptr
= *histpos()) == NULL
) {
1712 internal_errorf(0, "grabhist: bad history array");
1717 if ((es
->linelen
= strlen(hptr
)) >= es
->cbufsize
)
1718 es
->linelen
= es
->cbufsize
- 1;
1719 memmove(es
->cbuf
, hptr
, es
->linelen
);
1726 grabsearch(save
, start
, fwd
, pat
)
1727 int save
, start
, fwd
;
1734 if ((start
== 0 && fwd
== 0) || (start
>= hlast
-1 && fwd
== 1))
1740 anchored
= *pat
== '^' ? (++pat
, 1) : 0;
1741 if ((hist
= findhist(start
, fwd
, pat
, anchored
)) < 0) {
1742 /* if (start != 0 && fwd && match(holdbuf, pat) >= 0) { */
1743 /* XXX should FILECMP be strncmp? */
1744 if (start
!= 0 && fwd
&& FILECMP(holdbuf
, pat
) >= 0) {
1754 if ((es
->linelen
= strlen(hptr
)) >= es
->cbufsize
)
1755 es
->linelen
= es
->cbufsize
- 1;
1756 memmove(es
->cbuf
, hptr
, es
->linelen
);
1762 redraw_line(newlinex
)
1765 (void) memset(wbuf
[win
], ' ', wbuf_len
);
1785 display(wbuf
[1 - win
], wbuf
[win
], leftside
);
1794 if (es
->cursor
< es
->winleft
)
1798 while (cur
< es
->cursor
)
1799 col
= newcol((unsigned char) es
->cbuf
[cur
++], col
);
1800 if (col
>= winwidth
)
1808 register int tcur
, tcol
;
1809 int holdcur1
, holdcol1
;
1810 int holdcur2
, holdcol2
;
1812 holdcur1
= holdcur2
= tcur
= 0;
1813 holdcol1
= holdcol2
= tcol
= 0;
1814 while (tcur
< es
->cursor
) {
1815 if (tcol
- holdcol2
> winwidth
/ 2) {
1816 holdcur1
= holdcur2
;
1817 holdcol1
= holdcol2
;
1821 tcol
= newcol((unsigned char) es
->cbuf
[tcur
++], tcol
);
1823 while (tcol
- holdcol1
> winwidth
/ 2)
1824 holdcol1
= newcol((unsigned char) es
->cbuf
[holdcur1
++],
1826 es
->winleft
= holdcur1
;
1834 return (col
| 7) + 1;
1835 return col
+ char_len(ch
);
1839 display(wb1
, wb2
, leftside
)
1844 char *twb1
, *twb2
, mc
;
1846 int UNINITIALIZED(ncol
);
1853 while (col
< winwidth
&& cur
< es
->linelen
) {
1854 if (cur
== es
->cursor
&& leftside
)
1855 ncol
= col
+ pwidth
;
1856 if ((ch
= es
->cbuf
[cur
]) == '\t') {
1859 } while (++col
< winwidth
&& (col
& 7) != 0);
1861 if ((ch
& 0x80) && Flag(FVISHOW8
)) {
1863 if (++col
< winwidth
) {
1869 if (col
< winwidth
) {
1870 if (ch
< ' ' || ch
== 0x7f) {
1872 if (++col
< winwidth
) {
1882 if (cur
== es
->cursor
&& !leftside
)
1883 ncol
= col
+ pwidth
- 1;
1886 if (cur
== es
->cursor
)
1887 ncol
= col
+ pwidth
;
1888 if (col
< winwidth
) {
1889 while (col
< winwidth
) {
1902 if (*twb1
!= *twb2
) {
1904 ed_mov_opt(col
, wb1
);
1912 if (es
->winleft
> 0 && moreright
)
1913 /* POSIX says to use * for this but that is a globbing
1914 * character and may confuse people; + is more innocuous
1917 else if (es
->winleft
> 0)
1924 ed_mov_opt(pwidth
+ winwidth
+ 1, wb1
);
1929 if (cur_col
!= ncol
)
1930 ed_mov_opt(ncol
, wb1
);
1938 if (col
< cur_col
) {
1939 if (col
+ 1 < cur_col
- col
) {
1943 while (cur_col
++ < col
)
1946 while (cur_col
-- > col
)
1950 wb
= &wb
[cur_col
- pwidth
];
1951 while (cur_col
++ < col
)
1958 /* replace word with all expansions (ie, expand word*) */
1960 expand_word(commandx
)
1963 static struct edstate
*buf
;
1970 /* Undo previous expansion */
1971 if (commandx
== 0 && expanded
== EXPAND
&& buf
) {
1972 restore_edstate(es
, buf
);
1982 nwords
= x_cf_glob(XCF_COMMAND_FILE
|XCF_FULLPATH
,
1983 es
->cbuf
, es
->linelen
, es
->cursor
,
1984 &start
, &end
, &words
, (int *) 0);
1990 buf
= save_edstate(es
);
1992 del_range(start
, end
);
1994 for (i
= 0; i
< nwords
; ) {
1995 if (x_escape(words
[i
], strlen(words
[i
]), x_vi_putbuf
) != 0) {
1999 if (++i
< nwords
&& putbuf(space
, 1, 0) != 0) {
2004 i
= buf
->cursor
- end
;
2005 if (rval
== 0 && i
> 0)
2007 modified
= 1; hnum
= hlast
;
2015 complete_word(commandx
, count
)
2019 static struct edstate
*buf
;
2029 /* Undo previous completion */
2030 if (commandx
== 0 && expanded
== COMPLETE
&& buf
) {
2031 print_expansions(buf
, 0);
2035 if (commandx
== 0 && expanded
== PRINT
&& buf
) {
2036 restore_edstate(es
, buf
);
2046 /* XCF_FULLPATH for count 'cause the menu printed by print_expansions()
2047 * was done this way.
2049 nwords
= x_cf_glob(XCF_COMMAND_FILE
| (count
? XCF_FULLPATH
: 0),
2050 es
->cbuf
, es
->linelen
, es
->cursor
,
2051 &start
, &end
, &words
, &is_command
);
2060 if (count
>= nwords
) {
2062 x_print_expansions(nwords
, words
, is_command
);
2063 x_free_words(nwords
, words
);
2068 * Expand the count'th word to its basename
2071 match
= words
[count
]
2072 + x_basename(words
[count
], (char *) 0);
2073 /* If more than one possible match, use full path */
2074 for (i
= 0; i
< nwords
; i
++)
2077 + x_basename(words
[i
], (char *) 0),
2080 match
= words
[count
];
2084 match
= words
[count
];
2085 match_len
= strlen(match
);
2087 /* expanded = PRINT; next call undo */
2090 match_len
= x_longest_prefix(nwords
, words
);
2091 expanded
= COMPLETE
; /* next call will list completions */
2092 is_unique
= nwords
== 1;
2095 buf
= save_edstate(es
);
2096 del_range(start
, end
);
2099 /* escape all shell-sensitive characters and put the result into
2101 rval
= x_escape(match
, match_len
, x_vi_putbuf
);
2103 if (rval
== 0 && is_unique
) {
2104 /* If exact match, don't undo. Allows directory completions
2105 * to be used (ie, complete the next portion of the path).
2109 /* If not a directory, add a space to the end... */
2110 if (match_len
> 0 && !ISDIRSEP(match
[match_len
- 1]))
2111 rval
= putbuf(space
, 1, 0);
2113 x_free_words(nwords
, words
);
2115 modified
= 1; hnum
= hlast
;
2117 lastac
= 0; /* prevent this from being redone... */
2124 print_expansions(ex
, commandx
)
2133 nwords
= x_cf_glob(XCF_COMMAND_FILE
|XCF_FULLPATH
,
2134 ex
->cbuf
, ex
->linelen
, ex
->cursor
,
2135 &start
, &end
, &words
, &is_command
);
2140 x_print_expansions(nwords
, words
, is_command
);
2141 x_free_words(nwords
, words
);
2146 /* How long is char when displayed (not counting tabs) */
2153 if ((c
& 0x80) && Flag(FVISHOW8
)) {
2157 if (c
< ' ' || c
== 0x7f)
2162 /* Similar to x_zotc(emacs.c), but no tab weirdness */
2167 if (Flag(FVISHOW8
) && (c
& 0x80)) {
2171 if (c
< ' ' || c
== 0x7f) {
2182 pprompt(prompt
+ (full
? 0 : prompt_skip
), prompt_trunc
);
2188 /* Beem out of any macros as soon as an error occurs */
2198 afree(macro
.buf
, APERM
);
2199 memset((char *) ¯o
, 0, sizeof(macro
));