GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: bin/ksh/vi.c Lines: 623 1162 53.6 %
Date: 2017-11-13 Branches: 457 946 48.3 %

Line Branch Exec Source
1
/*	$OpenBSD: vi.c,v 1.49 2017/09/02 18:53:53 deraadt 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 <sys/stat.h>		/* completion */
13
14
#include <ctype.h>
15
#include <stdlib.h>
16
#include <string.h>
17
18
#include "sh.h"
19
#include "edit.h"
20
21
#define CTRL(c)		(c & 0x1f)
22
23
struct edstate {
24
	char	*cbuf;		/* main buffer to build the command line */
25
	int	cbufsize;	/* number of bytes allocated for cbuf */
26
	int	linelen;	/* current number of bytes in cbuf */
27
	int	winleft;	/* first byte# in cbuf to be displayed */
28
	int	cursor;		/* byte# in cbuf having the cursor */
29
};
30
31
32
static int	vi_hook(int);
33
static void	vi_reset(char *, size_t);
34
static int	nextstate(int);
35
static int	vi_insert(int);
36
static int	vi_cmd(int, const char *);
37
static int	domove(int, const char *, int);
38
static int	redo_insert(int);
39
static void	yank_range(int, int);
40
static int	bracktype(int);
41
static void	save_cbuf(void);
42
static void	restore_cbuf(void);
43
static void	edit_reset(char *, size_t);
44
static int	putbuf(const char *, int, int);
45
static void	del_range(int, int);
46
static int	findch(int, int, int, int);
47
static int	forwword(int);
48
static int	backword(int);
49
static int	endword(int);
50
static int	Forwword(int);
51
static int	Backword(int);
52
static int	Endword(int);
53
static int	grabhist(int, int);
54
static int	grabsearch(int, int, int, char *);
55
static void	redraw_line(int);
56
static void	refresh(int);
57
static int	outofwin(void);
58
static void	rewindow(void);
59
static int	newcol(int, int);
60
static void	display(char *, char *, int);
61
static void	ed_mov_opt(int, char *);
62
static int	expand_word(int);
63
static int	complete_word(int, int);
64
static int	print_expansions(struct edstate *, int);
65
static int	char_len(int);
66
static void	x_vi_zotc(int);
67
static void	vi_pprompt(int);
68
static void	vi_error(void);
69
static void	vi_macro_reset(void);
70
static int	x_vi_putbuf(const char *, size_t);
71
static int	isu8cont(unsigned char);
72
73
#define C_	0x1		/* a valid command that isn't a M_, E_, U_ */
74
#define M_	0x2		/* movement command (h, l, etc.) */
75
#define E_	0x4		/* extended command (c, d, y) */
76
#define X_	0x8		/* long command (@, f, F, t, T, etc.) */
77
#define U_	0x10		/* an UN-undoable command (that isn't a M_) */
78
#define B_	0x20		/* bad command (^@) */
79
#define Z_	0x40		/* repeat count defaults to 0 (not 1) */
80
#define S_	0x80		/* search (/, ?) */
81
82
#define is_bad(c)	(classify[(c)&0x7f]&B_)
83
#define is_cmd(c)	(classify[(c)&0x7f]&(M_|E_|C_|U_))
84
#define is_move(c)	(classify[(c)&0x7f]&M_)
85
#define is_extend(c)	(classify[(c)&0x7f]&E_)
86
#define is_long(c)	(classify[(c)&0x7f]&X_)
87
#define is_undoable(c)	(!(classify[(c)&0x7f]&U_))
88
#define is_srch(c)	(classify[(c)&0x7f]&S_)
89
#define is_zerocount(c)	(classify[(c)&0x7f]&Z_)
90
91
const unsigned char	classify[128] = {
92
   /*       0       1       2       3       4       5       6       7        */
93
   /*   0   ^@     ^A      ^B      ^C      ^D      ^E      ^F      ^G        */
94
	    B_,     0,      0,      0,      0,      C_|U_,  C_|Z_,  0,
95
   /*  01   ^H     ^I      ^J      ^K      ^L      ^M      ^N      ^O        */
96
	    M_,     C_|Z_,  0,      0,      C_|U_,  0,      C_,     0,
97
   /*  02   ^P     ^Q      ^R      ^S      ^T      ^U      ^V      ^W        */
98
	    C_,     0,      C_|U_,  0,      0,      0,      C_,     0,
99
   /*  03   ^X     ^Y      ^Z      ^[      ^\      ^]      ^^      ^_        */
100
	    C_,     0,      0,      C_|Z_,  0,      0,      0,      0,
101
   /*  04  <space>  !       "       #       $       %       &       '        */
102
	    M_,     0,      0,      C_,     M_,     M_,     0,      0,
103
   /*  05   (       )       *       +       ,       -       .       /        */
104
	    0,      0,      C_,     C_,     M_,     C_,     0,      C_|S_,
105
   /*  06   0       1       2       3       4       5       6       7        */
106
	    M_,     0,      0,      0,      0,      0,      0,      0,
107
   /*  07   8       9       :       ;       <       =       >       ?        */
108
	    0,      0,      0,      M_,     0,      C_,     0,      C_|S_,
109
   /* 010   @       A       B       C       D       E       F       G        */
110
	    C_|X_,  C_,     M_,     C_,     C_,     M_,     M_|X_,  C_|U_|Z_,
111
   /* 011   H       I       J       K       L       M       N       O        */
112
	    0,      C_,     0,      0,      0,      0,      C_|U_,  0,
113
   /* 012   P       Q       R       S       T       U       V       W        */
114
	    C_,     0,      C_,     C_,     M_|X_,  C_,     0,      M_,
115
   /* 013   X       Y       Z       [       \       ]       ^       _        */
116
	    C_,     C_|U_,  0,      0,      C_|Z_,  0,      M_,     C_|Z_,
117
   /* 014   `       a       b       c       d       e       f       g        */
118
	    0,      C_,     M_,     E_,     E_,     M_,     M_|X_,  C_|Z_,
119
   /* 015   h       i       j       k       l       m       n       o        */
120
	    M_,     C_,     C_|U_,  C_|U_,  M_,     0,      C_|U_,  0,
121
   /* 016   p       q       r       s       t       u       v       w        */
122
	    C_,     0,      X_,     C_,     M_|X_,  C_|U_,  C_|U_|Z_,M_,
123
   /* 017   x       y       z       {       |       }       ~      ^?        */
124
	    C_,     E_|U_,  0,      0,      M_|Z_,  0,      C_,     0
125
};
126
127
#define MAXVICMD	3
128
#define SRCHLEN		40
129
130
#define INSERT		1
131
#define REPLACE		2
132
133
#define VNORMAL		0		/* command, insert or replace mode */
134
#define VARG1		1		/* digit prefix (first, eg, 5l) */
135
#define VEXTCMD		2		/* cmd + movement (eg, cl) */
136
#define VARG2		3		/* digit prefix (second, eg, 2c3l) */
137
#define VXCH		4		/* f, F, t, T, @ */
138
#define VFAIL		5		/* bad command */
139
#define VCMD		6		/* single char command (eg, X) */
140
#define VREDO		7		/* . */
141
#define VLIT		8		/* ^V */
142
#define VSEARCH		9		/* /, ? */
143
#define VVERSION	10		/* <ESC> ^V */
144
145
static char		undocbuf[LINE];
146
147
static struct edstate	*save_edstate(struct edstate *old);
148
static void		restore_edstate(struct edstate *old, struct edstate *new);
149
static void		free_edstate(struct edstate *old);
150
151
static struct edstate	ebuf;
152
static struct edstate	undobuf = { undocbuf, LINE, 0, 0, 0 };
153
154
static struct edstate	*es;			/* current editor state */
155
static struct edstate	*undo;
156
157
static char	ibuf[LINE];		/* input buffer */
158
static int	first_insert;		/* set when starting in insert mode */
159
static int	saved_inslen;		/* saved inslen for first insert */
160
static int	inslen;			/* length of input buffer */
161
static int	srchlen;		/* number of bytes in search pattern */
162
static char	ybuf[LINE];		/* yank buffer */
163
static int	yanklen;		/* length of yank buffer */
164
static int	fsavecmd = ' ';		/* last find command */
165
static int	fsavech;		/* character to find */
166
static char	lastcmd[MAXVICMD];	/* last non-move command */
167
static int	lastac;			/* argcnt for lastcmd */
168
static int	lastsearch = ' ';	/* last search command */
169
static char	srchpat[SRCHLEN];	/* last search pattern */
170
static int	insert;			/* mode: INSERT, REPLACE, or 0 */
171
static int	hnum;			/* position in history */
172
static int	ohnum;			/* history line copied (after mod) */
173
static int	hlast;			/* 1 past last position in history */
174
static int	modified;		/* buffer has been "modified" */
175
static int	state;
176
177
/* Information for keeping track of macros that are being expanded.
178
 * The format of buf is the alias contents followed by a null byte followed
179
 * by the name (letter) of the alias.  The end of the buffer is marked by
180
 * a double null.  The name of the alias is stored so recursive macros can
181
 * be detected.
182
 */
