GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: bin/ksh/emacs.c Lines: 452 975 46.4 %
Date: 2017-11-13 Branches: 197 643 30.6 %

Line Branch Exec Source
1
/*	$OpenBSD: emacs.c,v 1.73 2017/08/30 17:02:53 jca Exp $	*/
2
3
/*
4
 *  Emacs-like command line editing and history
5
 *
6
 *  created by Ron Natalie at BRL
7
 *  modified by Doug Kingston, Doug Gwyn, and Lou Salkind
8
 *  adapted to PD ksh by Eric Gisin
9
 *
10
 * partial rewrite by Marco Peereboom <marco@openbsd.org>
11
 * under the same license
12
 */
13
14
#include "config.h"
15
#ifdef EMACS
16
17
#include <sys/queue.h>
18
#include <sys/stat.h>
19
20
#include <ctype.h>
21
#include <stdio.h>
22
#include <stdlib.h>
23
#include <string.h>
24
25
#include "sh.h"
26
#include "edit.h"
27
28
static	Area	aedit;
29
#define	AEDIT	&aedit		/* area for kill ring and macro defns */
30
31
#define	CTRL(x)		((x) == '?' ? 0x7F : (x) & 0x1F)	/* ASCII */
32
#define	UNCTRL(x)	((x) == 0x7F ? '?' : (x) | 0x40)	/* ASCII */
33
34
/* values returned by keyboard functions */
35
#define	KSTD	0
36
#define	KEOL	1		/* ^M, ^J */
37
#define	KINTR	2		/* ^G, ^C */
38
39
struct	x_ftab {
40
	int		(*xf_func)(int c);
41
	const char	*xf_name;
42
	short		xf_flags;
43
};
44
45
#define XF_ARG		1	/* command takes number prefix */
46
#define	XF_NOBIND	2	/* not allowed to bind to function */
47
#define	XF_PREFIX	4	/* function sets prefix */
48
49
/* Separator for completion */
50
#define	is_cfs(c)	(c == ' ' || c == '\t' || c == '"' || c == '\'')
51
52
/* Separator for motion */
53
#define	is_mfs(c)	(!(isalnum((unsigned char)c) || \
54
			c == '_' || c == '$' || c & 0x80))
55
56
/* Arguments for do_complete()
57
 * 0 = enumerate  M-= complete as much as possible and then list
58
 * 1 = complete   M-Esc
59
 * 2 = list       M-?
60
 */
61
typedef enum {
62
	CT_LIST,	/* list the possible completions */
63
	CT_COMPLETE,	/* complete to longest prefix */
64
	CT_COMPLIST	/* complete and then list (if non-exact) */
65
} Comp_type;
66
67
/* keybindings */
68
struct kb_entry {
69
	TAILQ_ENTRY(kb_entry)	entry;
70
	unsigned char		*seq;
71
	int			len;
72
	struct x_ftab		*ftab;
73
	void			*args;
74
};
75
TAILQ_HEAD(kb_list, kb_entry);
76
struct kb_list			kblist = TAILQ_HEAD_INITIALIZER(kblist);
77
78
/* { from 4.9 edit.h */
79
/*
80
 * The following are used for my horizontal scrolling stuff
81
 */
82
static char    *xbuf;		/* beg input buffer */
83
static char    *xend;		/* end input buffer */
84
static char    *xcp;		/* current position */
85
static char    *xep;		/* current end */
86
static char    *xbp;		/* start of visible portion of input buffer */
87
static char    *xlp;		/* last byte visible on screen */
88
static int	x_adj_ok;
89
/*
90
 * we use x_adj_done so that functions can tell
91
 * whether x_adjust() has been called while they are active.
92
 */
