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