183
struct macro_state {
184
    unsigned char	*p;	/* current position in buf */
185
    unsigned char	*buf;	/* pointer to macro(s) being expanded */
186
    int			len;	/* how much data in buffer */
187
};
188
static struct macro_state macro;
189
190
enum expand_mode { NONE, EXPAND, COMPLETE, PRINT };
191
static enum expand_mode expanded = NONE;/* last input was expanded */
192
193
int
194
x_vi(char *buf, size_t len)
195
{
196
	int	c;
197
198
852
	vi_reset(buf, len > LINE ? LINE : len);
199
426
	vi_pprompt(1);
200
426
	x_flush();
201
4212
	while (1) {
202
4212
		if (macro.p) {
203
			c = (unsigned char)*macro.p++;
204
			/* end of current macro? */
205
			if (!c) {
206
				/* more macros left to finish? */
207
				if (*macro.p++)
208
					continue;
209
				/* must be the end of all the macros */
210
				vi_macro_reset();
211
				c = x_getc();
212
			}
213
		} else
214
4212
			c = x_getc();
215
216
3810
		if (c == -1)
217
			break;
218
3810
		if (state != VLIT) {
219

7608
			if (c == edchars.intr || c == edchars.quit) {
220
				/* pretend we got an interrupt */
221
				x_vi_zotc(c);
222
				x_flush();
223
				trapsig(c == edchars.intr ? SIGINT : SIGQUIT);
224
				x_mode(false);
225
				unwind(LSHELL);
226
3804
			} else if (c == edchars.eof && state != VVERSION) {
227
				if (es->linelen == 0) {
228
					x_vi_zotc(edchars.eof);
229
					c = -1;
230
					break;
231
				}
232
				continue;
233
			}
234
		}
235
3810
		if (vi_hook(c))
236
			break;
237
3786
		x_flush();
238
	}
239
240
24
	x_putc('\r'); x_putc('\n'); x_flush();
241
242

48
	if (c == -1 || len <= es->linelen)
243
		return -1;
244
245
24
	if (es->cbuf != buf)
246
		memmove(buf, es->cbuf, es->linelen);
247
248
24
	buf[es->linelen++] = '\n';
249
250
24
	return es->linelen;
251
24
}
252
253
static int
254
vi_hook(int ch)
255
{
256
	static char	curcmd[MAXVICMD], locpat[SRCHLEN];
257
	static int	cmdlen, argc1, argc2;
258
259


8778
	switch (state) {
260
261
	case VNORMAL:
262
3468
		if (insert != 0) {
263
2640
			if (ch == CTRL('v')) {
264
6
				state = VLIT;
265
				ch = '^';
266
6
			}
267

5268
			switch (vi_insert(ch)) {
268
			case -1:
269
				vi_error();
270
				state = VNORMAL;
271
				break;
272
			case 0:
273
2628
				if (state == VLIT) {
274
6
					es->cursor--;
275
6
					refresh(0);
276
6
				} else
277
2622
					refresh(insert != 0);
278
				break;
279
			case 1:
280
12
				return 1;
281
			}
282
		} else {
283
828
			if (ch == '\r' || ch == '\n')
284
12
				return 1;
285
816
			cmdlen = 0;
286
816
			argc1 = 0;
287
816
			if (ch >= '1' && ch <= '9') {
288
210
				argc1 = ch - '0';
289
210
				state = VARG1;
290
210
			} else {
291
606
				curcmd[cmdlen++] = ch;
292
606
				state = nextstate(ch);
293
606
				if (state == VSEARCH) {
294
					save_cbuf();
295
					es->cursor = 0;
296
					es->linelen = 0;
297
					if (ch == '/') {
298
						if (putbuf("/", 1, 0) != 0)
299
							return -1;
300
					} else if (putbuf("?", 1, 0) != 0)
301
						return -1;
302
					refresh(0);
303
				}
304
606
				if (state == VVERSION) {
305
					save_cbuf();
306
					es->cursor = 0;
307
					es->linelen = 0;
308
					putbuf(ksh_version + 4,
309
					    strlen(ksh_version + 4), 0);
310
					refresh(0);
311
				}
312
			}
313
		}
314
		break;
315
316
	case VLIT:
317
6
		if (is_bad(ch)) {
318
			del_range(es->cursor, es->cursor + 1);
319
			vi_error();
320
		} else
321
6
			es->cbuf[es->cursor++] = ch;
322
6
		refresh(1);
323
6
		state = VNORMAL;
324
6
		break;
325
326
	case VVERSION:
327
		restore_cbuf();
328
		state = VNORMAL;
329
		refresh(0);
330
		break;
331
332
	case VARG1:
333
210
		if (isdigit(ch))
334
			argc1 = argc1 * 10 + ch - '0';
335
		else {
336
210
			curcmd[cmdlen++] = ch;
337
210
			state = nextstate(ch);
338
		}
339
		break;
340
341
	case VEXTCMD:
342
36
		argc2 = 0;
343
36
		if (ch >= '1' && ch <= '9') {
344
			argc2 = ch - '0';
345
			state = VARG2;
346
			return 0;
347
		} else {
348
36
			curcmd[cmdlen++] = ch;
349
36
			if (ch == curcmd[0])
350
				state = VCMD;
351
36
			else if (is_move(ch))
352
36
				state = nextstate(ch);
353
			else
354
				state = VFAIL;
355
		}
356
36
		break;
357
358
	case VARG2:
359
		if (isdigit(ch))
360
			argc2 = argc2 * 10 + ch - '0';
361
		else {
362
			if (argc1 == 0)
363
				argc1 = argc2;
364
			else
365
				argc1 *= argc2;
366
			curcmd[cmdlen++] = ch;
367
			if (ch == curcmd[0])
368
				state = VCMD;
369
			else if (is_move(ch))
370
				state = nextstate(ch);
371
			else
372
				state = VFAIL;
373
		}
374
		break;
375
376
	case VXCH:
377
90
		if (ch == CTRL('['))
378
			state = VNORMAL;
379
		else {
380
90
			curcmd[cmdlen++] = ch;
381
			state = VCMD;
382
		}
383
90
		break;
384
385
	case VSEARCH:
386
		if (ch == '\r' || ch == '\n' /*|| ch == CTRL('[')*/ ) {
387
			restore_cbuf();
388
			/* Repeat last search? */
389
			if (srchlen == 0) {
390
				if (!srchpat[0]) {
391
					vi_error();
392
					state = VNORMAL;
393
					refresh(0);
394
					return 0;
395
				}
396
			} else {
397
				locpat[srchlen] = '\0';
398
				(void) strlcpy(srchpat, locpat, sizeof srchpat);
399
			}
400
			state = VCMD;
401
		} else if (ch == edchars.erase || ch == CTRL('h')) {
402
			if (srchlen != 0) {
403
				do {
404
					srchlen--;
405
					es->linelen -= char_len(
406
					    (unsigned char)locpat[srchlen]);
407
				} while (srchlen > 0 &&
408
				    isu8cont(locpat[srchlen]));
409
				es->cursor = es->linelen;
410
				refresh(0);
411
				return 0;
412
			}
413
			restore_cbuf();
414
			state = VNORMAL;
415
			refresh(0);
416
		} else if (ch == edchars.kill) {
417
			srchlen = 0;
418
			es->linelen = 1;
419
			es->cursor = 1;
420
			refresh(0);
421
			return 0;
422
		} else if (ch == edchars.werase) {
423
			struct edstate new_es, *save_es;
424
			int i;
425
			int n = srchlen;
426
427
			new_es.cursor = n;
428
			new_es.cbuf = locpat;
429
430
			save_es = es;
431
			es = &new_es;
432
			n = backword(1);
433
			es = save_es;
434
435
			for (i = srchlen; --i >= n; )
436
				es->linelen -= char_len((unsigned char)locpat[i]);
437
			srchlen = n;
438
			es->cursor = es->linelen;
439
			refresh(0);
440
			return 0;
441
		} else {
442
			if (srchlen == SRCHLEN - 1)
443
				vi_error();
444
			else {
445
				locpat[srchlen++] = ch;
446
				if ((ch & 0x80) && Flag(FVISHOW8)) {
447
					if (es->linelen + 2 > es->cbufsize)
448
						vi_error();
449
					es->cbuf[es->linelen++] = 'M';
450
					es->cbuf[es->linelen++] = '-';
451
					ch &= 0x7f;
452
				}
453
				if (ch < ' ' || ch == 0x7f) {
454
					if (es->linelen + 2 > es->cbufsize)
455
						vi_error();
456
					es->cbuf[es->linelen++] = '^';
457
					es->cbuf[es->linelen++] = ch ^ '@';
458
				} else {
459
					if (es->linelen >= es->cbufsize)
460
						vi_error();
461
					es->cbuf[es->linelen++] = ch;
462
				}
463
				es->cursor = es->linelen;
464
				refresh(0);
465
			}
466
			return 0;
467
		}
468
		break;
469
	}
470
471

3792
	switch (state) {
472
	case VCMD:
473
1608
		state = VNORMAL;
474

1608
		switch (vi_cmd(argc1, curcmd)) {
475
		case -1:
476
6
			vi_error();
477
6
			refresh(0);
478
6
			break;
479
		case 0:
480
798
			if (insert != 0)
481
156
				inslen = 0;
482
798
			refresh(insert != 0);
483
798
			break;
484
		case 1:
485
			refresh(0);
486
			return 1;
487
		case 2:
488
			/* back from a 'v' command - don't redraw the screen */
489
			return 1;
490
		}
491
		break;
492
493
	case VREDO:
494
6
		state = VNORMAL;
495
6
		if (argc1 != 0)
496
			lastac = argc1;
497

12
		switch (vi_cmd(lastac, lastcmd)) {
498
		case -1:
499
			vi_error();
500
			refresh(0);
501
			break;
502
		case 0:
503
6
			if (insert != 0) {
504

18
				if (lastcmd[0] == 's' || lastcmd[0] == 'c' ||
505
6
				    lastcmd[0] == 'C') {
506
					if (redo_insert(1) != 0)
507
						vi_error();
508
				} else {
509
6
					if (redo_insert(lastac) != 0)
510
						vi_error();
511
				}
512
			}
513
6
			refresh(0);
514
6
			break;
515
		case 1:
516
			refresh(0);
517
			return 1;
518
		case 2:
519
			/* back from a 'v' command - can't happen */
520
			break;
521
		}
522
		break;
523
524
	case VFAIL:
525
6
		state = VNORMAL;
526
6
		vi_error();
527
6
		break;
528
	}
529
3786
	return 0;
530
3810
}
531
532
static void
533
vi_reset(char *buf, size_t len)
534
{
535
852
	state = VNORMAL;
536
426
	ohnum = hnum = hlast = histnum(-1) + 1;
537
426
	insert = INSERT;
538
426
	saved_inslen = inslen;
539
426
	first_insert = 1;
540
426
	inslen = 0;
541
426
	modified = 1;
542
426
	vi_macro_reset();
543
426
	edit_reset(buf, len);
544
426
}
545
546
static int
547
nextstate(int ch)
548
{
549
1704
	if (is_extend(ch))
550
36
		return VEXTCMD;
551
816
	else if (is_srch(ch))
552
		return VSEARCH;
553
816
	else if (is_long(ch))
554
90
		return VXCH;
555
726
	else if (ch == '.')
556
6
		return VREDO;
557
720
	else if (ch == CTRL('v'))
558
		return VVERSION;
559
720
	else if (is_cmd(ch))
560
714
		return VCMD;
561
	else
562
6
		return VFAIL;
563
852
}
564
565
static int
566
vi_insert(int ch)
567
{
568
	int	tcursor;
569
570
5280
	if (ch == edchars.erase || ch == CTRL('h')) {
571
12
		if (insert == REPLACE) {
572
			if (es->cursor == undo->cursor) {
573
				vi_error();
574
				return 0;
575
			}
576
		} else {
577
12
			if (es->cursor == 0) {
578
				/* x_putc(BEL); no annoying bell here */
579
				return 0;
580
			}
581
		}
582
12
		tcursor = es->cursor - 1;
583

36
		while(tcursor > 0 && isu8cont(es->cbuf[tcursor]))
584
			tcursor--;
585
12
		if (insert == INSERT)
586
24
			memmove(es->cbuf + tcursor, es->cbuf + es->cursor,
587
12
			    es->linelen - es->cursor);
588

12
		if (insert == REPLACE && es->cursor < undo->linelen)
589
			memcpy(es->cbuf + tcursor, undo->cbuf + tcursor,
590
			    es->cursor - tcursor);
591
		else
592
12
			es->linelen -= es->cursor - tcursor;
593
12
		if (inslen < es->cursor - tcursor)
594
			inslen = 0;
595
		else
596
12
			inslen -= es->cursor - tcursor;
597
12
		es->cursor = tcursor;
598
12
		expanded = NONE;
599
12
		return 0;
600
	}
601
2628
	if (ch == edchars.kill) {
602
6
		if (es->cursor != 0) {
603
6
			inslen = 0;
604
12
			memmove(es->cbuf, &es->cbuf[es->cursor],
605
6
			    es->linelen - es->cursor);
606
6
			es->linelen -= es->cursor;
607
6
			es->cursor = 0;
608
6
		}
609
6
		expanded = NONE;
610
6
		return 0;
611
	}
612
2622
	if (ch == edchars.werase) {
613
6
		if (es->cursor != 0) {
614
6
			tcursor = backword(1);
615
12
			memmove(&es->cbuf[tcursor], &es->cbuf[es->cursor],
616
6
			    es->linelen - es->cursor);
617
6
			es->linelen -= es->cursor - tcursor;
618
6
			if (inslen < es->cursor - tcursor)
619
				inslen = 0;
620
			else
621
6
				inslen -= es->cursor - tcursor;
622
6
			es->cursor = tcursor;
623
6
		}
624
6
		expanded = NONE;
625
6
		return 0;
626
	}
627
	/* If any chars are entered before escape, trash the saved insert
628
	 * buffer (if user inserts & deletes char, ibuf gets trashed and
629
	 * we don't want to use it)
630
	 */
631
2616
	if (first_insert && ch != CTRL('['))
632
1908
		saved_inslen = 0;
633


2616
	switch (ch) {
634
	case '\0':
635
		return -1;
636
637
	case '\r':
638
	case '\n':
639
12
		return 1;
640
641
	case CTRL('['):
642
426
		expanded = NONE;
643
426
		if (first_insert) {
644
366
			first_insert = 0;
645
366
			if (inslen == 0) {
646
				inslen = saved_inslen;
647
				return redo_insert(0);
648
			}
649
366
			lastcmd[0] = 'a';
650
366
			lastac = 1;
651
366
		}
652

1278
		if (lastcmd[0] == 's' || lastcmd[0] == 'c' ||
653
426
		    lastcmd[0] == 'C')
654
			return redo_insert(0);
655
		else
656
426
			return redo_insert(lastac - 1);
657
658
	/* { Begin nonstandard vi commands */
659
	case CTRL('x'):
660
		expand_word(0);
661
		break;
662
663
	case CTRL('f'):
664
		complete_word(0, 0);
665
		break;
666
667
	case CTRL('e'):
668
		print_expansions(es, 0);
669
		break;
670
671
	case CTRL('i'):
672
		if (Flag(FVITABCOMPLETE)) {
673
			complete_word(0, 0);
674
			break;
675
		}
676
		/* FALLTHROUGH */
677
	/* End nonstandard vi commands } */
678
679
	default:
680
2178
		if (es->linelen >= es->cbufsize - 1)
681
			return -1;
682
2178
		ibuf[inslen++] = ch;
683
2178
		if (insert == INSERT) {
684
4320
			memmove(&es->cbuf[es->cursor+1], &es->cbuf[es->cursor],
685
2160
			    es->linelen - es->cursor);
686
2160
			es->linelen++;
687
2160
		}
688
2178
		es->cbuf[es->cursor++] = ch;
689

2196
		if (insert == REPLACE && es->cursor > es->linelen)
690
			es->linelen++;
691
2178
		expanded = NONE;
692
2178
	}
693
2178
	return 0;
694
2640
}
695
696
static int
697
vi_cmd(int argcnt, const char *cmd)
698
{
699
	int		ncursor;
700
	int		cur, c1, c2, c3 = 0;
701
	int		any;
702
	struct edstate	*t;
703
704

2214
	if (argcnt == 0 && !is_zerocount(*cmd))
705
594
		argcnt = 1;
706
707
810
	if (is_move(*cmd)) {
708
366
		if ((cur = domove(argcnt, cmd, 0)) >= 0) {
709
366
			if (cur == es->linelen && cur != 0)
710
12
				while (isu8cont(es->cbuf[--cur]))
711
					continue;
712
366
			es->cursor = cur;
713
		} else
714
			return -1;
715
366
	} else {
716
		/* Don't save state in middle of macro.. */
717
444
		if (is_undoable(*cmd) && !macro.p) {
718
408
			undo->winleft = es->winleft;
719
408
			memmove(undo->cbuf, es->cbuf, es->linelen);
720
408
			undo->linelen = es->linelen;
721
408
			undo->cursor = es->cursor;
722
408
			lastac = argcnt;
723
408
			memmove(lastcmd, cmd, MAXVICMD);
724
408
		}
725












888
		switch (*cmd) {
726
727
		case CTRL('l'):
728
		case CTRL('r'):
729
12
			redraw_line(1);
730
12
			break;
731
732
		case '@':
733
			{
734
				static char alias[] = "_\0";
735
				struct tbl *ap;
736
				int	olen, nlen;
737
				char	*p, *nbuf;
738
739
				/* lookup letter in alias list... */
740
				alias[1] = cmd[1];
741
				ap = ktsearch(&aliases, alias, hash(alias));
742
				if (!cmd[1] || !ap || !(ap->flag & ISSET))
743
					return -1;
744
				/* check if this is a recursive call... */
745
				if ((p = (char *) macro.p))
746
					while ((p = strchr(p, '\0')) && p[1])
747
						if (*++p == cmd[1])
748
							return -1;
749
				/* insert alias into macro buffer */
750
				nlen = strlen(ap->val.s) + 1;
751
				olen = !macro.p ? 2 :
752
				    macro.len - (macro.p - macro.buf);
753
				nbuf = alloc(nlen + 1 + olen, APERM);
754
				memcpy(nbuf, ap->val.s, nlen);
755
				nbuf[nlen++] = cmd[1];
756
				if (macro.p) {
757
					memcpy(nbuf + nlen, macro.p, olen);
758
					afree(macro.buf, APERM);
759
					nlen += olen;
760
				} else {
761
					nbuf[nlen++] = '\0';
762
					nbuf[nlen++] = '\0';
763
				}
764
				macro.p = macro.buf = (unsigned char *) nbuf;
765
				macro.len = nlen;
766
			}
767
			break;
768
769
		case 'a':
770
30
			modified = 1; hnum = hlast;
771
30
			if (es->linelen != 0)
772
60
				while (isu8cont(es->cbuf[++es->cursor]))
773
					continue;
774
30
			insert = INSERT;
775
30
			break;
776
777
		case 'A':
778
12
			modified = 1; hnum = hlast;
779
12
			del_range(0, 0);
780
12
			es->cursor = es->linelen;
781
12
			insert = INSERT;
782
12
			break;
783
784
		case 'S':
785
12
			es->cursor = domove(1, "^", 1);
786
12
			del_range(es->cursor, es->linelen);
787
12
			modified = 1; hnum = hlast;
788
12
			insert = INSERT;
789
12
			break;
790
791
		case 'Y':
792
6
			cmd = "y$";
793
			/* ahhhhhh... */
794
		case 'c':
795
		case 'd':
796
		case 'y':
797
42
			if (*cmd == cmd[1]) {
798
				c1 = *cmd == 'c' ? domove(1, "^", 1) : 0;
799
				c2 = es->linelen;
800
42
			} else if (!is_move(cmd[1]))
801
				return -1;
802
			else {
803
42
				if ((ncursor = domove(argcnt, &cmd[1], 1)) < 0)
804
					return -1;
805

42
				if (*cmd == 'c' &&
806

24
				    (cmd[1]=='w' || cmd[1]=='W') &&
807
				    !isspace((unsigned char)es->cbuf[es->cursor])) {
808
					while (isspace(
809
					    (unsigned char)es->cbuf[--ncursor]))
810
						;
811
					ncursor++;
812
				}
813
42
				if (ncursor > es->cursor) {
814
					c1 = es->cursor;
815
					c2 = ncursor;
816
18
				} else {
817
					c1 = ncursor;
818
					c2 = es->cursor;
819
24
					if (cmd[1] == '%')
820
						c2++;
821
				}
822
			}
823

72
			if (*cmd != 'c' && c1 != c2)
824
30
				yank_range(c1, c2);
825
42
			if (*cmd != 'y') {
826
24
				del_range(c1, c2);
827
24
				es->cursor = c1;
828
24
			}
829
42
			if (*cmd == 'c') {
830
12
				modified = 1; hnum = hlast;
831
12
				insert = INSERT;
832
12
			}
833
			break;
834
835
		case 'p':
836
30
			modified = 1; hnum = hlast;
837
30
			if (es->linelen != 0)
838
30
				es->cursor++;
839

108
			while (putbuf(ybuf, yanklen, 0) == 0 && --argcnt > 0)
840
				;
841
30
			if (es->cursor != 0)
842
30
				es->cursor--;
843
30
			if (argcnt != 0)
844
				return -1;
845
			break;
846
847
		case 'P':
848
12
			modified = 1; hnum = hlast;
849
			any = 0;
850

54
			while (putbuf(ybuf, yanklen, 0) == 0 && --argcnt > 0)
851
				any = 1;
852

18
			if (any && es->cursor != 0)
853
6
				es->cursor--;
854
12
			if (argcnt != 0)
855
				return -1;
856
			break;
857
858
		case 'C':
859
6
			modified = 1; hnum = hlast;
860
6
			del_range(es->cursor, es->linelen);
861
6
			insert = INSERT;
862
6
			break;
863
864
		case 'D':
865
102
			yank_range(es->cursor, es->linelen);
866
102
			del_range(es->cursor, es->linelen);
867
102
			if (es->cursor != 0)
868
102
				es->cursor--;
869
			break;
870
871
		case 'g':
872
			if (!argcnt)
873
				argcnt = hlast;
874
			/* FALLTHROUGH */
875
		case 'G':
876
			if (!argcnt)
877
				argcnt = 1;
878
			else
879
				argcnt = hlast - (source->line - argcnt);
880
			if (grabhist(modified, argcnt - 1) < 0)
881
				return -1;
882
			else {
883
				modified = 0;
884
				hnum = argcnt - 1;
885
			}
886
			break;
887
888
		case 'i':
889
54
			modified = 1; hnum = hlast;
890
54
			insert = INSERT;
891
54
			break;
892
893
		case 'I':
894
12
			modified = 1; hnum = hlast;
895
12
			es->cursor = domove(1, "^", 1);
896
12
			insert = INSERT;
897
12
			break;
898
899
		case 'j':
900
		case '+':
901
		case CTRL('n'):
902
			if (grabhist(modified, hnum + argcnt) < 0)
903
				return -1;
904
			else {
905
				modified = 0;
906
				hnum += argcnt;
907
			}
908
			break;
909
910
		case 'k':
911
		case '-':
912
		case CTRL('p'):
913
			if (grabhist(modified, hnum - argcnt) < 0)
914
				return -1;
915
			else {
916
				modified = 0;
917
				hnum -= argcnt;
918
			}
919
			break;
920
921
		case 'r':
922
42
			if (es->linelen == 0)
923
				return -1;
924
42
			modified = 1; hnum = hlast;
925
42
			if (cmd[1] == 0)
926
				vi_error();
927
			else {
928
				c1 = 0;
929
228
				for (cur = es->cursor;
930
186
				    cur < es->linelen; cur++) {
931
96
					if (!isu8cont(es->cbuf[cur]))
932
72
						c1++;
933
96
					if (c1 > argcnt)
934
						break;
935
				}
936
42
				if (argcnt > c1)
937
6
					return -1;
938
939
36
				del_range(es->cursor, cur);
940
156
				while (argcnt-- > 0)
941
42
					putbuf(&cmd[1], 1, 0);
942
72
				while (es->cursor > 0)
943
36
					if (!isu8cont(es->cbuf[--es->cursor]))
944
						break;
945
36
				es->cbuf[es->linelen] = '\0';
946
			}
947
			break;
948
949
		case 'R':
950
12
			modified = 1; hnum = hlast;
951
12
			insert = REPLACE;
952
12
			break;
953
954
		case 's':
955
12
			if (es->linelen == 0)
956
				return -1;
957
12
			modified = 1; hnum = hlast;
958
72
			for (cur = es->cursor; cur < es->linelen; cur++)
959
30
				if (!isu8cont(es->cbuf[cur]))
960
24
					if (argcnt-- == 0)
961
						break;
962
12
			del_range(es->cursor, cur);
963
12
			insert = INSERT;
964
12
			break;
965
966
		case 'v':
967
			if (es->linelen == 0 && argcnt == 0)
968
				return -1;
969
			if (!argcnt) {
970
				if (modified) {
971
					es->cbuf[es->linelen] = '\0';
972
					source->line++;
973
					histsave(source->line, es->cbuf, 1);
974
				} else
975
					argcnt = source->line + 1
976
						- (hlast - hnum);
977
			}
978
			shf_snprintf(es->cbuf, es->cbufsize,
979
			    argcnt ? "%s %d" : "%s",
980
			    "fc -e ${VISUAL:-${EDITOR:-vi}} --",
981
			    argcnt);
982
			es->linelen = strlen(es->cbuf);
983
			return 2;
984
985
		case 'x':
986
30
			if (es->linelen == 0)
987
				return -1;
988
30
			modified = 1; hnum = hlast;
989
144
			for (cur = es->cursor; cur < es->linelen; cur++)
990
54
				if (!isu8cont(es->cbuf[cur]))
991
48
					if (argcnt-- == 0)
992
						break;
993
30
			yank_range(es->cursor, cur);
994
30
			del_range(es->cursor, cur);
995
30
			break;
996
997
		case 'X':
998
6
			if (es->cursor == 0)
999
				return -1;
1000
6
			modified = 1; hnum = hlast;
1001
36
			for (cur = es->cursor; cur > 0; cur--)
1002
18
				if (!isu8cont(es->cbuf[cur]))
1003
18
					if (argcnt-- == 0)
1004
						break;
1005
6
			yank_range(cur, es->cursor);
1006
6
			del_range(cur, es->cursor);
1007
6
			es->cursor = cur;
1008
6
			break;
1009
1010
		case 'u':
1011
6
			t = es;
1012
6
			es = undo;
1013
6
			undo = t;
1014
6
			break;
1015
1016
		case 'U':
1017
6
			if (!modified)
1018
				return -1;
1019
6
			if (grabhist(modified, ohnum) < 0)
1020
				return -1;
1021
6
			modified = 0;
1022
6
			hnum = ohnum;
1023
6
			break;
1024
1025
		case '?':
1026
			if (hnum == hlast)
1027
				hnum = -1;
1028
			/* ahhh */
1029
		case '/':
1030
			c3 = 1;
1031
			srchlen = 0;
1032
			lastsearch = *cmd;
1033
			/* FALLTHROUGH */
1034
		case 'n':
1035
		case 'N':
1036
			if (lastsearch == ' ')
1037
				return -1;
1038
			if (lastsearch == '?')
1039
				c1 = 1;
1040
			else
1041
				c1 = 0;
1042
			if (*cmd == 'N')
1043
				c1 = !c1;
1044
			if ((c2 = grabsearch(modified, hnum,
1045
			    c1, srchpat)) < 0) {
1046
				if (c3) {
1047
					restore_cbuf();
1048
					refresh(0);
1049
				}
1050
				return -1;
1051
			} else {
1052
				modified = 0;
1053
				hnum = c2;
1054
				ohnum = hnum;
1055
			}
1056
			break;
1057
		case '_': {
1058
			int	inspace;
1059
			char	*p, *sp;
1060
1061
			if (histnum(-1) < 0)
1062
				return -1;
1063
			p = *histpos();
1064
#define issp(c)		(isspace((unsigned char)(c)) || (c) == '\n')
1065
			if (argcnt) {
1066
				while (*p && issp(*p))
1067
					p++;
1068
				while (*p && --argcnt) {
1069
					while (*p && !issp(*p))
1070
						p++;
1071
					while (*p && issp(*p))
1072
						p++;
1073
				}
1074
				if (!*p)
1075
					return -1;
1076
				sp = p;
1077
			} else {
1078
				sp = p;
1079
				inspace = 0;
1080
				while (*p) {
1081
					if (issp(*p))
1082
						inspace = 1;
1083
					else if (inspace) {
1084
						inspace = 0;
1085
						sp = p;
1086
					}
1087
					p++;
1088
				}
1089
				p = sp;
1090
			}
1091
			modified = 1; hnum = hlast;
1092
			if (es->cursor != es->linelen)
1093
				es->cursor++;
1094
			while (*p && !issp(*p)) {
1095
				argcnt++;
1096
				p++;
1097
			}
1098
			if (putbuf(" ", 1, 0) != 0)
1099
				argcnt = -1;
1100
			else if (putbuf(sp, argcnt, 0) != 0)
1101
				argcnt = -1;
1102
			if (argcnt < 0) {
1103
				if (es->cursor != 0)
1104
					es->cursor--;
1105
				return -1;
1106
			}
1107
			insert = INSERT;
1108
			}
1109
			break;
1110
1111
		case '~': {
1112
			char	*p;
1113
			unsigned char c;
1114
			int	i;
1115
1116
6
			if (es->linelen == 0)
1117
				return -1;
1118
24
			for (i = 0; i < argcnt; i++) {
1119
6
				p = &es->cbuf[es->cursor];
1120
6
				c = (unsigned char)*p;
1121
6
				if (islower(c)) {
1122
6
					modified = 1; hnum = hlast;
1123
6
					*p = toupper(c);
1124
6
				} else if (isupper(c)) {
1125
					modified = 1; hnum = hlast;
1126
					*p = tolower(c);
1127
				}
1128
6
				if (es->cursor < es->linelen - 1)
1129
					es->cursor++;
1130
			}
1131
6
			break;
1132
			}
1133
1134
		case '#':
1135
		    {
1136
			int ret = x_do_comment(es->cbuf, es->cbufsize,
1137
			    &es->linelen);
1138
			if (ret >= 0)
1139
				es->cursor = 0;
1140
			return ret;
1141
		    }
1142
1143
		case '=':			/* at&t ksh */
1144
		case CTRL('e'):			/* Nonstandard vi/ksh */
1145
			print_expansions(es, 1);
1146
			break;
1147
1148
1149
		case CTRL('i'):			/* Nonstandard vi/ksh */
1150
			if (!Flag(FVITABCOMPLETE))
1151
				return -1;
1152
			complete_word(1, argcnt);
1153
			break;
1154
1155
		case CTRL('['):			/* some annoying at&t ksh's */
1156
			if (!Flag(FVIESCCOMPLETE))
1157
				return -1;
1158
		case '\\':			/* at&t ksh */
1159
		case CTRL('f'):			/* Nonstandard vi/ksh */
1160
			complete_word(1, argcnt);
1161
			break;
1162
1163
1164
		case '*':			/* at&t ksh */
1165
		case CTRL('x'):			/* Nonstandard vi/ksh */
1166
			expand_word(1);
1167
			break;
1168
		}
1169

714
		if (insert == 0 && es->cursor >= es->linelen)
1170
60
			while (es->cursor > 0)
1171
18
				if (!isu8cont(es->cbuf[--es->cursor]))
1172
					break;
1173
	}
1174
804
	return 0;
1175
810
}
1176
1177
static int
1178
domove(int argcnt, const char *cmd, int sub)
1179
{
1180
	int	bcount, i = 0, t;
1181
	int	ncursor = 0;
1182
1183





912
	switch (*cmd) {
1184
1185
	case 'b':
1186
	case 'B':
1187

66
		if (!sub && es->cursor == 0)
1188
			return -1;
1189
42
		ncursor = (*cmd == 'b' ? backword : Backword)(argcnt);
1190
42
		break;
1191
1192
	case 'e':
1193
	case 'E':
1194

24
		if (!sub && es->cursor + 1 >= es->linelen)
1195
			return -1;
1196
12
		ncursor = (*cmd == 'e' ? endword : Endword)(argcnt);
1197
12
		if (!sub)
1198
24
			while (isu8cont((unsigned char)es->cbuf[--ncursor]))
1199
				continue;
1200
		break;
1201
1202
	case 'f':
1203
	case 'F':
1204
	case 't':
1205
	case 'T':
1206
48
		fsavecmd = *cmd;
1207
48
		fsavech = cmd[1];
1208
		/* drop through */
1209
1210
	case ',':
1211
	case ';':
1212
60
		if (fsavecmd == ' ')
1213
			return -1;
1214
60
		i = fsavecmd == 'f' || fsavecmd == 'F';
1215
60
		t = fsavecmd > 'a';
1216
60
		if (*cmd == ',')
1217
6
			t = !t;
1218
60
		if ((ncursor = findch(fsavech, argcnt, t, i)) < 0)
1219
			return -1;
1220
60
		if (sub && t)
1221
			ncursor++;
1222
		break;
1223
1224
	case 'h':
1225
	case CTRL('h'):
1226

246
		if (!sub && es->cursor == 0)
1227
			return -1;
1228
636
		for (ncursor = es->cursor; ncursor > 0; ncursor--)
1229
306
			if (!isu8cont(es->cbuf[ncursor]))
1230
306
				if (argcnt-- == 0)
1231
					break;
1232
		break;
1233
1234
	case ' ':
1235
	case 'l':
1236

48
		if (!sub && es->cursor + 1 >= es->linelen)
1237
			return -1;
1238
156
		for (ncursor = es->cursor; ncursor < es->linelen; ncursor++)
1239
78
			if (!isu8cont(es->cbuf[ncursor]))
1240
78
				if (argcnt-- == 0)
1241
					break;
1242
		break;
1243
1244
	case 'w':
1245
	case 'W':
1246

24
		if (!sub && es->cursor + 1 >= es->linelen)
1247
			return -1;
1248
12
		ncursor = (*cmd == 'w' ? forwword : Forwword)(argcnt);
1249
12
		break;
1250
1251
	case '0':
1252
		ncursor = 0;
1253
90
		break;
1254
1255
	case '^':
1256
		ncursor = 0;
1257

198
		while (ncursor < es->linelen - 1 &&
1258
66
		    isspace((unsigned char)es->cbuf[ncursor]))
1259
36
			ncursor++;
1260
		break;
1261
1262
	case '|':
1263
		ncursor = argcnt;
1264
6
		if (ncursor > es->linelen)
1265
			ncursor = es->linelen;
1266
6
		if (ncursor)
1267
6
			ncursor--;
1268
12
		while (isu8cont(es->cbuf[ncursor]))
1269
			ncursor--;
1270
		break;
1271
1272
	case '$':
1273
12
		ncursor = es->linelen;
1274
12
		break;
1275
1276
	case '%':
1277
12
		ncursor = es->cursor;
1278

36
		while (ncursor < es->linelen &&
1279
12
		    (i = bracktype(es->cbuf[ncursor])) == 0)
1280
			ncursor++;
1281
12
		if (ncursor == es->linelen)
1282
			return -1;
1283
		bcount = 1;
1284
12
		do {
1285
24
			if (i > 0) {
1286
12
				if (++ncursor >= es->linelen)
1287
					return -1;
1288
			} else {
1289
12
				if (--ncursor < 0)
1290
					return -1;
1291
			}
1292
24
			t = bracktype(es->cbuf[ncursor]);
1293
24
			if (t == i)
1294
				bcount++;
1295
24
			else if (t == -i)
1296
12
				bcount--;
1297
24
		} while (bcount != 0);
1298
12
		if (sub && i > 0)
1299
			ncursor++;
1300
		break;
1301
1302
	default:
1303
		return -1;
1304
	}
1305
432
	return ncursor;
1306
432
}
1307
1308
static int
1309
redo_insert(int count)
1310
{
1311
1380
	while (count-- > 0)
1312
42
		if (putbuf(ibuf, inslen, insert==REPLACE) != 0)
1313
			return -1;
1314
432
	if (es->cursor > 0)
1315
894
		while (isu8cont(es->cbuf[--es->cursor]))
1316
30
			continue;
1317
432
	insert = 0;
1318
432
	return 0;
1319
432
}
1320
1321
static void
1322
yank_range(int a, int b)
1323
{
1324
336
	yanklen = b - a;
1325
168
	if (yanklen != 0)
1326
168
		memmove(ybuf, &es->cbuf[a], yanklen);
1327
168
}
1328
1329
static int
1330
bracktype(int ch)
1331
{
1332

72
	switch (ch) {
1333
1334
	case '(':
1335
12
		return 1;
1336
1337
	case '[':
1338
		return 2;
1339
1340
	case '{':
1341
		return 3;
1342
1343
	case ')':
1344
12
		return -1;
1345
1346
	case ']':
1347
		return -2;
1348
1349
	case '}':
1350
		return -3;
1351
1352
	default:
1353
12
		return 0;
1354
	}
1355
36
}
1356
1357
/*
1358
 *	Non user interface editor routines below here
1359
 */