93
static int	x_adj_done;
94
95
static int	xx_cols;
96
static int	x_col;
97
static int	x_displen;
98
static int	x_arg;		/* general purpose arg */
99
static int	x_arg_defaulted;/* x_arg not explicitly set; defaulted to 1 */
100
101
static int	xlp_valid;
102
/* end from 4.9 edit.h } */
103
static	int	x_tty;		/* are we on a tty? */
104
static	int	x_bind_quiet;	/* be quiet when binding keys */
105
static int	(*x_last_command)(int);
106
107
static	char   **x_histp;	/* history position */
108
static	int	x_nextcmd;	/* for newline-and-next */
109
static	char	*xmp;		/* mark pointer */
110
#define	KILLSIZE	20
111
static	char	*killstack[KILLSIZE];
112
static	int	killsp, killtp;
113
static	int	x_literal_set;
114
static	int	x_arg_set;
115
static	char	*macro_args;
116
static	int	prompt_skip;
117
static	int	prompt_redraw;
118
119
static int	x_ins(char *);
120
static void	x_delete(int, int);
121
static int	x_bword(void);
122
static int	x_fword(void);
123
static void	x_goto(char *);
124
static void	x_bs(int);
125
static int	x_size_str(char *);
126
static int	x_size(int);
127
static void	x_zots(char *);
128
static void	x_zotc(int);
129
static void	x_load_hist(char **);
130
static int	x_search(char *, int, int);
131
static int	x_match(char *, char *);
132
static void	x_redraw(int);
133
static void	x_push(int);
134
static void	x_adjust(void);
135
static void	x_e_ungetc(int);
136
static int	x_e_getc(void);
137
static int	x_e_getu8(char *, int);
138
static void	x_e_putc(int);
139
static void	x_e_puts(const char *);
140
static int	x_comment(int);
141
static int	x_fold_case(int);
142
static char	*x_lastcp(void);
143
static void	do_complete(int, Comp_type);
144
static int	isu8cont(unsigned char);
145
146
/* proto's for keybindings */
147
static int	x_abort(int);
148
static int	x_beg_hist(int);
149
static int	x_comp_comm(int);
150
static int	x_comp_file(int);
151
static int	x_complete(int);
152
static int	x_del_back(int);
153
static int	x_del_bword(int);
154
static int	x_del_char(int);
155
static int	x_del_fword(int);
156
static int	x_del_line(int);
157
static int	x_draw_line(int);
158
static int	x_end_hist(int);
159
static int	x_end_of_text(int);
160
static int	x_enumerate(int);
161
static int	x_eot_del(int);
162
static int	x_error(int);
163
static int	x_goto_hist(int);
164
static int	x_ins_string(int);
165
static int	x_insert(int);
166
static int	x_kill(int);
167
static int	x_kill_region(int);
168
static int	x_list_comm(int);
169
static int	x_list_file(int);
170
static int	x_literal(int);
171
static int	x_meta_yank(int);
172
static int	x_mv_back(int);
173
static int	x_mv_begin(int);
174
static int	x_mv_bword(int);
175
static int	x_mv_end(int);
176
static int	x_mv_forw(int);
177
static int	x_mv_fword(int);
178
static int	x_newline(int);
179
static int	x_next_com(int);
180
static int	x_nl_next_com(int);
181
static int	x_noop(int);
182
static int	x_prev_com(int);
183
static int	x_prev_histword(int);
184
static int	x_search_char_forw(int);
185
static int	x_search_char_back(int);
186
static int	x_search_hist(int);
187
static int	x_set_mark(int);
188
static int	x_transpose(int);
189
static int	x_version(int);
190
static int	x_xchg_point_mark(int);
191
static int	x_yank(int);
192
static int	x_comp_list(int);
193
static int	x_expand(int);
194
static int	x_fold_capitalize(int);
195
static int	x_fold_lower(int);
196
static int	x_fold_upper(int);
197
static int	x_set_arg(int);
198
static int	x_comment(int);
199
#ifdef DEBUG
200
static int	x_debug_info(int);
201
#endif
202
203
static const struct x_ftab x_ftab[] = {
204
	{ x_abort,		"abort",			0 },
205
	{ x_beg_hist,		"beginning-of-history",		0 },
206
	{ x_comp_comm,		"complete-command",		0 },
207
	{ x_comp_file,		"complete-file",		0 },
208
	{ x_complete,		"complete",			0 },
209
	{ x_del_back,		"delete-char-backward",		XF_ARG },
210
	{ x_del_bword,		"delete-word-backward",		XF_ARG },
211
	{ x_del_char,		"delete-char-forward",		XF_ARG },
212
	{ x_del_fword,		"delete-word-forward",		XF_ARG },
213
	{ x_del_line,		"kill-line",			0 },
214
	{ x_draw_line,		"redraw",			0 },
215
	{ x_end_hist,		"end-of-history",		0 },
216
	{ x_end_of_text,	"eot",				0 },
217
	{ x_enumerate,		"list",				0 },
218
	{ x_eot_del,		"eot-or-delete",		XF_ARG },
219
	{ x_error,		"error",			0 },
220
	{ x_goto_hist,		"goto-history",			XF_ARG },
221
	{ x_ins_string,		"macro-string",			XF_NOBIND },
222
	{ x_insert,		"auto-insert",			XF_ARG },
223
	{ x_kill,		"kill-to-eol",			XF_ARG },
224
	{ x_kill_region,	"kill-region",			0 },
225
	{ x_list_comm,		"list-command",			0 },
226
	{ x_list_file,		"list-file",			0 },
227
	{ x_literal,		"quote",			0 },
228
	{ x_meta_yank,		"yank-pop",			0 },
229
	{ x_mv_back,		"backward-char",		XF_ARG },
230
	{ x_mv_begin,		"beginning-of-line",		0 },
231
	{ x_mv_bword,		"backward-word",		XF_ARG },
232
	{ x_mv_end,		"end-of-line",			0 },
233
	{ x_mv_forw,		"forward-char",			XF_ARG },
234
	{ x_mv_fword,		"forward-word",			XF_ARG },
235
	{ x_newline,		"newline",			0 },
236
	{ x_next_com,		"down-history",			XF_ARG },
237
	{ x_nl_next_com,	"newline-and-next",		0 },
238
	{ x_noop,		"no-op",			0 },
239
	{ x_prev_com,		"up-history",			XF_ARG },
240
	{ x_prev_histword,	"prev-hist-word",		XF_ARG },
241
	{ x_search_char_forw,	"search-character-forward",	XF_ARG },
242
	{ x_search_char_back,	"search-character-backward",	XF_ARG },
243
	{ x_search_hist,	"search-history",		0 },
244
	{ x_set_mark,		"set-mark-command",		0 },
245
	{ x_transpose,		"transpose-chars",		0 },
246
	{ x_version,		"version",			0 },
247
	{ x_xchg_point_mark,	"exchange-point-and-mark",	0 },
248
	{ x_yank,		"yank",				0 },
249
	{ x_comp_list,		"complete-list",		0 },
250
	{ x_expand,		"expand-file",			0 },
251
	{ x_fold_capitalize,	"capitalize-word",		XF_ARG },
252
	{ x_fold_lower,		"downcase-word",		XF_ARG },
253
	{ x_fold_upper,		"upcase-word",			XF_ARG },
254
	{ x_set_arg,		"set-arg",			XF_NOBIND },
255
	{ x_comment,		"comment",			0 },
256
	{ 0, 0, 0 },
257
#ifdef DEBUG
258
	{ x_debug_info,		"debug-info",			0 },
259
#else
260
	{ 0, 0, 0 },
261
#endif
262
	{ 0, 0, 0 },
263
};
264
265
int
266
isu8cont(unsigned char c)
267
{
268
11378
	return (c & (0x80 | 0x40)) == 0x80;
269
}
270
271
int
272
x_emacs(char *buf, size_t len)
273
{
274
	struct kb_entry		*k, *kmatch = NULL;
275
532
	char			line[LINE + 1];
276
	int			at = 0, ntries = 0, submatch, ret;
277
266
	const char		*p;
278
279
266
	xbp = xbuf = buf; xend = buf + len;
280
266
	xlp = xcp = xep = buf;
281
266
	*xcp = 0;
282
266
	xlp_valid = true;
283
266
	xmp = NULL;
284
266
	x_histp = histptr + 1;
285
286
266
	xx_cols = x_cols;
287
266
	x_col = promptlen(prompt, &p);
288
266
	prompt_skip = p - prompt;
289
266
	x_adj_ok = 1;
290
266
	prompt_redraw = 1;
291
266
	if (x_col > xx_cols)
292
		x_col = x_col - (x_col / xx_cols) * xx_cols;
293
266
	x_displen = xx_cols - 2 - x_col;
294
266
	x_adj_done = 0;
295
296
266
	pprompt(prompt, 0);
297
266
	if (x_displen < 1) {
298
		x_col = 0;
299
		x_displen = xx_cols - 2;
300
		x_e_putc('\n');
301
		prompt_redraw = 0;
302
	}
303
304
266
	if (x_nextcmd >= 0) {
305
		int off = source->line - x_nextcmd;
306
		if (histptr - history >= off)
307
			x_load_hist(histptr - off);
308
		x_nextcmd = -1;
309
	}
310
311
266
	x_literal_set = 0;
312
266
	x_arg = -1;
313
266
	x_last_command = NULL;
314
1306
	while (1) {
315
1074
		x_flush();
316
1074
		if ((at = x_e_getu8(line, at)) < 0)
317
			return 0;
318
1074
		ntries++;
319
320
1074
		if (x_arg == -1) {
321
1054
			x_arg = 1;
322
1054
			x_arg_defaulted = 1;
323
1054
		}
324
325
1074
		if (x_literal_set) {
326
			/* literal, so insert it */
327
			x_literal_set = 0;
328
			submatch = 0;
329
		} else {
330
			submatch = 0;
331
			kmatch = NULL;
332
193830
			TAILQ_FOREACH(k, &kblist, entry) {
333
95855
				if (at > k->len)
334
					continue;
335
336
88992
				if (memcmp(k->seq, line, at) == 0) {
337
					/* sub match */
338
387
					submatch++;
339
387
					if (k->len == at)
340
353
						kmatch = k;
341
				}
342
343
				/* see if we can abort search early */
344
88992
				if (submatch > 1)
345
					break;
346
			}
347
		}
348
349
1074
		if (submatch == 1 && kmatch) {
350
353
			if (kmatch->ftab->xf_func == x_ins_string &&
351
			    kmatch->args && !macro_args) {
352
				/* treat macro string as input */
353
				macro_args = kmatch->args;
354
				ret = KSTD;
355
			} else
356
353
				ret = kmatch->ftab->xf_func(line[at - 1]);
357
		} else {
358
721
			if (submatch)
359
20
				continue;
360
701
			if (ntries > 1) {
361
6
				ret = x_error(0); /* unmatched meta sequence */
362
701
			} else if (at > 1) {
363
108
				x_ins(line);
364
				ret = KSTD;
365
108
			} else {
366
587
				ret = x_insert(line[0]);
367
			}
368
		}
369
370

1054
		switch (ret) {
371
		case KSTD:
372
1040
			if (kmatch)
373
339
				x_last_command = kmatch->ftab->xf_func;
374
			else
375
				x_last_command = NULL;
376
1040
			break;
377
		case KEOL:
378
13
			ret = xep - xbuf;
379
13
			return (ret);
380
			break;
381
		case KINTR:
382
			trapsig(SIGINT);
383
			x_mode(false);
384
			unwind(LSHELL);
385
			x_arg = -1;
386
			break;
387
		default:
388
			bi_errorf("invalid return code"); /* can't happen */
389
		}
390
391
		/* reset meta sequence */
392
		at = ntries = 0;
393
1040
		if (x_arg_set)
394
			x_arg_set = 0; /* reset args next time around */
395
		else
396
1040
			x_arg = -1;
397
	}
398
13
}
399
400
static int
401
x_insert(int c)
402
{
403
1174
	char	str[2];
404
405
	/*
406
	 *  Should allow tab and control chars.
407
	 */
408
587
	if (c == 0) {
409
		x_e_putc(BEL);
410
		return KSTD;
411
	}
412
587
	str[0] = c;
413
587
	str[1] = '\0';
414
2348
	while (x_arg--)
415
587
		x_ins(str);
416
587
	return KSTD;
417
587
}
418
419
static int
420
x_ins_string(int c)
421
{
422
	return x_insert(c);
423
}
424
425
static int
426
x_do_ins(const char *cp, size_t len)
427
{
428
1408
	if (xep+len >= xend) {
429
		x_e_putc(BEL);
430
		return -1;
431
	}
432
433
704
	memmove(xcp+len, xcp, xep - xcp + 1);
434
704
	memmove(xcp, cp, len);
435
704
	xcp += len;
436
704
	xep += len;
437
704
	return 0;
438
704
}
439
440
static int
441
x_ins(char *s)
442
{
443
1396
	char	*cp = xcp;
444
698
	int	adj = x_adj_done;
445
446
698
	if (x_do_ins(s, strlen(s)) < 0)
447
		return -1;
448
	/*
449
	 * x_zots() may result in a call to x_adjust()
450
	 * we want xcp to reflect the new position.
451
	 */
452
698
	xlp_valid = false;
453
698
	x_lastcp();
454
698
	x_adj_ok = (xcp >= xlp);
455
698
	x_zots(cp);
456
698
	if (adj == x_adj_done) {	/* has x_adjust() been called? */
457
		/* no */
458
2188
		for (cp = xlp; cp > xcp; )
459
396
			x_bs(*--cp);
460
	}
461
462
698
	x_adj_ok = 1;
463
698
	return 0;
464
698
}
465
466
static int
467
x_del_back(int c)
468
{
469
4
	int col = xcp - xbuf;
470
471
2
	if (col == 0) {
472
1
		x_e_putc(BEL);
473
1
		return KSTD;
474
	}
475
1
	if (x_arg > col)
476
		x_arg = col;
477

2
	while (x_arg < col && isu8cont(xcp[-x_arg]))
478
		x_arg++;
479
1
	x_goto(xcp - x_arg);
480
1
	x_delete(x_arg, false);
481
1
	return KSTD;
482
2
}
483
484
static int
485
x_del_char(int c)
486
{
487
	int nleft = xep - xcp;
488
489
	if (!nleft) {
490
		x_e_putc(BEL);
491
		return KSTD;
492
	}
493
	if (x_arg > nleft)
494
		x_arg = nleft;
495
	while (x_arg < nleft && isu8cont(xcp[x_arg]))
496
		x_arg++;
497
	x_delete(x_arg, false);
498
	return KSTD;
499
}
500
501
/* Delete nc bytes to the right of the cursor (including cursor position) */
502
static void
503
x_delete(int nc, int push)
504
{
505
	int	i,j;
506
	char	*cp;
507
508
14
	if (nc == 0)
509
		return;
510

7
	if (xmp != NULL && xmp > xcp) {
511
		if (xcp + nc > xmp)
512
			xmp = xcp;
513
		else
514
			xmp -= nc;
515
	}
516
517
	/*
518
	 * This lets us yank a word we have deleted.
519
	 */
520
7
	if (push)
521
		x_push(nc);
522
523
7
	xep -= nc;
524
7
	cp = xcp;
525
	j = 0;
526
	i = nc;
527
64
	while (i--) {
528
25
		j += x_size((unsigned char)*cp++);
529
	}
530
7
	memmove(xcp, xcp+nc, xep - xcp + 1);	/* Copies the null */
531
7
	x_adj_ok = 0;			/* don't redraw */
532
7
	xlp_valid = false;
533
7
	x_zots(xcp);
534
	/*
535
	 * if we are already filling the line,
536
	 * there is no need to ' ','\b'.
537
	 * But if we must, make sure we do the minimum.
538
	 */
539
7
	if ((i = xx_cols - 2 - x_col) > 0) {
540
7
		j = (j < i) ? j : i;
541
		i = j;
542
64
		while (i--)
543
25
			x_e_putc(' ');
544
		i = j;
545
64
		while (i--)
546
25
			x_e_putc('\b');
547
	}
548
	/*x_goto(xcp);*/
549
7
	x_adj_ok = 1;
550
7
	xlp_valid = false;
551
14
	for (cp = x_lastcp(); cp > xcp; )
552
		x_bs(*--cp);
553
554
7
	return;
555
7
}
556
557
static int
558
x_del_bword(int c)
559
{
560
	x_delete(x_bword(), true);
561
	return KSTD;
562
}
563
564
static int
565
x_mv_bword(int c)
566
{
567
	(void)x_bword();
568
	return KSTD;
569
}
570
571
static int
572
x_mv_fword(int c)
573
{
574
	x_goto(xcp + x_fword());
575
	return KSTD;
576
}
577
578
static int
579
x_del_fword(int c)
580
{
581
	x_delete(x_fword(), true);
582
	return KSTD;
583
}
584
585
static int
586
x_bword(void)
587
{
588
	int	nc = 0;
589
	char	*cp = xcp;
590
591
	if (cp == xbuf) {
592
		x_e_putc(BEL);
593
		return 0;
594
	}
595
	while (x_arg--) {
596
		while (cp != xbuf && is_mfs(cp[-1])) {
597
			cp--;
598
			nc++;
599
		}
600
		while (cp != xbuf && !is_mfs(cp[-1])) {
601
			cp--;
602
			nc++;
603
		}
604
	}
605
	x_goto(cp);
606
	return nc;
607
}
608
609
static int
610
x_fword(void)
611
{
612
	int	nc = 0;
613
	char	*cp = xcp;
614
615
	if (cp == xep) {
616
		x_e_putc(BEL);
617
		return 0;
618
	}
619
	while (x_arg--) {
620
		while (cp != xep && is_mfs(*cp)) {
621
			cp++;
622
			nc++;
623
		}
624
		while (cp != xep && !is_mfs(*cp)) {
625
			cp++;
626
			nc++;
627
		}
628
	}
629
	return nc;
630
}
631
632
static void
633
x_goto(char *cp)
634
{
635

1014
	if (cp < xbp || cp >= (xbp + x_displen)) {
636
		/* we are heading off screen */
637
		xcp = cp;
638
		x_adjust();
639
338
	} else if (cp < xcp) {		/* move back */
640
1048
		while (cp < xcp)
641
271
			x_bs((unsigned char)*--xcp);
642
85
	} else if (cp > xcp) {		/* move forward */
643
336
		while (cp > xcp)
644
84
			x_zotc((unsigned char)*xcp++);
645
	}
646
338
}
647
648
static void
649
x_bs(int c)
650
{
651
	int i;
652
653
1334
	i = x_size(c);
654
2668
	while (i--)
655
667
		x_e_putc('\b');
656
667
}
657
658
static int
659
x_size_str(char *cp)
660
{
661
	int size = 0;
662
3
	while (*cp)
663
		size += x_size(*cp++);
664
1
	return size;
665
}
666
667
static int
668
x_size(int c)
669
{
670
4938
	if (c=='\t')
671
		return 4;	/* Kludge, tabs are always four spaces. */
672
2469
	if (iscntrl(c))		/* control char */
673
		return 2;
674
2469
	if (isu8cont(c))
675
408
		return 0;
676
2061
	return 1;
677
2469
}
678
679
static void
680
x_zots(char *str)
681
{
682
1436
	int	adj = x_adj_done;
683
684

920
	if (str > xbuf && isu8cont(*str)) {
685

996
		while (str > xbuf && isu8cont(*str))
686
228
			str--;
687
156
		x_e_putc('\b');
688
156
	}
689
718
	x_lastcp();
690

7876
	while (*str && str < xlp && adj == x_adj_done)
691
1610
		x_zotc(*str++);
692
718
}
693
694
static void
695
x_zotc(int c)
696
{
697
3396
	if (c == '\t') {
698
		/*  Kludge, tabs are always four spaces.  */
699
		x_e_puts("    ");
700
1698
	} else if (iscntrl(c)) {
701
4
		x_e_putc('^');
702
4
		x_e_putc(UNCTRL(c));
703
4
	} else
704
1694
		x_e_putc(c);
705
1698
}
706
707
static int
708
x_mv_back(int c)
709
{
710
492
	int col = xcp - xbuf;
711
712
246
	if (col == 0) {
713
		x_e_putc(BEL);
714
		return KSTD;
715
	}
716
246
	if (x_arg > col)
717
		x_arg = col;
718

492
	while (x_arg < col && isu8cont(xcp[-x_arg]))
719
		x_arg++;
720
246
	x_goto(xcp - x_arg);
721
246
	return KSTD;
722
246
}
723
724
static int
725
x_mv_forw(int c)
726
{
727
168
	int nleft = xep - xcp;
728
729
84
	if (!nleft) {
730
		x_e_putc(BEL);
731
		return KSTD;
732
	}
733
84
	if (x_arg > nleft)
734
		x_arg = nleft;
735

168
	while (x_arg < nleft && isu8cont(xcp[x_arg]))
736
		x_arg++;
737
84
	x_goto(xcp + x_arg);
738
84
	return KSTD;
739
84
}
740
741
static int
742
x_search_char_forw(int c)
743
{
744
	char *cp = xcp;
745
746
	*xep = '\0';
747
	c = x_e_getc();
748
	while (x_arg--) {
749
		if (c < 0 ||
750
		    ((cp = (cp == xep) ? NULL : strchr(cp + 1, c)) == NULL &&
751
		    (cp = strchr(xbuf, c)) == NULL)) {
752
			x_e_putc(BEL);
753
			return KSTD;
754
		}
755
	}
756
	x_goto(cp);
757
	return KSTD;
758
}
759
760
static int
761
x_search_char_back(int c)
762
{
763
	char *cp = xcp, *p;
764
765
	c = x_e_getc();
766
	for (; x_arg--; cp = p)
767
		for (p = cp; ; ) {
768
			if (p-- == xbuf)
769
				p = xep;
770
			if (c < 0 || p == cp) {
771
				x_e_putc(BEL);
772
				return KSTD;
773
			}
774
			if (*p == c)
775
				break;
776
		}
777
	x_goto(cp);
778
	return KSTD;
779
}
780
781
static int
782
x_newline(int c)
783
{
784
18
	x_e_putc('\r');
785
9
	x_e_putc('\n');
786
9
	x_flush();
787
9
	*xep++ = '\n';
788
9
	return KEOL;
789
}
790
791
static int
792
x_end_of_text(int c)
793
{
794
8
	x_zotc(edchars.eof);
795
4
	x_putc('\r');
796
4
	x_putc('\n');
797
4
	x_flush();
798
4
	return KEOL;
799
}
800
801
static int x_beg_hist(int c) { x_load_hist(history); return KSTD;}
802
803
static int x_end_hist(int c) { x_load_hist(histptr); return KSTD;}
804
805
2
static int x_prev_com(int c) { x_load_hist(x_histp - x_arg); return KSTD;}
806
807
static int x_next_com(int c) { x_load_hist(x_histp + x_arg); return KSTD;}
808
809
/* Goto a particular history number obtained from argument.
810
 * If no argument is given history 1 is probably not what you
811
 * want so we'll simply go to the oldest one.
812
 */
