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