1360
1361
static int	cur_col;		/* current display column */
1362
static int	pwidth;			/* display columns needed for prompt */
1363
static int	prompt_trunc;		/* how much of prompt to truncate */
1364
static int	prompt_skip;		/* how much of prompt to skip */
1365
static int	winwidth;		/* available column positions */
1366
static char	*wbuf[2];		/* current & previous window buffer */
1367
static int	wbuf_len;		/* length of window buffers (x_cols-3)*/
1368
static int	win;			/* number of window buffer in use */
1369
static char	morec;			/* more character at right of window */
1370
static char	holdbuf[LINE];		/* place to hold last edit buffer */
1371
static int	holdlen;		/* length of holdbuf */
1372
1373
static void
1374
save_cbuf(void)
1375
{
1376
	memmove(holdbuf, es->cbuf, es->linelen);
1377
	holdlen = es->linelen;
1378
	holdbuf[holdlen] = '\0';
1379
}
1380
1381
static void
1382
restore_cbuf(void)
1383
{
1384
12
	es->cursor = 0;
1385
6
	es->linelen = holdlen;
1386
6
	memmove(es->cbuf, holdbuf, holdlen);
1387
6
}
1388
1389
/* return a new edstate */
1390
static struct edstate *
1391
save_edstate(struct edstate *old)
1392
{
1393
	struct edstate *new;
1394
1395
	new = alloc(sizeof(struct edstate), APERM);
1396
	new->cbuf = alloc(old->cbufsize, APERM);
1397
	memcpy(new->cbuf, old->cbuf, old->linelen);
1398
	new->cbufsize = old->cbufsize;
1399
	new->linelen = old->linelen;
1400
	new->cursor = old->cursor;
1401
	new->winleft = old->winleft;
1402
	return new;
1403
}
1404
1405
static void
1406
restore_edstate(struct edstate *new, struct edstate *old)
1407
{
1408
	memcpy(new->cbuf, old->cbuf, old->linelen);
1409
	new->linelen = old->linelen;
1410
	new->cursor = old->cursor;
1411
	new->winleft = old->winleft;
1412
	free_edstate(old);
1413
}
1414
1415
static void
1416
free_edstate(struct edstate *old)
1417
{
1418
	afree(old->cbuf, APERM);
1419
	afree(old, APERM);
1420
}
1421
1422
1423
1424
static void
1425
edit_reset(char *buf, size_t len)
1426
{
1427
852
	const char *p;
1428
1429
426
	es = &ebuf;
1430
426
	es->cbuf = buf;
1431
426
	es->cbufsize = len;
1432
426
	undo = &undobuf;
1433
426
	undo->cbufsize = len;
1434
1435
426
	es->linelen = undo->linelen = 0;
1436
426
	es->cursor = undo->cursor = 0;
1437
426
	es->winleft = undo->winleft = 0;
1438
1439
426
	cur_col = pwidth = promptlen(prompt, &p);
1440
426
	prompt_skip = p - prompt;
1441
426
	if (pwidth > x_cols - 3 - MIN_EDIT_SPACE) {
1442
		cur_col = x_cols - 3 - MIN_EDIT_SPACE;
1443
		prompt_trunc = pwidth - cur_col;
1444
		pwidth -= prompt_trunc;
1445
	} else
1446
426
		prompt_trunc = 0;
1447

450
	if (!wbuf_len || wbuf_len != x_cols - 3) {
1448
402
		wbuf_len = x_cols - 3;
1449
402
		wbuf[0] = aresize(wbuf[0], wbuf_len, APERM);
1450
402
		wbuf[1] = aresize(wbuf[1], wbuf_len, APERM);
1451
402
	}
1452
426
	(void) memset(wbuf[0], ' ', wbuf_len);
1453
426
	(void) memset(wbuf[1], ' ', wbuf_len);
1454
426
	winwidth = x_cols - pwidth - 3;
1455
426
	win = 0;
1456
426
	morec = ' ';
1457
426
	holdlen = 0;
1458
426
}
1459
1460
/*
1461
 * this is used for calling x_escape() in complete_word()
1462
 */