813
static int
814
x_goto_hist(int c)
815
{
816
	if (x_arg_defaulted)
817
		x_load_hist(history);
818
	else
819
		x_load_hist(histptr + x_arg - source->line);
820
	return KSTD;
821
}
822
823
static void
824
x_load_hist(char **hp)
825
{
826
	int	oldsize;
827
828

3
	if (hp < history || hp > histptr) {
829
		x_e_putc(BEL);
830
		return;
831
	}
832
1
	x_histp = hp;
833
1
	oldsize = x_size_str(xbuf);
834
1
	strlcpy(xbuf, *hp, xend - xbuf);
835
1
	xbp = xbuf;
836
1
	xep = xcp = xbuf + strlen(xbuf);
837
1
	xlp_valid = false;
838
1
	if (xep <= x_lastcp())
839
1
		x_redraw(oldsize);
840
1
	x_goto(xep);
841
2
}
842
843
static int
844
x_nl_next_com(int c)
845
{
846
	x_nextcmd = source->line - (histptr - x_histp) + 1;
847
	return (x_newline(c));
848
}
849
850
static int
851
x_eot_del(int c)
852
{
853
8
	if (xep == xbuf && x_arg_defaulted)
854
4
		return (x_end_of_text(c));
855
	else
856
		return (x_del_char(c));
857
4
}
858
859
static void *
860
kb_find_hist_func(char c)
861
{
862
	struct kb_entry		*k;
863
	char			line[LINE + 1];
864
865
	line[0] = c;
866
	line[1] = '\0';
867
	TAILQ_FOREACH(k, &kblist, entry)
868
		if (!strcmp(k->seq, line))
869
			return (k->ftab->xf_func);
870
871
	return (x_insert);
872
}
873
874
/* reverse incremental history search */
875
static int
876
x_search_hist(int c)
877
{
878
	int offset = -1;	/* offset of match in xbuf, else -1 */
879
	char pat [256+1];	/* pattern buffer */
880
	char *p = pat;
881
	int (*f)(int);
882
883
	*p = '\0';
884
	while (1) {
885
		if (offset < 0) {
886
			x_e_puts("\nI-search: ");
887
			x_e_puts(pat);
888
		}
889
		x_flush();
890
		if ((c = x_e_getc()) < 0)
891
			return KSTD;
892
		f = kb_find_hist_func(c);
893
		if (c == CTRL('[')) {
894
			x_e_ungetc(c);
895
			break;
896
		} else if (f == x_search_hist)
897
			offset = x_search(pat, 0, offset);
898
		else if (f == x_del_back) {
899
			if (p == pat) {
900
				offset = -1;
901
				break;
902
			}
903
			if (p > pat)
904
				*--p = '\0';
905
			if (p == pat)
906
				offset = -1;
907
			else
908
				offset = x_search(pat, 1, offset);
909
			continue;
910
		} else if (f == x_insert) {
911
			/* add char to pattern */
912
			/* overflow check... */
913
			if (p >= &pat[sizeof(pat) - 1]) {
914
				x_e_putc(BEL);
915
				continue;
916
			}
917
			*p++ = c, *p = '\0';
918
			if (offset >= 0) {
919
				/* already have partial match */
920
				offset = x_match(xbuf, pat);
921
				if (offset >= 0) {
922
					x_goto(xbuf + offset + (p - pat) -
923
					    (*pat == '^'));
924
					continue;
925
				}
926
			}
927
			offset = x_search(pat, 0, offset);
928
		} else { /* other command */
929
			x_e_ungetc(c);
930
			break;
931
		}
932
	}
933
	if (offset < 0)
934
		x_redraw(-1);
935
	return KSTD;
936
}
937
938
/* search backward from current line */
939
static int
940
x_search(char *pat, int sameline, int offset)
941
{
942
	char **hp;
943
	int i;
944
945
	for (hp = x_histp - (sameline ? 0 : 1) ; hp >= history; --hp) {
946
		i = x_match(*hp, pat);
947
		if (i >= 0) {
948
			if (offset < 0)
949
				x_e_putc('\n');
950
			x_load_hist(hp);
951
			x_goto(xbuf + i + strlen(pat) - (*pat == '^'));
952
			return i;
953
		}
954
	}
955
	x_e_putc(BEL);
956
	x_histp = histptr;
957
	return -1;
958
}
959
960
/* return position of first match of pattern in string, else -1 */
961
static int
962
x_match(char *str, char *pat)
963
{
964
	if (*pat == '^') {
965
		return (strncmp(str, pat+1, strlen(pat+1)) == 0) ? 0 : -1;
966
	} else {
967
		char *q = strstr(str, pat);
968
		return (q == NULL) ? -1 : q - str;
969
	}
970
}
971
972
static int
973
x_del_line(int c)
974
{
975
	int	i, j;
976
977
	*xep = 0;
978
	i = xep - xbuf;
979
	j = x_size_str(xbuf);
980
	xcp = xbuf;
981
	x_push(i);
982
	xlp = xbp = xep = xbuf;
983
	xlp_valid = true;
984
	*xcp = 0;
985
	xmp = NULL;
986
	x_redraw(j);
987
	return KSTD;
988
}
989
990
static int
991
x_mv_end(int c)
992
{
993
	x_goto(xep);
994
	return KSTD;
995
}
996
997
static int
998
x_mv_begin(int c)
999
{
1000
	x_goto(xbuf);
1001
	return KSTD;
1002
}
1003
1004
static int
1005
x_draw_line(int c)
1006
{
1007
	x_redraw(-1);
1008
	return KSTD;
1009
1010
}
1011
1012
/* Redraw (part of) the line.  If limit is < 0, the everything is redrawn
1013
 * on a NEW line, otherwise limit is the screen column up to which needs
1014
 * redrawing.
1015
 */
