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