1463
static int
1464
x_vi_putbuf(const char *s, size_t len)
1465
{
1466
	return putbuf(s, len, 0);
1467
}
1468
1469
static int
1470
putbuf(const char *buf, int len, int repl)
1471
{
1472
276
	if (len == 0)
1473
		return 0;
1474
138
	if (repl) {
1475
12
		if (es->cursor + len >= es->cbufsize)
1476
			return -1;
1477
12
		if (es->cursor + len > es->linelen)
1478
			es->linelen = es->cursor + len;
1479
	} else {
1480
126
		if (es->linelen + len >= es->cbufsize)
1481
			return -1;
1482
252
		memmove(&es->cbuf[es->cursor + len], &es->cbuf[es->cursor],
1483
126
		    es->linelen - es->cursor);
1484
126
		es->linelen += len;
1485
	}
1486
138
	memmove(&es->cbuf[es->cursor], buf, len);
1487
138
	es->cursor += len;
1488
138
	return 0;
1489
138
}
1490
1491
static void
1492
del_range(int a, int b)
1493
{
1494
480
	if (es->linelen != b)
1495
84
		memmove(&es->cbuf[a], &es->cbuf[b], es->linelen - b);
1496
240
	es->linelen -= b - a;
1497
240
}
1498
1499
static int
1500
findch(int ch, int cnt, int forw, int incl)
1501
{
1502
	int	ncursor;
1503
1504
120
	if (es->linelen == 0)
1505
		return -1;
1506
60
	ncursor = es->cursor;
1507
312
	while (cnt--) {
1508
96
		do {
1509
120
			if (forw) {
1510
60
				if (++ncursor == es->linelen)
1511
					return -1;
1512
			} else {
1513
60
				if (--ncursor < 0)
1514
					return -1;
1515
			}
1516
120
		} while (es->cbuf[ncursor] != ch);
1517
	}
1518
60
	if (!incl) {
1519
24
		if (forw)
1520
12
			ncursor--;
1521
		else
1522
12
			ncursor++;
1523
	}
1524
60
	return ncursor;
1525
60
}
1526
1527
/* Move right one character, and then to the beginning of the next word. */
1528
static int
1529
forwword(int argcnt)
1530
{
1531
	int ncursor, skip_space, want_letnum;
1532
	unsigned char uc;
1533
1534
12
	ncursor = es->cursor;
1535

54
	while (ncursor < es->linelen && argcnt--) {
1536
		skip_space = 0;
1537
		want_letnum = -1;
1538
12
		ncursor--;
1539
84
		while (++ncursor < es->linelen) {
1540
48
			uc = es->cbuf[ncursor];
1541
48
			if (isspace(uc)) {
1542
				skip_space = 1;
1543
12
				continue;
1544
36
			} else if (skip_space)
1545
				break;
1546
24
			if (uc & 0x80)
1547
				continue;
1548
24
			if (want_letnum == -1)
1549
24
				want_letnum = letnum(uc);
1550

24
			else if (want_letnum != letnum(uc))
1551
				break;
1552
		}
1553
	}
1554
6
	return ncursor;
1555
}
1556
1557
/* Move left one character, and then to the beginning of the word. */
1558
static int
1559
backword(int argcnt)
1560
{
1561
	int ncursor, skip_space, want_letnum;
1562
	unsigned char uc;
1563
1564
84
	ncursor = es->cursor;
1565

288
	while (ncursor > 0 && argcnt--) {
1566
		skip_space = 1;
1567
		want_letnum = -1;
1568
318
		while (ncursor-- > 0) {
1569
162
			uc = es->cbuf[ncursor];
1570
162
			if (isspace(uc)) {
1571
48
				if (skip_space)
1572
6
					continue;
1573
				else
1574
					break;
1575
			}
1576
			skip_space = 0;
1577
114
			if (uc & 0x80)
1578
				continue;
1579
114
			if (want_letnum == -1)
1580
114
				want_letnum = letnum(uc);
1581

126
			else if (want_letnum != letnum(uc))
1582
				break;
1583
		}
1584
		ncursor++;
1585
	}
1586
42
	return ncursor;
1587
}
1588
1589
/* Move right one character, and then to the byte after the word. */
1590
static int
1591
endword(int argcnt)
1592
{
1593
	int ncursor, skip_space, want_letnum;
1594
	unsigned char uc;
1595
1596
12
	ncursor = es->cursor;
1597

36
	while (ncursor < es->linelen && argcnt--) {
1598
		skip_space = 1;
1599
		want_letnum = -1;
1600
48
		while (++ncursor < es->linelen) {
1601
24
			uc = es->cbuf[ncursor];
1602
24
			if (isspace(uc)) {
1603
6
				if (skip_space)
1604
					continue;
1605
				else
1606
					break;
1607
			}
1608
			skip_space = 0;
1609
18
			if (uc & 0x80)
1610
				continue;
1611
18
			if (want_letnum == -1)
1612
12
				want_letnum = letnum(uc);
1613

24
			else if (want_letnum != letnum(uc))
1614
				break;
1615
		}
1616
	}
1617
6
	return ncursor;
1618
}
1619
1620
/* Move right one character, and then to the beginning of the next big word. */
1621
static int
1622
Forwword(int argcnt)
1623
{
1624
	int	ncursor;
1625
1626
12
	ncursor = es->cursor;
1627

36
	while (ncursor < es->linelen && argcnt--) {
1628

66
		while (!isspace((unsigned char)es->cbuf[ncursor]) &&
1629
18
		    ncursor < es->linelen)
1630
18
			ncursor++;
1631

30
		while (isspace((unsigned char)es->cbuf[ncursor]) &&
1632
6
		    ncursor < es->linelen)
1633
6
			ncursor++;
1634
	}
1635
6
	return ncursor;
1636
}
1637
1638
/* Move left one character, and then to the beginning of the big word. */
1639
static int
1640
Backword(int argcnt)
1641
{
1642
	int	ncursor;
1643
1644
12
	ncursor = es->cursor;
1645

36
	while (ncursor > 0 && argcnt--) {
1646

18
		while (--ncursor >= 0 &&
1647
6
		    isspace((unsigned char)es->cbuf[ncursor]))
1648
			;
1649

54
		while (ncursor >= 0 &&
1650
18
		    !isspace((unsigned char)es->cbuf[ncursor]))
1651
12
			ncursor--;
1652
6
		ncursor++;
1653
	}
1654
6
	return ncursor;
1655
}
1656
1657
/* Move right one character, and then to the byte after the big word. */
1658
static int
1659
Endword(int argcnt)
1660
{
1661
	int	ncursor;
1662
1663
12
	ncursor = es->cursor;
1664

36
	while (ncursor < es->linelen && argcnt--) {
1665

18
		while (++ncursor < es->linelen &&
1666
6
		    isspace((unsigned char)es->cbuf[ncursor]))
1667
			;
1668

72
		while (ncursor < es->linelen &&
1669
24
		    !isspace((unsigned char)es->cbuf[ncursor]))
1670
18
			ncursor++;
1671
	}
1672
6
	return ncursor;
1673
}
1674
1675
static int
1676
grabhist(int save, int n)
1677
{
1678
	char	*hptr;
1679
1680

18
	if (n < 0 || n > hlast)
1681
		return -1;
1682
6
	if (n == hlast) {
1683
6
		restore_cbuf();
1684
6
		ohnum = n;
1685
6
		return 0;
1686
	}
1687
	(void) histnum(n);
1688
	if ((hptr = *histpos()) == NULL) {
1689
		internal_errorf(0, "grabhist: bad history array");
1690
		return -1;
1691
	}
1692
	if (save)
1693
		save_cbuf();
1694
	if ((es->linelen = strlen(hptr)) >= es->cbufsize)
1695
		es->linelen = es->cbufsize - 1;
1696
	memmove(es->cbuf, hptr, es->linelen);
1697
	es->cursor = 0;
1698
	ohnum = n;
1699
	return 0;
1700
6
}
1701
1702
static int
1703
grabsearch(int save, int start, int fwd, 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 strcmp be strncmp? */
1719
		if (start != 0 && fwd && strcmp(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(int newline)
1738
{
1739
24
	(void) memset(wbuf[win], ' ', wbuf_len);
1740
12
	if (newline) {
1741
12
		x_putc('\r');
1742
12
		x_putc('\n');
1743
12
	}
1744
12
	vi_pprompt(0);
1745
12
	cur_col = pwidth;
1746
12
	morec = ' ';
1747
12
}
1748
1749
static void
1750
refresh(int leftside)
1751
{
1752
6888
	if (outofwin())
1753
		rewindow();
1754
3444
	display(wbuf[1 - win], wbuf[win], leftside);
1755
3444
	win = 1 - win;
1756
3444
}
1757
1758
static int
1759
outofwin(void)
1760
{
1761
	int	cur, col;
1762
1763
6888
	if (es->cursor < es->winleft)
1764
		return 1;
1765
	col = 0;
1766
	cur = es->winleft;
1767
27264
	while (cur < es->cursor)
1768
10188
		col = newcol((unsigned char) es->cbuf[cur++], col);
1769
3444
	if (col >= winwidth)
1770
		return 1;
1771
3444
	return 0;
1772
3444
}
1773
1774
static void
1775
rewindow(void)
1776
{
1777
	int	tcur, tcol;
1778
	int	holdcur1, holdcol1;
1779
	int	holdcur2, holdcol2;
1780
1781
	holdcur1 = holdcur2 = tcur = 0;
1782
	holdcol1 = holdcol2 = tcol = 0;
1783
	while (tcur < es->cursor) {
1784
		if (tcol - holdcol2 > winwidth / 2) {
1785
			holdcur1 = holdcur2;
1786
			holdcol1 = holdcol2;
1787
			holdcur2 = tcur;
1788
			holdcol2 = tcol;
1789
		}
1790
		tcol = newcol((unsigned char) es->cbuf[tcur++], tcol);
1791
	}
1792
	while (tcol - holdcol1 > winwidth / 2)
1793
		holdcol1 = newcol((unsigned char) es->cbuf[holdcur1++],
1794
		    holdcol1);
1795
	es->winleft = holdcur1;
1796
}
1797
1798
/* Printing the byte ch at display column col moves to which column? */
1799
static int
1800
newcol(int ch, int col)
1801
{
1802
22896
	if (ch == '\t')
1803
		return (col | 7) + 1;
1804
11448
	if (isu8cont(ch))
1805
54
		return col;
1806
11394
	return col + char_len(ch);
1807
11448
}
1808
1809
/* Display wb1 assuming that wb2 is currently displayed. */
1810
static void
1811
display(char *wb1, char *wb2, int leftside)
1812
{
1813
	char	*twb1;	/* pointer into the buffer to display */
1814
	char	*twb2;	/* pointer into the previous display buffer */
1815
	static int lastb = -1; /* last byte# written from wb1, if UTF-8 */
1816
	int	 cur;	/* byte# in the main command line buffer */
1817
	int	 col;	/* display column loop variable */
1818
	int	 ncol;	/* display column of the cursor */
1819
	int	 cnt;	/* remaining display columns to fill */
1820
	int	 moreright;
1821
	char	 mc;	/* new "more character" at the right of window */
1822
	unsigned char ch;
1823
1824
	/*
1825
	 * Fill the current display buffer with data from cbuf.
1826
	 * In this first loop, col does not include the prompt.
1827
	 */
1828
1829
	ncol = col = 0;
1830
6888
	cur = es->winleft;
1831
	moreright = 0;
1832
	twb1 = wb1;
1833

64968
	while (col < winwidth && cur < es->linelen) {
1834
12798
		if (cur == es->cursor && leftside)
1835
294
			ncol = col + pwidth;
1836
12798
		if ((ch = es->cbuf[cur]) == '\t') {
1837
			do {
1838
				*twb1++ = ' ';
1839
			} while (++col < winwidth && (col & 7) != 0);
1840
		} else {
1841

13008
			if ((ch & 0x80) && Flag(FVISHOW8)) {
1842
				*twb1++ = 'M';
1843
				if (++col < winwidth) {
1844
					*twb1++ = '-';
1845
					col++;
1846
				}
1847
				ch &= 0x7f;
1848
			}
1849
12798
			if (col < winwidth) {
1850

25584
				if (ch < ' ' || ch == 0x7f) {
1851
12
					*twb1++ = '^';
1852
12
					if (++col < winwidth) {
1853
12
						*twb1++ = ch ^ '@';
1854
12
						col++;
1855
12
					}
1856
				} else {
1857
12786
					*twb1++ = ch;
1858
12786
					if (!isu8cont(ch))
1859
12690
						col++;
1860
				}
1861
			}
1862
		}
1863
12798
		if (cur == es->cursor && !leftside)
1864
1074
			ncol = col + pwidth - 1;
1865
12798
		cur++;
1866
	}
1867
3444
	if (cur == es->cursor)
1868
2076
		ncol = col + pwidth;
1869
1870
	/* Pad the current display buffer to the right margin. */
1871
1872
3444
	if (col < winwidth) {
1873
491172
		while (col < winwidth) {
1874
242142
			*twb1++ = ' ';
1875
242142
			col++;
1876
		}
1877
	} else
1878
		moreright++;
1879
3444
	*twb1 = ' ';
1880
1881
	/*
1882
	 * Update the terminal display with data from wb1.
1883
	 * In this final loop, col includes the prompt.
1884
	 */
1885
1886
3444
	col = pwidth;
1887
3444
	cnt = winwidth;
1888
516720
	for (twb1 = wb1, twb2 = wb2; cnt; twb1++, twb2++) {
1889
254916
		if (*twb1 != *twb2) {
1890
1891
			/*
1892
			 * When a byte changes in the middle of a UTF-8
1893
			 * character, back up to the start byte, unless
1894
			 * the previous byte was the last one written.
1895
			 */
1896
1897

6564
			if (col > 0 && isu8cont(*twb1)) {
1898
36
				col--;
1899

72
				if (lastb >= 0 && twb1 == wb1 + lastb + 1)
1900
36
					cur_col = col;
1901
				else while (twb1 > wb1 && isu8cont(*twb1)) {
1902
					twb1--;
1903
					twb2--;
1904
				}
1905
			}
1906
1907
3282
			if (cur_col != col)
1908
162
				ed_mov_opt(col, wb1);
1909
1910
			/*
1911
			 * Always write complete characters, and
1912
			 * advance all pointers accordingly.
1913
			 */
1914
1915
3282
			x_putc(*twb1);
1916
6564
			while (isu8cont(twb1[1])) {
1917
				x_putc(*++twb1);
1918
				twb2++;
1919
			}
1920
6630
			lastb = *twb1 & 0x80 ? twb1 - wb1 : -1;
1921
3282
			cur_col++;
1922
254916
		} else if (isu8cont(*twb1))
1923
			continue;
1924
1925
		/*
1926
		 * For changed continuation bytes, we backed up.
1927
		 * For unchanged ones, we jumped to the next byte.
1928
		 * So, getting here, we had a real column.
1929
		 */
1930
1931
254856
		col++;
1932
254856
		cnt--;
1933
254856
	}
1934
1935
	/* Update the "more character". */
1936
1937
3444
	if (es->winleft > 0 && moreright)
1938
		/* POSIX says to use * for this but that is a globbing
1939
		 * character and may confuse people; + is more innocuous
1940
		 */
1941
		mc = '+';
1942
3444
	else if (es->winleft > 0)
1943
		mc = '<';
1944
3444
	else if (moreright)
1945
		mc = '>';
1946
	else
1947
		mc = ' ';
1948
3444
	if (mc != morec) {
1949
		ed_mov_opt(pwidth + winwidth + 1, wb1);
1950
		x_putc(mc);
1951
		cur_col++;
1952
		morec = mc;
1953
		lastb = -1;
1954
	}
1955
1956
	/* Move the cursor to its new position. */
1957
1958
3444
	if (cur_col != ncol) {
1959
1488
		ed_mov_opt(ncol, wb1);
1960
1488
		lastb = -1;
1961
1488
	}
1962
3444
}
1963
1964
/* Move the display cursor to display column number col. */
1965
static void
1966
ed_mov_opt(int col, char *wb)
1967
{
1968
	int ci;
1969
1970
	/* The cursor is already at the right place. */
1971
1972
3300
	if (cur_col == col)
1973
		return;
1974
1975
	/* The cursor is too far right. */
1976
1977
1650
	if (cur_col > col) {
1978
1290
		if (cur_col > 2 * col + 1) {
1979
			/* Much too far right, redraw from scratch. */
1980
42
			x_putc('\r');
1981
42
			vi_pprompt(0);
1982
42
			cur_col = pwidth;
1983
		} else {
1984
			/* Slightly too far right, back up. */
1985
1248
			do {
1986
2214
				x_putc('\b');
1987
2214
			} while (--cur_col > col);
1988
1248
			return;
1989
		}
1990
42
	}
1991
1992
	/* Advance the cursor. */
1993
1994

5388
	for (ci = pwidth; ci < col || isu8cont(*wb);
1995
1260
	     ci = newcol((unsigned char)*wb++, ci))
1996

2742
		if (ci > cur_col || (ci == cur_col && !isu8cont(*wb)))
1997
498
			x_putc(*wb);
1998
402
	cur_col = ci;
1999
2052
}
2000
2001
2002
/* replace word with all expansions (ie, expand word*) */
2003
static int
2004
expand_word(int command)
2005
{
2006
	static struct edstate *buf;
2007
	int rval = 0;
2008
	int nwords;
2009
	int start, end;
2010
	char **words;
2011
	int i;
2012
2013
	/* Undo previous expansion */
2014
	if (command == 0 && expanded == EXPAND && buf) {
2015
		restore_edstate(es, buf);
2016
		buf = NULL;
2017
		expanded = NONE;
2018
		return 0;
2019
	}
2020
	if (buf) {
2021
		free_edstate(buf);
2022
		buf = NULL;
2023
	}
2024
2025
	nwords = x_cf_glob(XCF_COMMAND_FILE|XCF_FULLPATH,
2026
	    es->cbuf, es->linelen, es->cursor,
2027
	    &start, &end, &words, NULL);
2028
	if (nwords == 0) {
2029
		vi_error();
2030
		return -1;
2031
	}
2032
2033
	buf = save_edstate(es);
2034
	expanded = EXPAND;
2035
	del_range(start, end);
2036
	es->cursor = start;
2037
	for (i = 0; i < nwords; ) {
2038
		if (x_escape(words[i], strlen(words[i]), x_vi_putbuf) != 0) {
2039
			rval = -1;
2040
			break;
2041
		}
2042
		if (++i < nwords && putbuf(" ", 1, 0) != 0) {
2043
			rval = -1;
2044
			break;
2045
		}
2046
	}
2047
	i = buf->cursor - end;
2048
	if (rval == 0 && i > 0)
2049
		es->cursor += i;
2050
	modified = 1; hnum = hlast;
2051
	insert = INSERT;
2052
	lastac = 0;
2053
	refresh(0);
2054
	return rval;
2055
}
2056
2057
static int
2058
complete_word(int command, int count)
2059
{
2060
	static struct edstate *buf;
2061
	int rval = 0;
2062
	int nwords;
2063
	int start, end;
2064
	char **words;
2065
	char *match;
2066
	int match_len;
2067
	int is_unique;
2068
	int is_command;
2069
2070
	/* Undo previous completion */
2071
	if (command == 0 && expanded == COMPLETE && buf) {
2072
		print_expansions(buf, 0);
2073
		expanded = PRINT;
2074
		return 0;
2075
	}
2076
	if (command == 0 && expanded == PRINT && buf) {
2077
		restore_edstate(es, buf);
2078
		buf = NULL;
2079
		expanded = NONE;
2080
		return 0;
2081
	}
2082
	if (buf) {
2083
		free_edstate(buf);
2084
		buf = NULL;
2085
	}
2086
2087
	/* XCF_FULLPATH for count 'cause the menu printed by print_expansions()
2088
	 * was done this way.
2089
	 */
2090
	nwords = x_cf_glob(XCF_COMMAND_FILE | (count ? XCF_FULLPATH : 0),
2091
	    es->cbuf, es->linelen, es->cursor,
2092
	    &start, &end, &words, &is_command);
2093
	if (nwords == 0) {
2094
		vi_error();
2095
		return -1;
2096
	}
2097
	if (count) {
2098
		int i;
2099
2100
		count--;
2101
		if (count >= nwords) {
2102
			vi_error();
2103
			x_print_expansions(nwords, words, is_command);
2104
			x_free_words(nwords, words);
2105
			redraw_line(0);
2106
			return -1;
2107
		}
2108
		/*
2109
		 * Expand the count'th word to its basename
2110
		 */
2111
		if (is_command) {
2112
			match = words[count] +
2113
			    x_basename(words[count], NULL);
2114
			/* If more than one possible match, use full path */
2115
			for (i = 0; i < nwords; i++)
2116
				if (i != count &&
2117
				    strcmp(words[i] + x_basename(words[i],
2118
				    NULL), match) == 0) {
2119
					match = words[count];
2120
					break;
2121
				}
2122
		} else
2123
			match = words[count];
2124
		match_len = strlen(match);
2125
		is_unique = 1;
2126
		/* expanded = PRINT;	next call undo */
2127
	} else {
2128
		match = words[0];
2129
		match_len = x_longest_prefix(nwords, words);
2130
		expanded = COMPLETE;	/* next call will list completions */
2131
		is_unique = nwords == 1;
2132
	}
2133
2134
	buf = save_edstate(es);
2135
	del_range(start, end);
2136
	es->cursor = start;
2137
2138
	/* escape all shell-sensitive characters and put the result into
2139
	 * command buffer */
2140
	rval = x_escape(match, match_len, x_vi_putbuf);
2141
2142
	if (rval == 0 && is_unique) {
2143
		/* If exact match, don't undo.  Allows directory completions
2144
		 * to be used (ie, complete the next portion of the path).
2145
		 */
2146
		expanded = NONE;
2147
2148
		/* If not a directory, add a space to the end... */
2149
		if (match_len > 0 && match[match_len - 1] != '/')
2150
			rval = putbuf(" ", 1, 0);
2151
	}
2152
	x_free_words(nwords, words);
2153
2154
	modified = 1; hnum = hlast;
2155
	insert = INSERT;
2156
	lastac = 0;	 /* prevent this from being redone... */
2157
	refresh(0);
2158
2159
	return rval;
2160
}
2161
2162
static int
2163
print_expansions(struct edstate *e, int command)
2164
{
2165
	int nwords;
2166
	int start, end;
2167
	char **words;
2168
	int is_command;
2169
2170
	nwords = x_cf_glob(XCF_COMMAND_FILE|XCF_FULLPATH,
2171
	    e->cbuf, e->linelen, e->cursor,
2172
	    &start, &end, &words, &is_command);
2173
	if (nwords == 0) {
2174
		vi_error();
2175
		return -1;
2176
	}
2177
	x_print_expansions(nwords, words, is_command);
2178
	x_free_words(nwords, words);
2179
	redraw_line(0);
2180
	return 0;
2181
}
2182
2183
/*
2184
 * The number of bytes needed to encode byte c.
2185
 * Control bytes get "M-" or "^" prepended.
2186
 * This function does not handle tabs.
2187
 */
2188
static int
2189
char_len(int c)
2190
{
2191
	int len = 1;
2192
2193

22866
	if ((c & 0x80) && Flag(FVISHOW8)) {
2194
		len += 2;
2195
		c &= 0x7f;
2196
	}
2197
11394
	if (c < ' ' || c == 0x7f)
2198
12
		len++;
2199
11394
	return len;
2200
}
2201
2202
/* Similar to x_zotc(emacs.c), but no tab weirdness */
2203
static void
2204
x_vi_zotc(int c)
2205
{
2206
	if (Flag(FVISHOW8) && (c & 0x80)) {
2207
		x_puts("M-");
2208
		c &= 0x7f;
2209
	}
2210
	if (c < ' ' || c == 0x7f) {
2211
		x_putc('^');
2212
		c ^= '@';
2213
	}
2214
	x_putc(c);
2215
}
2216
2217
static void
2218
vi_pprompt(int full)
2219
{
2220
960
	pprompt(prompt + (full ? 0 : prompt_skip), prompt_trunc);
2221
480
}
2222
2223
static void
2224
vi_error(void)
2225
{
2226
	/* Beem out of any macros as soon as an error occurs */
2227
24
	vi_macro_reset();
2228
12
	x_putc(BEL);
2229
12
	x_flush();
2230
12
}
2231
2232
static void
2233
vi_macro_reset(void)
2234
{
2235
876
	if (macro.p) {
2236
		afree(macro.buf, APERM);
2237
		memset((char *) &macro, 0, sizeof(macro));
2238
	}
2239
438
}
2240
2241
static int
2242
isu8cont(unsigned char c)
2243
{
2244
1137432
	return !Flag(FVISHOW8) && (c & (0x80 | 0x40)) == 0x80;
2245
}
2246
#endif	/* VI */