1016
static void
1017
x_redraw(int limit)
1018
{
1019
	int	i, j, truncate = 0;
1020
	char	*cp;
1021
1022
26
	x_adj_ok = 0;
1023
13
	if (limit == -1)
1024
		x_e_putc('\n');
1025
	else
1026
13
		x_e_putc('\r');
1027
13
	x_flush();
1028
13
	if (xbp == xbuf) {
1029
13
		x_col = promptlen(prompt, NULL);
1030
13
		if (x_col > xx_cols)
1031
			truncate = (x_col / xx_cols) * xx_cols;
1032
13
		if (prompt_redraw)
1033
13
			pprompt(prompt + prompt_skip, truncate);
1034
	}
1035
13
	if (x_col > xx_cols)
1036
		x_col = x_col - (x_col / xx_cols) * xx_cols;
1037
13
	x_displen = xx_cols - 2 - x_col;
1038
13
	if (x_displen < 1) {
1039
		x_col = 0;
1040
		x_displen = xx_cols - 2;
1041
	}
1042
13
	xlp_valid = false;
1043
13
	cp = x_lastcp();
1044
13
	x_zots(xbp);
1045

26
	if (xbp != xbuf || xep > xlp)
1046
		limit = xx_cols;
1047
13
	if (limit >= 0) {
1048
13
		if (xep > xlp)
1049
			i = 0;			/* we fill the line */
1050
		else
1051
13
			i = limit - (xlp - xbp);
1052
1053

2264
		for (j = 0; j < i && x_col < (xx_cols - 2); j++)
1054
744
			x_e_putc(' ');
1055
		i = ' ';
1056
13
		if (xep > xlp) {		/* more off screen */
1057
			if (xbp > xbuf)
1058
				i = '*';
1059
			else
1060
				i = '>';
1061
13
		} else if (xbp > xbuf)
1062
			i = '<';
1063
13
		x_e_putc(i);
1064
13
		j++;
1065
1540
		while (j--)
1066
757
			x_e_putc('\b');
1067
	}
1068
26
	for (cp = xlp; cp > xcp; )
1069
		x_bs(*--cp);
1070
13
	x_adj_ok = 1;
1071
#ifdef DEBUG
1072
	x_flush();
1073
#endif
1074
	return;
1075
13
}
1076
1077
static int
1078
x_transpose(int c)
1079
{
1080
	char	tmp;
1081
1082
	/* What transpose is meant to do seems to be up for debate. This
1083
	 * is a general summary of the options; the text is abcd with the
1084
	 * upper case character or underscore indicating the cursor position:
1085
	 *     Who			Before	After  Before	After
1086
	 *     at&t ksh in emacs mode:	abCd	abdC   abcd_	(bell)
1087
	 *     at&t ksh in gmacs mode:	abCd	baCd   abcd_	abdc_
1088
	 *     gnu emacs:		abCd	acbD   abcd_	abdc_
1089
	 * Pdksh currently goes with GNU behavior since I believe this is the
1090
	 * most common version of emacs, unless in gmacs mode, in which case
1091
	 * it does the at&t ksh gmacs mode.
1092
	 * This should really be broken up into 3 functions so users can bind
1093
	 * to the one they want.
1094
	 */
1095
	if (xcp == xbuf) {
1096
		x_e_putc(BEL);
1097
		return KSTD;
1098
	} else if (xcp == xep || Flag(FGMACS)) {
1099
		if (xcp - xbuf == 1) {
1100
			x_e_putc(BEL);
1101
			return KSTD;
1102
		}
1103
		/* Gosling/Unipress emacs style: Swap two characters before the
1104
		 * cursor, do not change cursor position
1105
		 */
1106
		x_bs(xcp[-1]);
1107
		x_bs(xcp[-2]);
1108
		x_zotc(xcp[-1]);
1109
		x_zotc(xcp[-2]);
1110
		tmp = xcp[-1];
1111
		xcp[-1] = xcp[-2];
1112
		xcp[-2] = tmp;
1113
	} else {
1114
		/* GNU emacs style: Swap the characters before and under the
1115
		 * cursor, move cursor position along one.
1116
		 */
1117
		x_bs(xcp[-1]);
1118
		x_zotc(xcp[0]);
1119
		x_zotc(xcp[-1]);
1120
		tmp = xcp[-1];
1121
		xcp[-1] = xcp[0];
1122
		xcp[0] = tmp;
1123
		x_bs(xcp[0]);
1124
		x_goto(xcp + 1);
1125
	}
1126
	return KSTD;
1127
}
1128
1129
static int
1130
x_literal(int c)
1131
{
1132
	x_literal_set = 1;
1133
	return KSTD;
1134
}
1135
1136
static int
1137
x_kill(int c)
1138
{
1139
	int col = xcp - xbuf;
1140
	int lastcol = xep - xbuf;
1141
	int ndel;
1142
1143
	if (x_arg_defaulted)
1144
		x_arg = lastcol;
1145
	else if (x_arg > lastcol)
1146
		x_arg = lastcol;
1147
	while (x_arg < lastcol && isu8cont(xbuf[x_arg]))
1148
		x_arg++;
1149
	ndel = x_arg - col;
1150
	if (ndel < 0) {
1151
		x_goto(xbuf + x_arg);
1152
		ndel = -ndel;
1153
	}
1154
	x_delete(ndel, true);
1155
	return KSTD;
1156
}
1157
1158
static void
1159
x_push(int nchars)
1160
{
1161
	char	*cp = str_nsave(xcp, nchars, AEDIT);
1162
	afree(killstack[killsp], AEDIT);
1163
	killstack[killsp] = cp;
1164
	killsp = (killsp + 1) % KILLSIZE;
1165
}
1166
1167
static int
1168
x_yank(int c)
1169
{
1170
	if (killsp == 0)
1171
		killtp = KILLSIZE;
1172
	else
1173
		killtp = killsp;
1174
	killtp --;
1175
	if (killstack[killtp] == 0) {
1176
		x_e_puts("\nnothing to yank");
1177
		x_redraw(-1);
1178
		return KSTD;
1179
	}
1180
	xmp = xcp;
1181
	x_ins(killstack[killtp]);
1182
	return KSTD;
1183
}
1184
1185
static int
1186
x_meta_yank(int c)
1187
{
1188
	int	len;
1189
	if ((x_last_command != x_yank && x_last_command != x_meta_yank) ||
1190
	    killstack[killtp] == 0) {
1191
		killtp = killsp;
1192
		x_e_puts("\nyank something first");
1193
		x_redraw(-1);
1194
		return KSTD;
1195
	}
1196
	len = strlen(killstack[killtp]);
1197
	x_goto(xcp - len);
1198
	x_delete(len, false);
1199
	do {
1200
		if (killtp == 0)
1201
			killtp = KILLSIZE - 1;
1202
		else
1203
			killtp--;
1204
	} while (killstack[killtp] == 0);
1205
	x_ins(killstack[killtp]);
1206
	return KSTD;
1207
}
1208
1209
static int
1210
x_abort(int c)
1211
{
1212
	/* x_zotc(c); */
1213
2
	xlp = xep = xcp = xbp = xbuf;
1214
1
	xlp_valid = true;
1215
1
	*xcp = 0;
1216
1
	return KINTR;
1217
}
1218
1219
static int
1220
x_error(int c)
1221
{
1222
12
	x_e_putc(BEL);
1223
6
	return KSTD;
1224
}
1225
1226
static char *
1227
kb_encode(const char *s)
1228
{
1229
	static char		l[LINE + 1];
1230
	int			at = 0;
1231
1232
	l[at] = '\0';
1233
	while (*s) {
1234
		if (*s == '^') {
1235
			s++;
1236
			if (*s >= '?')
1237
				l[at++] = CTRL(*s);
1238
			else {
1239
				l[at++] = '^';
1240
				s--;
1241
			}
1242
		} else
1243
			l[at++] = *s;
1244
		l[at] = '\0';
1245
		s++;
1246
	}
1247
	return (l);
1248
}
1249
1250
static char *
1251
kb_decode(const char *s)
1252
{
1253
	static char		l[LINE + 1];
1254
	int			i, at = 0;
1255
1256
	l[0] = '\0';
1257
	for (i = 0; i < strlen(s); i++) {
1258
		if (iscntrl((unsigned char)s[i])) {
1259
			l[at++] = '^';
1260
			l[at++] = UNCTRL(s[i]);
1261
		} else
1262
			l[at++] = s[i];
1263
		l[at] = '\0';
1264
	}
1265
1266
	return (l);
1267
}
1268
1269
static int
1270
kb_match(char *s)
1271
{
1272
161016
	int			len = strlen(s);
1273
	struct kb_entry		*k;
1274
1275
7179456
	TAILQ_FOREACH(k, &kblist, entry) {
1276
3511194
		if (len > k->len)
1277
			continue;
1278
1279
1966366
		if (memcmp(k->seq, s, len) == 0)
1280
1974
			return (1);
1281
	}
1282
1283
78534
	return (0);
1284
80508
}
1285
1286
static void
1287
kb_del(struct kb_entry *k)
1288
{
1289
	TAILQ_REMOVE(&kblist, k, entry);
1290
	free(k->args);
1291
	afree(k, AEDIT);
1292
}
1293
1294
static struct kb_entry *
1295
kb_add_string(void *func, void *args, char *str)
1296
{
1297
	int			i, count;
1298
	struct kb_entry		*k;
1299
	struct x_ftab		*xf = NULL;
1300
1301
4824324
	for (i = 0; i < NELEM(x_ftab); i++)
1302
2371908
		if (x_ftab[i].xf_func == func) {
1303
			xf = (struct x_ftab *)&x_ftab[i];
1304
80508
			break;
1305
		}
1306
80508
	if (xf == NULL)
1307
		return (NULL);
1308
1309
80508
	if (kb_match(str)) {
1310
1974
		if (x_bind_quiet == 0)
1311
			bi_errorf("duplicate binding for %s", kb_decode(str));
1312
1974
		return (NULL);
1313
	}
1314
78534
	count = strlen(str);
1315
1316
78534
	k = alloc(sizeof *k + count + 1, AEDIT);
1317
78534
	k->seq = (unsigned char *)(k + 1);
1318
78534
	k->len = count;
1319
78534
	k->ftab = xf;
1320
157068
	k->args = args ? strdup(args) : NULL;
1321
1322
78534
	strlcpy(k->seq, str, count + 1);
1323
1324
78534
	TAILQ_INSERT_TAIL(&kblist, k, entry);
1325
1326
78534
	return (k);
1327
80508
}
1328
1329
static struct kb_entry *
1330
kb_add(void *func, void *args, ...)
1331
{
1332
161016
	va_list			ap;
1333
	int			i, count;
1334
80508
	char			l[LINE + 1];
1335
1336
80508
	va_start(ap, args);
1337
	count = 0;
1338

988136
	while (va_arg(ap, unsigned int) != 0)
1339
166526
		count++;
1340
80508
	va_end(ap);
1341
1342
80508
	va_start(ap, args);
1343
655084
	for (i = 0; i <= count /* <= is correct */; i++)
1344
741102
		l[i] = (unsigned char)va_arg(ap, unsigned int);
1345
80508
	va_end(ap);
1346
1347
161016
	return (kb_add_string(func, args, l));
1348
80508
}
1349
1350
static void
1351
kb_print(struct kb_entry *k)
1352
{
1353
	if (!(k->ftab->xf_flags & XF_NOBIND))
1354
		shprintf("%s = %s\n",
1355
		    kb_decode(k->seq), k->ftab->xf_name);
1356
	else if (k->args) {
1357
		shprintf("%s = ", kb_decode(k->seq));
1358
		shprintf("'%s'\n", kb_decode(k->args));
1359
	}
1360
}
1361
1362
int
1363
x_bind(const char *a1, const char *a2,
1364
	int macro,		/* bind -m */
1365
	int list)		/* bind -l */
