GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: bin/ksh/vi.c Lines: 0 1089 0.0 %
Date: 2016-12-06 Branches: 0 902 0.0 %

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