1366
{
1367
	int			i;
1368
	struct kb_entry		*k, *kb;
1369
	char			in[LINE + 1];
1370
1371
	if (x_tty == 0) {
1372
		bi_errorf("cannot bind, not a tty");
1373
		return (1);
1374
	}
1375
1376
	if (list) {
1377
		/* show all function names */
1378
		for (i = 0; i < NELEM(x_ftab); i++) {
1379
			if (x_ftab[i].xf_name == NULL)
1380
				continue;
1381
			if (x_ftab[i].xf_name &&
1382
			    !(x_ftab[i].xf_flags & XF_NOBIND))
1383
				shprintf("%s\n", x_ftab[i].xf_name);
1384
		}
1385
		return (0);
1386
	}
1387
1388
	if (a1 == NULL) {
1389
		/* show all bindings */
1390
		TAILQ_FOREACH(k, &kblist, entry)
1391
			kb_print(k);
1392
		return (0);
1393
	}
1394
1395
	snprintf(in, sizeof in, "%s", kb_encode(a1));
1396
	if (a2 == NULL) {
1397
		/* print binding */
1398
		TAILQ_FOREACH(k, &kblist, entry)
1399
			if (!strcmp(k->seq, in)) {
1400
				kb_print(k);
1401
				return (0);
1402
			}
1403
		shprintf("%s = %s\n", kb_decode(a1), "auto-insert");
1404
		return (0);
1405
	}
1406
1407
	if (strlen(a2) == 0) {
1408
		/* clear binding */
1409
		TAILQ_FOREACH_SAFE(k, &kblist, entry, kb)
1410
			if (!strcmp(k->seq, in)) {
1411
				kb_del(k);
1412
				break;
1413
			}
1414
		return (0);
1415
	}
1416
1417
	/* set binding */
1418
	if (macro) {
1419
		/* delete old mapping */
1420
		TAILQ_FOREACH_SAFE(k, &kblist, entry, kb)
1421
			if (!strcmp(k->seq, in)) {
1422
				kb_del(k);
1423
				break;
1424
			}
1425
		kb_add_string(x_ins_string, kb_encode(a2), in);
1426
		return (0);
1427
	}
1428
1429
	/* set non macro binding */
1430
	for (i = 0; i < NELEM(x_ftab); i++) {
1431
		if (x_ftab[i].xf_name == NULL)
1432
			continue;
1433
		if (!strcmp(x_ftab[i].xf_name, a2)) {
1434
			/* delete old mapping */
1435
			TAILQ_FOREACH_SAFE(k, &kblist, entry, kb)
1436
				if (!strcmp(k->seq, in)) {
1437
					kb_del(k);
1438
					break;
1439
				}
1440
			kb_add_string(x_ftab[i].xf_func, NULL, in);
1441
			return (0);
1442
		}
1443
	}
1444
	bi_errorf("%s: no such function", a2);
1445
	return (1);
1446
}
1447
1448
void
1449
x_init_emacs(void)
1450
{
1451
1760
	x_tty = 1;
1452
880
	ainit(AEDIT);
1453
880
	x_nextcmd = -1;
1454
1455
880
	TAILQ_INIT(&kblist);
1456
1457
	/* man page order */
1458
880
	kb_add(x_abort,			NULL, CTRL('G'), 0);
1459
880
	kb_add(x_mv_back,		NULL, CTRL('B'), 0);
1460
880
	kb_add(x_mv_back,		NULL, CTRL('X'), CTRL('D'), 0);
1461
880
	kb_add(x_mv_bword,		NULL, CTRL('['), 'b', 0);
1462
880
	kb_add(x_beg_hist,		NULL, CTRL('['), '<', 0);
1463
880
	kb_add(x_mv_begin,		NULL, CTRL('A'), 0);
1464
880
	kb_add(x_fold_capitalize,	NULL, CTRL('['), 'C', 0);
1465
880
	kb_add(x_fold_capitalize,	NULL, CTRL('['), 'c', 0);
1466
880
	kb_add(x_comment,		NULL, CTRL('['), '#', 0);
1467
880
	kb_add(x_complete,		NULL, CTRL('['), CTRL('['), 0);
1468
880
	kb_add(x_comp_comm,		NULL, CTRL('X'), CTRL('['), 0);
1469
880
	kb_add(x_comp_file,		NULL, CTRL('['), CTRL('X'), 0);
1470
880
	kb_add(x_comp_list,		NULL, CTRL('I'), 0);
1471
880
	kb_add(x_comp_list,		NULL, CTRL('['), '=', 0);
1472
880
	kb_add(x_del_back,		NULL, CTRL('?'), 0);
1473
880
	kb_add(x_del_back,		NULL, CTRL('H'), 0);
1474
880
	kb_add(x_del_char,		NULL, CTRL('['), '[', '3', '~', 0); /* delete */
1475
880
	kb_add(x_del_bword,		NULL, CTRL('['), CTRL('?'), 0);
1476
880
	kb_add(x_del_bword,		NULL, CTRL('['), CTRL('H'), 0);
1477
880
	kb_add(x_del_bword,		NULL, CTRL('['), 'h', 0);
1478
880
	kb_add(x_del_fword,		NULL, CTRL('['), 'd', 0);
1479
880
	kb_add(x_next_com,		NULL, CTRL('N'), 0);
1480
880
	kb_add(x_next_com,		NULL, CTRL('X'), 'B', 0);
1481
880
	kb_add(x_fold_lower,		NULL, CTRL('['), 'L', 0);
1482
880
	kb_add(x_fold_lower,		NULL, CTRL('['), 'l', 0);
1483
880
	kb_add(x_end_hist,		NULL, CTRL('['), '>', 0);
1484
880
	kb_add(x_mv_end,		NULL, CTRL('E'), 0);
1485
	/* how to handle: eot: ^_, underneath copied from original keybindings */
1486
880
	kb_add(x_end_of_text,		NULL, CTRL('_'), 0);
1487
880
	kb_add(x_eot_del,		NULL, CTRL('D'), 0);
1488
	/* error */
1489
880
	kb_add(x_xchg_point_mark,	NULL, CTRL('X'), CTRL('X'), 0);
1490
880
	kb_add(x_expand,		NULL, CTRL('['), '*', 0);
1491
880
	kb_add(x_mv_forw,		NULL, CTRL('F'), 0);
1492
880
	kb_add(x_mv_forw,		NULL, CTRL('X'), 'C', 0);
1493
880
	kb_add(x_mv_fword,		NULL, CTRL('['), 'f', 0);
1494
880
	kb_add(x_goto_hist,		NULL, CTRL('['), 'g', 0);
1495
	/* kill-line */
1496
880
	kb_add(x_del_bword,		NULL, CTRL('W'), 0); /* not what man says */
1497
880
	kb_add(x_kill,			NULL, CTRL('K'), 0);
1498
880
	kb_add(x_enumerate,		NULL, CTRL('['), '?', 0);
1499
880
	kb_add(x_list_comm,		NULL, CTRL('X'), '?', 0);
1500
880
	kb_add(x_list_file,		NULL, CTRL('X'), CTRL('Y'), 0);
1501
880
	kb_add(x_newline,		NULL, CTRL('J'), 0);
1502
880
	kb_add(x_newline,		NULL, CTRL('M'), 0);
1503
880
	kb_add(x_nl_next_com,		NULL, CTRL('O'), 0);
1504
	/* no-op */
1505
880
	kb_add(x_prev_histword,		NULL, CTRL('['), '.', 0);
1506
880
	kb_add(x_prev_histword,		NULL, CTRL('['), '_', 0);
1507
	/* how to handle: quote: ^^ */
1508
880
	kb_add(x_draw_line,		NULL, CTRL('L'), 0);
1509
880
	kb_add(x_search_char_back,	NULL, CTRL('['), CTRL(']'), 0);
1510
880
	kb_add(x_search_char_forw,	NULL, CTRL(']'), 0);
1511
880
	kb_add(x_search_hist,		NULL, CTRL('R'), 0);
1512
880
	kb_add(x_set_mark,		NULL, CTRL('['), ' ', 0);
1513
880
	kb_add(x_transpose,		NULL, CTRL('T'), 0);
1514
880
	kb_add(x_prev_com,		NULL, CTRL('P'), 0);
1515
880
	kb_add(x_prev_com,		NULL, CTRL('X'), 'A', 0);
1516
880
	kb_add(x_fold_upper,		NULL, CTRL('['), 'U', 0);
1517
880
	kb_add(x_fold_upper,		NULL, CTRL('['), 'u', 0);
1518
880
	kb_add(x_literal,		NULL, CTRL('V'), 0);
1519
880
	kb_add(x_literal,		NULL, CTRL('^'), 0);
1520
880
	kb_add(x_yank,			NULL, CTRL('Y'), 0);
1521
880
	kb_add(x_meta_yank,		NULL, CTRL('['), 'y', 0);
1522
	/* man page ends here */
1523
1524
	/* arrow keys */
1525
880
	kb_add(x_prev_com,		NULL, CTRL('['), '[', 'A', 0); /* up */
1526
880
	kb_add(x_next_com,		NULL, CTRL('['), '[', 'B', 0); /* down */
1527
880
	kb_add(x_mv_forw,		NULL, CTRL('['), '[', 'C', 0); /* right */
1528
880
	kb_add(x_mv_back,		NULL, CTRL('['), '[', 'D', 0); /* left */
1529
880
	kb_add(x_prev_com,		NULL, CTRL('['), 'O', 'A', 0); /* up */
1530
880
	kb_add(x_next_com,		NULL, CTRL('['), 'O', 'B', 0); /* down */
1531
880
	kb_add(x_mv_forw,		NULL, CTRL('['), 'O', 'C', 0); /* right */
1532
880
	kb_add(x_mv_back,		NULL, CTRL('['), 'O', 'D', 0); /* left */
1533
1534
	/* more navigation keys */
1535
880
	kb_add(x_mv_begin,		NULL, CTRL('['), '[', 'H', 0); /* home */
1536
880
	kb_add(x_mv_end,		NULL, CTRL('['), '[', 'F', 0); /* end */
1537
880
	kb_add(x_mv_begin,		NULL, CTRL('['), 'O', 'H', 0); /* home */
1538
880
	kb_add(x_mv_end,		NULL, CTRL('['), 'O', 'F', 0); /* end */
1539
880
	kb_add(x_mv_begin,		NULL, CTRL('['), '[', '1', '~', 0); /* home */
1540
880
	kb_add(x_mv_end,		NULL, CTRL('['), '[', '4', '~', 0); /* end */
1541
1542
	/* can't be bound */
1543
880
	kb_add(x_set_arg,		NULL, CTRL('['), '0', 0);
1544
880
	kb_add(x_set_arg,		NULL, CTRL('['), '1', 0);
1545
880
	kb_add(x_set_arg,		NULL, CTRL('['), '2', 0);
1546
880
	kb_add(x_set_arg,		NULL, CTRL('['), '3', 0);
1547
880
	kb_add(x_set_arg,		NULL, CTRL('['), '4', 0);
1548
880
	kb_add(x_set_arg,		NULL, CTRL('['), '5', 0);
1549
880
	kb_add(x_set_arg,		NULL, CTRL('['), '6', 0);
1550
880
	kb_add(x_set_arg,		NULL, CTRL('['), '7', 0);
1551
880
	kb_add(x_set_arg,		NULL, CTRL('['), '8', 0);
1552
880
	kb_add(x_set_arg,		NULL, CTRL('['), '9', 0);
1553
1554
	/* ctrl arrow keys */
1555
880
	kb_add(x_mv_end,		NULL, CTRL('['), '[', '1', ';', '5', 'A', 0); /* ctrl up */
1556
880
	kb_add(x_mv_begin,		NULL, CTRL('['), '[', '1', ';', '5', 'B', 0); /* ctrl down */
1557
880
	kb_add(x_mv_fword,		NULL, CTRL('['), '[', '1', ';', '5', 'C', 0); /* ctrl right */
1558
880
	kb_add(x_mv_bword,		NULL, CTRL('['), '[', '1', ';', '5', 'D', 0); /* ctrl left */
1559
880
}
1560
1561
void
1562
x_emacs_keys(X_chars *ec)
1563
{
1564
1316
	x_bind_quiet = 1;
1565
658
	if (ec->erase >= 0) {
1566
658
		kb_add(x_del_back, NULL, ec->erase, 0);
1567
658
		kb_add(x_del_bword, NULL, CTRL('['), ec->erase, 0);
1568
658
	}
1569
658
	if (ec->kill >= 0)
1570
658
		kb_add(x_del_line, NULL, ec->kill, 0);
1571
658
	if (ec->werase >= 0)
1572
658
		kb_add(x_del_bword, NULL, ec->werase, 0);
1573
658
	if (ec->intr >= 0)
1574
658
		kb_add(x_abort, NULL, ec->intr, 0);
1575
658
	if (ec->quit >= 0)
1576
658
		kb_add(x_noop, NULL, ec->quit, 0);
1577
658
	x_bind_quiet = 0;
1578
658
}
1579
1580
static int
1581
x_set_mark(int c)
1582
{
1583
	xmp = xcp;
1584
	return KSTD;
1585
}
1586
1587
static int
1588
x_kill_region(int c)
1589
{
1590
	int	rsize;
1591
	char	*xr;
1592
1593
	if (xmp == NULL) {
1594
		x_e_putc(BEL);
1595
		return KSTD;
1596
	}
1597
	if (xmp > xcp) {
1598
		rsize = xmp - xcp;
1599
		xr = xcp;
1600
	} else {
1601
		rsize = xcp - xmp;
1602
		xr = xmp;
1603
	}
1604
	x_goto(xr);
1605
	x_delete(rsize, true);
1606
	xmp = xr;
1607
	return KSTD;
1608
}
1609
1610
static int
1611
x_xchg_point_mark(int c)
1612
{
1613
	char	*tmp;
1614
1615
	if (xmp == NULL) {
1616
		x_e_putc(BEL);
1617
		return KSTD;
1618
	}
1619
	tmp = xmp;
1620
	xmp = xcp;
1621
	x_goto( tmp );
1622
	return KSTD;
1623
}
1624
1625
static int
1626
x_version(int c)
1627
{
1628
	char *o_xbuf = xbuf, *o_xend = xend;
1629
	char *o_xbp = xbp, *o_xep = xep, *o_xcp = xcp;
1630
	int lim = x_lastcp() - xbp;
1631
1632
	xbuf = xbp = xcp = (char *) ksh_version + 4;
1633
	xend = xep = (char *) ksh_version + 4 + strlen(ksh_version + 4);
1634
	x_redraw(lim);
1635
	x_flush();
1636
1637
	c = x_e_getc();
1638
	xbuf = o_xbuf;
1639
	xend = o_xend;
1640
	xbp = o_xbp;
1641
	xep = o_xep;
1642
	xcp = o_xcp;
1643
	x_redraw(strlen(ksh_version));
1644
1645
	if (c < 0)
1646
		return KSTD;
1647
	/* This is what at&t ksh seems to do...  Very bizarre */
1648
	if (c != ' ')
1649
		x_e_ungetc(c);
1650
1651
	return KSTD;
1652
}
1653
1654
static int
1655
x_noop(int c)
1656
{
1657
	return KSTD;
1658
}
1659
1660
/*
1661
 *	File/command name completion routines
1662
 */
1663
1664
static int
1665
x_comp_comm(int c)
1666
{
1667
	do_complete(XCF_COMMAND, CT_COMPLETE);
1668
	return KSTD;
1669
}
1670
static int
1671
x_list_comm(int c)
1672
{
1673
	do_complete(XCF_COMMAND, CT_LIST);
1674
	return KSTD;
1675
}
1676
static int
1677
x_complete(int c)
1678
{
1679
	do_complete(XCF_COMMAND_FILE, CT_COMPLETE);
1680
	return KSTD;
1681
}
1682
static int
1683
x_enumerate(int c)
1684
{
1685
	do_complete(XCF_COMMAND_FILE, CT_LIST);
1686
	return KSTD;
1687
}
1688
static int
1689
x_comp_file(int c)
1690
{
1691
	do_complete(XCF_FILE, CT_COMPLETE);
1692
	return KSTD;
1693
}
1694
static int
1695
x_list_file(int c)
1696
{
1697
	do_complete(XCF_FILE, CT_LIST);
1698
	return KSTD;
1699
}
1700
static int
1701
x_comp_list(int c)
1702
{
1703
12
	do_complete(XCF_COMMAND_FILE, CT_COMPLIST);
1704
6
	return KSTD;
1705
}
1706
static int
1707
x_expand(int c)
1708
{
1709
	char **words;
1710
	int nwords = 0;
1711
	int start, end;
1712
	int is_command;
1713
	int i;
1714
1715
	nwords = x_cf_glob(XCF_FILE, xbuf, xep - xbuf, xcp - xbuf,
1716
	    &start, &end, &words, &is_command);
1717
1718
	if (nwords == 0) {
1719
		x_e_putc(BEL);
1720
		return KSTD;
1721
	}
1722
1723
	x_goto(xbuf + start);
1724
	x_delete(end - start, false);
1725
	for (i = 0; i < nwords;) {
1726
		if (x_escape(words[i], strlen(words[i]), x_do_ins) < 0 ||
1727
		    (++i < nwords && x_ins(" ") < 0)) {
1728
			x_e_putc(BEL);
1729
			return KSTD;
1730
		}
1731
	}
1732
	x_adjust();
1733
1734
	return KSTD;
1735
}
1736
1737
/* type == 0 for list, 1 for complete and 2 for complete-list */
1738
static void
1739
do_complete(int flags,	/* XCF_{COMMAND,FILE,COMMAND_FILE} */
1740
    Comp_type type)
1741
{
1742
12
	char **words;
1743
	int nwords;
1744
6
	int start, end, nlen, olen;
1745
6
	int is_command;
1746
	int completed = 0;
1747
1748
6
	nwords = x_cf_glob(flags, xbuf, xep - xbuf, xcp - xbuf,
1749
	    &start, &end, &words, &is_command);
1750
	/* no match */
1751
6
	if (nwords == 0) {
1752
		x_e_putc(BEL);
1753
		return;
1754
	}
1755
1756
6
	if (type == CT_LIST) {
1757
		x_print_expansions(nwords, words, is_command);
1758
		x_redraw(0);
1759
		x_free_words(nwords, words);
1760
		return;
1761
	}
1762
1763
6
	olen = end - start;
1764
6
	nlen = x_longest_prefix(nwords, words);
1765
	/* complete */
1766

7
	if (nwords == 1 || nlen > olen) {
1767
6
		x_goto(xbuf + start);
1768
6
		x_delete(olen, false);
1769
6
		x_escape(words[0], nlen, x_do_ins);
1770
6
		x_adjust();
1771
		completed = 1;
1772
6
	}
1773
	/* add space if single non-dir match */
1774

11
	if (nwords == 1 && words[0][nlen - 1] != '/') {
1775
3
		x_ins(" ");
1776
		completed = 1;
1777
3
	}
1778
1779
6
	if (type == CT_COMPLIST && !completed) {
1780
		x_print_expansions(nwords, words, is_command);
1781
		completed = 1;
1782
	}
1783
1784
6
	if (completed)
1785
6
		x_redraw(0);
1786
1787
6
	x_free_words(nwords, words);
1788
12
}
1789
1790
/* NAME:
1791
 *      x_adjust - redraw the line adjusting starting point etc.
1792
 *
1793
 * DESCRIPTION:
1794
 *      This function is called when we have exceeded the bounds
1795
 *      of the edit window.  It increments x_adj_done so that
1796
 *      functions like x_ins and x_delete know that we have been
1797
 *      called and can skip the x_bs() stuff which has already
1798
 *      been done by x_redraw.
1799
 *
1800
 * RETURN VALUE:
1801
 *      None
1802
 */
1803
1804
static void
1805
x_adjust(void)
1806
{
1807
12
	x_adj_done++;			/* flag the fact that we were called. */
1808
	/*
1809
	 * we had a problem if the prompt length > xx_cols / 2
1810
	 */
1811
6
	if ((xbp = xcp - (x_displen / 2)) < xbuf)
1812
6
		xbp = xbuf;
1813
6
	xlp_valid = false;
1814
6
	x_redraw(xx_cols);
1815
6
	x_flush();
1816
6
}
1817
1818
static int unget_char = -1;
1819
1820
static void
1821
x_e_ungetc(int c)
1822
{
1823
252
	unget_char = c;
1824
126
}
1825
1826
static int
1827
x_e_getc(void)
1828
{
1829
	int c;
1830
1831
3264
	if (unget_char >= 0) {
1832
		c = unget_char;
1833
126
		unget_char = -1;
1834
1632
	} else if (macro_args) {
1835
		c = *macro_args++;
1836
		if (!c) {
1837
			macro_args = NULL;
1838
			c = x_getc();
1839
		}
1840
	} else
1841
1506
		c = x_getc();
1842
1843
1380
	return c;
1844
}
1845
1846
static int
1847
x_e_getu8(char *buf, int off)
1848
{
1849
	int	c, cc, len;
1850
1851
2400
	c = x_e_getc();
1852
1074
	if (c == -1)
1853
		return -1;
1854
1074
	buf[off++] = c;
1855
1856
1074
	if (c == 0xf4)
1857
42
		len = 4;
1858
1032
	else if ((c & 0xf0) == 0xe0)
1859
120
		len = 3;
1860
912
	else if ((c & 0xe0) == 0xc0 && c > 0xc1)
1861
24
		len = 2;
1862
	else
1863
		len = 1;
1864
1865
2508
	for (; len > 1; len--) {
1866
306
		cc = x_e_getc();
1867
306
		if (cc == -1)
1868
			break;
1869

342
		if (isu8cont(cc) == 0 ||
1870
228
		    (c == 0xe0 && len == 3 && cc < 0xa0) ||
1871

240
		    (c == 0xed && len == 3 && cc & 0x20) ||
1872
228
		    (c == 0xf4 && len == 4 && cc & 0x30)) {
1873
126
			x_e_ungetc(cc);
1874
126
			break;
1875
		}
1876
180
		buf[off++] = cc;
1877
	}
1878
1074
	buf[off] = '\0';
1879
1880
1074
	return off;
1881
1074
}
1882
1883
static void
1884
x_e_putc(int c)
1885
{
1886
8254
	if (c == '\r' || c == '\n')
1887
31
		x_col = 0;
1888
4127
	if (x_col < xx_cols) {
1889
8216
		x_putc(c);
1890

8216
		switch (c) {
1891
		case BEL:
1892
			break;
1893
		case '\r':
1894
		case '\n':
1895
			break;
1896
		case '\b':
1897
1605
			x_col--;
1898
1605
			break;
1899
		default:
1900
2484
			if (!isu8cont(c))
1901
2076
				x_col++;
1902
			break;
1903
		}
1904
	}
1905

5507
	if (x_adj_ok && (x_col < 0 || x_col >= (xx_cols - 2)))
1906
		x_adjust();
1907
4127
}
1908
1909
#ifdef DEBUG
1910
static int
1911
x_debug_info(int c)
1912
{
1913
	x_flush();
1914
	shellf("\nksh debug:\n");
1915
	shellf("\tx_col == %d,\t\tx_cols == %d,\tx_displen == %d\n",
1916
	    x_col, xx_cols, x_displen);
1917
	shellf("\txcp == 0x%lx,\txep == 0x%lx\n", (long) xcp, (long) xep);
1918
	shellf("\txbp == 0x%lx,\txbuf == 0x%lx\n", (long) xbp, (long) xbuf);
1919
	shellf("\txlp == 0x%lx\n", (long) xlp);
1920
	shellf("\txlp == 0x%lx\n", (long) x_lastcp());
1921
	shellf("\n");
1922
	x_redraw(-1);
1923
	return 0;
1924
}
1925
#endif
1926
1927
static void
1928
x_e_puts(const char *s)
1929
{
1930
	int	adj = x_adj_done;
1931
1932
	while (*s && adj == x_adj_done)
1933
		x_e_putc(*s++);
1934
}
1935
1936
/* NAME:
1937
 *      x_set_arg - set an arg value for next function
1938
 *
1939
 * DESCRIPTION:
1940
 *      This is a simple implementation of M-[0-9].
1941
 *
1942
 * RETURN VALUE:
1943
 *      KSTD
1944
 */
1945
1946
static int
1947
x_set_arg(int c)
1948
{
1949
	int n = 0;
1950
	int first = 1;
1951
1952
	for (; c >= 0 && isdigit(c); c = x_e_getc(), first = 0)
1953
		n = n * 10 + (c - '0');
1954
	if (c < 0 || first) {
1955
		x_e_putc(BEL);
1956
		x_arg = 1;
1957
		x_arg_defaulted = 1;
1958
	} else {
1959
		x_e_ungetc(c);
1960
		x_arg = n;
1961
		x_arg_defaulted = 0;
1962
		x_arg_set = 1;
1963
	}
1964
	return KSTD;
1965
}
1966
1967
1968
/* Comment or uncomment the current line. */
1969
static int
1970
x_comment(int c)
1971
{
1972
	int oldsize = x_size_str(xbuf);
1973
	int len = xep - xbuf;
1974
	int ret = x_do_comment(xbuf, xend - xbuf, &len);
1975
1976
	if (ret < 0)
1977
		x_e_putc(BEL);
1978
	else {
1979
		xep = xbuf + len;
1980
		*xep = '\0';
1981
		xcp = xbp = xbuf;
1982
		x_redraw(oldsize);
1983
		if (ret > 0)
1984
			return x_newline('\n');
1985
	}
1986
	return KSTD;
1987
}
1988
1989
1990
/* NAME:
1991
 *      x_prev_histword - recover word from prev command
1992
 *
1993
 * DESCRIPTION:
1994
 *      This function recovers the last word from the previous
1995
 *      command and inserts it into the current edit line.  If a
1996
 *      numeric arg is supplied then the n'th word from the
1997
 *      start of the previous command is used.
1998
 *
1999
 *      Bound to M-.
2000
 *
2001
 * RETURN VALUE:
2002
 *      KSTD
2003
 */
2004
2005
static int
2006
x_prev_histword(int c)
2007
{
2008
	char *rcp;
2009
	char *cp;
2010
2011
	cp = *histptr;
2012
	if (!cp)
2013
		x_e_putc(BEL);
2014
	else if (x_arg_defaulted) {
2015
		rcp = &cp[strlen(cp) - 1];
2016
		/*
2017
		 * ignore white-space after the last word
2018
		 */
2019
		while (rcp > cp && is_cfs(*rcp))
2020
			rcp--;
2021
		while (rcp > cp && !is_cfs(*rcp))
2022
			rcp--;
2023
		if (is_cfs(*rcp))
2024
			rcp++;
2025
		x_ins(rcp);
2026
	} else {
2027
		int c;
2028
2029
		rcp = cp;
2030
		/*
2031
		 * ignore white-space at start of line
2032
		 */
2033
		while (*rcp && is_cfs(*rcp))
2034
			rcp++;
2035
		while (x_arg-- > 1) {
2036
			while (*rcp && !is_cfs(*rcp))
2037
				rcp++;
2038
			while (*rcp && is_cfs(*rcp))
2039
				rcp++;
2040
		}
2041
		cp = rcp;
2042
		while (*rcp && !is_cfs(*rcp))
2043
			rcp++;
2044
		c = *rcp;
2045
		*rcp = '\0';
2046
		x_ins(cp);
2047
		*rcp = c;
2048
	}
2049
	return KSTD;
2050
}
2051
2052
/* Uppercase N(1) words */
2053
static int
2054
x_fold_upper(int c)
2055
{
2056
	return x_fold_case('U');
2057
}
2058
2059
/* Lowercase N(1) words */
2060
static int
2061
x_fold_lower(int c)
2062
{
2063
	return x_fold_case('L');
2064
}
2065
2066
/* Lowercase N(1) words */
2067
static int
2068
x_fold_capitalize(int c)
2069
{
2070
	return x_fold_case('C');
2071
}
2072
2073
/* NAME:
2074
 *      x_fold_case - convert word to UPPER/lower/Capital case
2075
 *
2076
 * DESCRIPTION:
2077
 *      This function is used to implement M-U,M-u,M-L,M-l,M-C and M-c
2078
 *      to UPPER case, lower case or Capitalize words.
2079
 *
2080
 * RETURN VALUE:
2081
 *      None
2082
 */
2083
2084
static int
2085
x_fold_case(int c)
2086
{
2087
	char *cp = xcp;
2088
2089
	if (cp == xep) {
2090
		x_e_putc(BEL);
2091
		return KSTD;
2092
	}
2093
	while (x_arg--) {
2094
		/*
2095
		 * first skip over any white-space
2096
		 */
2097
		while (cp != xep && is_mfs(*cp))
2098
			cp++;
2099
		/*
2100
		 * do the first char on its own since it may be
2101
		 * a different action than for the rest.
2102
		 */
2103
		if (cp != xep) {
2104
			if (c == 'L') {		/* lowercase */
2105
				if (isupper((unsigned char)*cp))
2106
					*cp = tolower((unsigned char)*cp);
2107
			} else {		/* uppercase, capitalize */
2108
				if (islower((unsigned char)*cp))
2109
					*cp = toupper((unsigned char)*cp);
2110
			}
2111
			cp++;
2112
		}
2113
		/*
2114
		 * now for the rest of the word
2115
		 */
2116
		while (cp != xep && !is_mfs(*cp)) {
2117
			if (c == 'U') {		/* uppercase */
2118
				if (islower((unsigned char)*cp))
2119
					*cp = toupper((unsigned char)*cp);
2120
			} else {		/* lowercase, capitalize */
2121
				if (isupper((unsigned char)*cp))
2122
					*cp = tolower((unsigned char)*cp);
2123
			}
2124
			cp++;
2125
		}
2126
	}
2127
	x_goto(cp);
2128
	return KSTD;
2129
}
2130
2131
/* NAME:
2132
 *      x_lastcp - last visible byte
2133
 *
2134
 * SYNOPSIS:
2135
 *      x_lastcp()
2136
 *
2137
 * DESCRIPTION:
2138
 *      This function returns a pointer to that byte in the
2139
 *      edit buffer that will be the last displayed on the
2140
 *      screen.  The sequence:
2141
 *
2142
 *      for (cp = x_lastcp(); cp > xcp; cp)
2143
 *        x_bs(*--cp);
2144
 *
2145
 *      Will position the cursor correctly on the screen.
2146
 *
2147
 * RETURN VALUE:
2148
 *      cp or NULL
2149
 */
2150
2151
static char *
2152
x_lastcp(void)
2153
{
2154
	char *rcp;
2155
	int i;
2156
2157
2874
	if (!xlp_valid) {
2158

9286
		for (i = 0, rcp = xbp; rcp < xep && i < x_displen; rcp++)
2159
1777
			i += x_size((unsigned char)*rcp);
2160
726
		xlp = rcp;
2161
726
	}
2162
1437
	xlp_valid = true;
2163
1437
	return (xlp);
2164
}
2165
2166
#endif /* EDIT */