GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: bin/ksh/edit.c Lines: 266 400 66.5 %
Date: 2017-11-13 Branches: 160 356 44.9 %

Line Branch Exec Source
1
/*	$OpenBSD: edit.c,v 1.57 2016/09/08 12:12:40 nicm Exp $	*/
2
3
/*
4
 * Command line editing - common code
5
 *
6
 */
7
8
#include "config.h"
9
#ifdef EDIT
10
11
#include <sys/ioctl.h>
12
#include <sys/stat.h>
13
14
#include <ctype.h>
15
#include <errno.h>
16
#include <libgen.h>
17
#include <stdlib.h>
18
#include <stdio.h>
19
#include <string.h>
20
#include <unistd.h>
21
22
#include "sh.h"
23
#include "edit.h"
24
#include "tty.h"
25
26
X_chars edchars;
27
28
static void x_sigwinch(int);
29
volatile sig_atomic_t got_sigwinch;
30
static void check_sigwinch(void);
31
32
static int	x_file_glob(int, const char *, int, char ***);
33
static int	x_command_glob(int, const char *, int, char ***);
34
static int	x_locate_word(const char *, int, int, int *, int *);
35
36
37
/* Called from main */
38
void
39
x_init(void)
40
{
41
	/* set to -2 to force initial binding */
42
880
	edchars.erase = edchars.kill = edchars.intr = edchars.quit =
43
1760
	    edchars.eof = -2;
44
	/* default value for deficient systems */
45
880
	edchars.werase = 027;	/* ^W */
46
47
880
	if (setsig(&sigtraps[SIGWINCH], x_sigwinch, SS_RESTORE_ORIG|SS_SHTRAP))
48
880
		sigtraps[SIGWINCH].flags |= TF_SHELL_USES;
49
880
	got_sigwinch = 1; /* force initial check */
50
880
	check_sigwinch();
51
52
#ifdef EMACS
53
880
	x_init_emacs();
54
#endif /* EMACS */
55
880
}
56
57
static void
58
x_sigwinch(int sig)
59
{
60
	got_sigwinch = 1;
61
}
62
63
static void
64
check_sigwinch(void)
65
{
66
1834
	if (got_sigwinch) {
67
917
		struct winsize ws;
68
69
917
		got_sigwinch = 0;
70

1834
		if (procpid == kshpid && ioctl(tty_fd, TIOCGWINSZ, &ws) >= 0) {
71
			struct tbl *vp;
72
73
			/* Do NOT export COLUMNS/LINES.  Many applications
74
			 * check COLUMNS/LINES before checking ws.ws_col/row,
75
			 * so if the app is started with C/L in the environ
76
			 * and the window is then resized, the app won't
77
			 * see the change cause the environ doesn't change.
78
			 */
79
917
			if (ws.ws_col) {
80
917
				x_cols = ws.ws_col < MIN_COLS ? MIN_COLS :
81
				    ws.ws_col;
82
83
917
				if ((vp = typeset("COLUMNS", 0, 0, 0, 0)))
84
917
					setint(vp, (long) ws.ws_col);
85
			}
86

1834
			if (ws.ws_row && (vp = typeset("LINES", 0, 0, 0, 0)))
87
917
				setint(vp, (long) ws.ws_row);
88
917
		}
89
917
	}
90
917
}
91
92
/*
93
 * read an edited command line
94
 */
95
int
96
x_read(char *buf, size_t len)
97
{
98
	int	i;
99
100
1384
	x_mode(true);
101
#ifdef EMACS
102

1118
	if (Flag(FEMACS) || Flag(FGMACS))
103
266
		i = x_emacs(buf, len);
104
	else
105
#endif
106
#ifdef VI
107
426
	if (Flag(FVI))
108
426
		i = x_vi(buf, len);
109
	else
110
#endif
111
		i = -1;		/* internal error */
112
37
	x_mode(false);
113
37
	check_sigwinch();
114
37
	return i;
115
}
116
117
/* tty I/O */
118
119
int
120
x_getc(void)
121
{
122
11436
	char c;
123
	int n;
124
125

12090
	while ((n = blocking_read(STDIN_FILENO, &c, 1)) < 0 && errno == EINTR)
126
654
		if (trap) {
127
654
			x_mode(false);
128
654
			runtraps(0);
129
654
			x_mode(true);
130
654
		}
131
5064
	if (n != 1)
132
		return -1;
133
5064
	return (int) (unsigned char) c;
134
5064
}
135
136
void
137
x_flush(void)
138
{
139
11212
	shf_flush(shl_out);
140
5606
}
141
142
void
143
x_putc(int c)
144
{
145
30765
	shf_putc(c, shl_out);
146
10255
}
147
148
void
149
x_puts(const char *s)
150
{
151
	while (*s != 0)
152
		shf_putc(*s++, shl_out);
153
}
154
155
bool
156
x_mode(bool onoff)
157
{
158
	static bool	x_cur_mode;
159
	bool		prev;
160
161
1384
	if (x_cur_mode == onoff)
162
		return x_cur_mode;
163
1384
	prev = x_cur_mode;
164
1384
	x_cur_mode = onoff;
165
166
1384
	if (onoff) {
167
692
		struct termios	cb;
168
692
		X_chars		oldchars;
169
170
692
		oldchars = edchars;
171
692
		cb = tty_state;
172
173
692
		edchars.erase = cb.c_cc[VERASE];
174
692
		edchars.kill = cb.c_cc[VKILL];
175
692
		edchars.intr = cb.c_cc[VINTR];
176
692
		edchars.quit = cb.c_cc[VQUIT];
177
692
		edchars.eof = cb.c_cc[VEOF];
178
692
		edchars.werase = cb.c_cc[VWERASE];
179
692
		cb.c_iflag &= ~(INLCR|ICRNL);
180
692
		cb.c_lflag &= ~(ISIG|ICANON|ECHO);
181
		/* osf/1 processes lnext when ~icanon */
182
692
		cb.c_cc[VLNEXT] = _POSIX_VDISABLE;
183
		/* sunos 4.1.x & osf/1 processes discard(flush) when ~icanon */
184
692
		cb.c_cc[VDISCARD] = _POSIX_VDISABLE;
185
692
		cb.c_cc[VTIME] = 0;
186
692
		cb.c_cc[VMIN] = 1;
187
188
692
		tcsetattr(tty_fd, TCSADRAIN, &cb);
189
190
		/* Convert unset values to internal `unset' value */
191
692
		if (edchars.erase == _POSIX_VDISABLE)
192
			edchars.erase = -1;
193
692
		if (edchars.kill == _POSIX_VDISABLE)
194
			edchars.kill = -1;
195
692
		if (edchars.intr == _POSIX_VDISABLE)
196
			edchars.intr = -1;
197
692
		if (edchars.quit == _POSIX_VDISABLE)
198
			edchars.quit = -1;
199
692
		if (edchars.eof == _POSIX_VDISABLE)
200
			edchars.eof = -1;
201
692
		if (edchars.werase == _POSIX_VDISABLE)
202
			edchars.werase = -1;
203
692
		if (memcmp(&edchars, &oldchars, sizeof(edchars)) != 0) {
204
#ifdef EMACS
205
658
			x_emacs_keys(&edchars);
206
#endif
207
658
		}
208
692
	} else {
209
692
		tcsetattr(tty_fd, TCSADRAIN, &tty_state);
210
	}
211
212
1384
	return prev;
213
1384
}
214
215
void
216
set_editmode(const char *ed)
217
{
218
	static const enum sh_flag edit_flags[] = {
219
#ifdef EMACS
220
		FEMACS, FGMACS,
221
#endif
222
#ifdef VI
223
		FVI,
224
#endif
225
	};
226
	char *rcp;
227
	int i;
228
229
6636
	if ((rcp = strrchr(ed, '/')))
230
		ed = ++rcp;
231
19548
	for (i = 0; i < NELEM(edit_flags); i++)
232
8430
		if (strstr(ed, options[(int) edit_flags[i]].name)) {
233
1974
			change_flag(edit_flags[i], OF_SPECIAL, 1);
234
1974
			return;
235
		}
236
4662
}
237
238
/* ------------------------------------------------------------------------- */
239
/*           Misc common code for vi/emacs				     */
240
241
/* Handle the commenting/uncommenting of a line.
242
 * Returns:
243
 *	1 if a carriage return is indicated (comment added)
244
 *	0 if no return (comment removed)
245
 *	-1 if there is an error (not enough room for comment chars)
246
 * If successful, *lenp contains the new length.  Note: cursor should be
247
 * moved to the start of the line after (un)commenting.
248
 */
249
int
250
x_do_comment(char *buf, int bsize, int *lenp)
251
{
252
	int i, j;
253
	int len = *lenp;
254
255
	if (len == 0)
256
		return 1; /* somewhat arbitrary - it's what at&t ksh does */
257
258
	/* Already commented? */
259
	if (buf[0] == '#') {
260
		int saw_nl = 0;
261
262
		for (j = 0, i = 1; i < len; i++) {
263
			if (!saw_nl || buf[i] != '#')
264
				buf[j++] = buf[i];
265
			saw_nl = buf[i] == '\n';
266
		}
267
		*lenp = j;
268
		return 0;
269
	} else {
270
		int n = 1;
271
272
		/* See if there's room for the #'s - 1 per \n */
273
		for (i = 0; i < len; i++)
274
			if (buf[i] == '\n')
275
				n++;
276
		if (len + n >= bsize)
277
			return -1;
278
		/* Now add them... */
279
		for (i = len, j = len + n; --i >= 0; ) {
280
			if (buf[i] == '\n')
281
				buf[--j] = '#';
282
			buf[--j] = buf[i];
283
		}
284
		buf[0] = '#';
285
		*lenp += n;
286
		return 1;
287
	}
288
}
289
290
/* ------------------------------------------------------------------------- */
291
/*           Common file/command completion code for vi/emacs	             */
292
293
294
static char	*add_glob(const char *str, int slen);
295
static void	glob_table(const char *pat, XPtrV *wp, struct table *tp);
296
static void	glob_path(int flags, const char *pat, XPtrV *wp,
297
				const char *path);
298
299
void
300
x_print_expansions(int nwords, char *const *words, int is_command)
301
{
302
	int prefix_len;
303
304
	/* Check if all matches are in the same directory (in this
305
	 * case, we want to omit the directory name)
306
	 */
307
	if (!is_command &&
308
	    (prefix_len = x_longest_prefix(nwords, words)) > 0) {
309
		int i;
310
311
		/* Special case for 1 match (prefix is whole word) */
312
		if (nwords == 1)
313
			prefix_len = x_basename(words[0], NULL);
314
		/* Any (non-trailing) slashes in non-common word suffixes? */
315
		for (i = 0; i < nwords; i++)
316
			if (x_basename(words[i] + prefix_len, NULL) >
317
			    prefix_len)
318
				break;
319
		/* All in same directory? */
320
		if (i == nwords) {
321
			XPtrV l;
322
323
			while (prefix_len > 0 && words[0][prefix_len - 1] != '/')
324
				prefix_len--;
325
			XPinit(l, nwords + 1);
326
			for (i = 0; i < nwords; i++)
327
				XPput(l, words[i] + prefix_len);
328
			XPput(l, NULL);
329
330
			/* Enumerate expansions */
331
			x_putc('\r');
332
			x_putc('\n');
333
			pr_list((char **) XPptrv(l));
334
335
			XPfree(l); /* not x_free_words() */
336
			return;
337
		}
338
	}
339
340
	/* Enumerate expansions */
341
	x_putc('\r');
342
	x_putc('\n');
343
	pr_list(words);
344
}
345
346
/*
347
 *  Do file globbing:
348
 *	- appends * to (copy of) str if no globbing chars found
349
 *	- does expansion, checks for no match, etc.
350
 *	- sets *wordsp to array of matching strings
351
 *	- returns number of matching strings
352
 */
353
static int
354
x_file_glob(int flags, const char *str, int slen, char ***wordsp)
355
{
356
	char *toglob;
357
	char **words;
358
	int nwords;
359
6
	XPtrV w;
360
	struct source *s, *sold;
361
362
3
	if (slen < 0)
363
		return 0;
364
365
3
	toglob = add_glob(str, slen);
366
367
	/*
368
	 * Convert "foo*" (toglob) to an array of strings (words)
369
	 */
370
3
	sold = source;
371
3
	s = pushs(SWSTR, ATEMP);
372
3
	s->start = s->str = toglob;
373
3
	source = s;
374
3
	if (yylex(ONEWORD|UNESCAPE) != LWORD) {
375
		source = sold;
376
		internal_errorf(0, "fileglob: substitute error");
377
		return 0;
378
	}
379
	source = sold;
380
3
	XPinit(w, 32);
381
3
	expand(yylval.cp, &w, DOGLOB|DOTILDE|DOMARKDIRS);
382
6
	XPput(w, NULL);
383
3
	words = (char **) XPclose(w);
384
385
14
	for (nwords = 0; words[nwords]; nwords++)
386
		;
387
3
	if (nwords == 1) {
388
2
		struct stat statb;
389
390
		/* Check if file exists, also, check for empty
391
		 * result - happens if we tried to glob something
392
		 * which evaluated to an empty string (e.g.,
393
		 * "$FOO" when there is no FOO, etc).
394
		 */
395

4
		 if ((lstat(words[0], &statb) < 0) ||
396
2
		    words[0][0] == '\0') {
397
			x_free_words(nwords, words);
398
			words = NULL;
399
			nwords = 0;
400
		}
401
2
	}
402
3
	afree(toglob, ATEMP);
403
404
3
	if (nwords) {
405
3
		*wordsp = words;
406
3
	} else if (words) {
407
		x_free_words(nwords, words);
408
		*wordsp = NULL;
409
	}
410
411
3
	return nwords;
412
3
}
413
414
/* Data structure used in x_command_glob() */
415
struct path_order_info {
416
	char *word;
417
	int base;
418
	int path_order;
419
};
420
421
static int path_order_cmp(const void *aa, const void *bb);
422
423
/* Compare routine used in x_command_glob() */
424
static int
425
path_order_cmp(const void *aa, const void *bb)
426
{
427
	const struct path_order_info *a = (const struct path_order_info *) aa;
428
	const struct path_order_info *b = (const struct path_order_info *) bb;
429
	int t;
430
431
	t = strcmp(a->word + a->base, b->word + b->base);
432
	return t ? t : a->path_order - b->path_order;
433
}
434
435
static int
436
x_command_glob(int flags, const char *str, int slen, char ***wordsp)
437
{
438
	char *toglob;
439
	char *pat;
440
	char *fpath;
441
	int nwords;
442
6
	XPtrV w;
443
	struct block *l;
444
445
3
	if (slen < 0)
446
		return 0;
447
448
3
	toglob = add_glob(str, slen);
449
450
	/* Convert "foo*" (toglob) to a pattern for future use */
451
3
	pat = evalstr(toglob, DOPAT|DOTILDE);
452
3
	afree(toglob, ATEMP);
453
454
3
	XPinit(w, 32);
455
456
3
	glob_table(pat, &w, &keywords);
457
3
	glob_table(pat, &w, &aliases);
458
3
	glob_table(pat, &w, &builtins);
459
12
	for (l = genv->loc; l; l = l->next)
460
3
		glob_table(pat, &w, &l->funs);
461
462
3
	glob_path(flags, pat, &w, path);
463
3
	if ((fpath = str_val(global("FPATH"))) != null)
464
		glob_path(flags, pat, &w, fpath);
465
466
3
	nwords = XPsize(w);
467
468
3
	if (!nwords) {
469
		*wordsp = NULL;
470
		XPfree(w);
471
		return 0;
472
	}
473
474
	/* Sort entries */
475
3
	if (flags & XCF_FULLPATH) {
476
		/* Sort by basename, then path order */
477
		struct path_order_info *info;
478
		struct path_order_info *last_info = NULL;
479
		char **words = (char **) XPptrv(w);
480
		int path_order = 0;
481
		int i;
482
483
		info = areallocarray(NULL, nwords,
484
		    sizeof(struct path_order_info), ATEMP);
485
486
		for (i = 0; i < nwords; i++) {
487
			info[i].word = words[i];
488
			info[i].base = x_basename(words[i], NULL);
489
			if (!last_info || info[i].base != last_info->base ||
490
			    strncmp(words[i], last_info->word, info[i].base) != 0) {
491
				last_info = &info[i];
492
				path_order++;
493
			}
494
			info[i].path_order = path_order;
495
		}
496
		qsort(info, nwords, sizeof(struct path_order_info),
497
			path_order_cmp);
498
		for (i = 0; i < nwords; i++)
499
			words[i] = info[i].word;
500
		afree(info, ATEMP);
501
	} else {
502
		/* Sort and remove duplicate entries */
503
		char **words = (char **) XPptrv(w);
504
		int i, j;
505
506
3
		qsortp(XPptrv(w), (size_t) nwords, xstrcmp);
507
508
6
		for (i = j = 0; i < nwords - 1; i++) {
509
			if (strcmp(words[i], words[i + 1]))
510
				words[j++] = words[i];
511
			else
512
				afree(words[i], ATEMP);
513
		}
514
3
		words[j++] = words[i];
515
		nwords = j;
516
3
		w.cur = (void **) &words[j];
517
	}
518
519
6
	XPput(w, NULL);
520
3
	*wordsp = (char **) XPclose(w);
521
522
3
	return nwords;
523
3
}
524
525
#define IS_WORDC(c)	!( ctype(c, C_LEX1) || (c) == '\'' || (c) == '"' || \
526
			    (c) == '`' || (c) == '=' || (c) == ':' )
527
528
static int
529
x_locate_word(const char *buf, int buflen, int pos, int *startp,
530
    int *is_commandp)
531
{
532
	int p;
533
	int start, end;
534
535
	/* Bad call?  Probably should report error */
536

18
	if (pos < 0 || pos > buflen) {
537
		*startp = pos;
538
		*is_commandp = 0;
539
		return 0;
540
	}
541
	/* The case where pos == buflen happens to take care of itself... */
542
543
	start = pos;
544
	/* Keep going backwards to start of word (has effect of allowing
545
	 * one blank after the end of a word)
546
	 */
547




210
	for (; (start > 0 && IS_WORDC(buf[start - 1])) ||
548
33
	    (start > 1 && buf[start-2] == '\\'); start--)
549
		;
550
	/* Go forwards to end of word */
551



228
	for (end = start; end < buflen && IS_WORDC(buf[end]); end++) {
552

24
		if (buf[end] == '\\' && (end+1) < buflen)
553
			end++;
554
	}
555
556
6
	if (is_commandp) {
557
		int iscmd;
558
559
		/* Figure out if this is a command */
560

24
		for (p = start - 1; p >= 0 && isspace((unsigned char)buf[p]);
561
3
		    p--)
562
			;
563
15
		iscmd = p < 0 || strchr(";|&()`", buf[p]);
564
6
		if (iscmd) {
565
			/* If command has a /, path, etc. is not searched;
566
			 * only current directory is searched, which is just
567
			 * like file globbing.
568
			 */
569
30
			for (p = start; p < end; p++)
570
12
				if (buf[p] == '/')
571
					break;
572
3
			iscmd = p == end;
573
3
		}
574
6
		*is_commandp = iscmd;
575
6
	}
576
577
6
	*startp = start;
578
579
6
	return end - start;
580
6
}
581
582
static int
583
x_try_array(const char *buf, int buflen, const char *want, int wantlen,
584
    int *nwords, char ***words)
585
{
586
	const char *cmd, *cp;
587
	int cmdlen, n, i, slen;
588
6
	char *name, *s;
589
	struct tbl *v, *vp;
590
591
3
	*nwords = 0;
592
3
	*words = NULL;
593
594
	/* Walk back to find start of command. */
595
3
	if (want == buf)
596
		return 0;
597
32
	for (cmd = want; cmd > buf; cmd--) {
598
13
		if (strchr(";|&()`", cmd[-1]) != NULL)
599
			break;
600
	}
601

9
	while (cmd < want && isspace((u_char)*cmd))
602
		cmd++;
603
	cmdlen = 0;
604

27
	while (cmd + cmdlen < want && !isspace((u_char)cmd[cmdlen]))
605
6
		cmdlen++;
606
18
	for (i = 0; i < cmdlen; i++) {
607

6
		if (!isalnum((u_char)cmd[i]) && cmd[i] != '_')
608
			return 0;
609
	}
610
611
	/* Take a stab at argument count from here. */
612
	n = 1;
613
14
	for (cp = cmd + cmdlen + 1; cp < want; cp++) {
614

7
		if (!isspace((u_char)cp[-1]) && isspace((u_char)*cp))
615
1
			n++;
616
	}
617
618
	/* Try to find the array. */
619
3
	if (asprintf(&name, "complete_%.*s_%d", cmdlen, cmd, n) < 0)
620
		internal_errorf(1, "unable to allocate memory");
621
3
	v = global(name);
622
3
	free(name);
623
3
	if (~v->flag & (ISSET|ARRAY)) {
624
3
		if (asprintf(&name, "complete_%.*s", cmdlen, cmd) < 0)
625
			internal_errorf(1, "unable to allocate memory");
626
3
		v = global(name);
627
3
		free(name);
628
3
		if (~v->flag & (ISSET|ARRAY))
629
3
			return 0;
630
	}
631
632
	/* Walk the array and build words list. */
633
	for (vp = v; vp; vp = vp->u.array) {
634
		if (~vp->flag & ISSET)
635
			continue;
636
637
		s = str_val(vp);
638
		slen = strlen(s);
639
640
		if (slen < wantlen)
641
			continue;
642
		if (slen > wantlen)
643
			slen = wantlen;
644
		if (slen != 0 && strncmp(s, want, slen) != 0)
645
			continue;
646
647
		*words = areallocarray(*words, (*nwords) + 2, sizeof **words,
648
		    ATEMP);
649
		(*words)[(*nwords)++] = str_save(s, ATEMP);
650
	}
651
	if (*nwords != 0)
652
		(*words)[*nwords] = NULL;
653
654
	return *nwords != 0;
655
3
}
656
657
int
658
x_cf_glob(int flags, const char *buf, int buflen, int pos, int *startp,
659
    int *endp, char ***wordsp, int *is_commandp)
660
{
661
	int len;
662
12
	int nwords;
663
6
	char **words = NULL;
664
6
	int is_command;
665
666
6
	len = x_locate_word(buf, buflen, pos, startp, &is_command);
667
6
	if (!(flags & XCF_COMMAND))
668
		is_command = 0;
669
	/* Don't do command globing on zero length strings - it takes too
670
	 * long and isn't very useful.  File globs are more likely to be
671
	 * useful, so allow these.
672
	 */
673
6
	if (len == 0 && is_command)
674
		return 0;
675
676
6
	if (is_command)
677
3
		nwords = x_command_glob(flags, buf + *startp, len, &words);
678
3
	else if (!x_try_array(buf, buflen, buf + *startp, len, &nwords, &words))
679
3
		nwords = x_file_glob(flags, buf + *startp, len, &words);
680
6
	if (nwords == 0) {
681
		*wordsp = NULL;
682
		return 0;
683
	}
684
685
6
	if (is_commandp)
686
6
		*is_commandp = is_command;
687
6
	*wordsp = words;
688
6
	*endp = *startp + len;
689
690
6
	return nwords;
691
6
}
692
693
/* Given a string, copy it and possibly add a '*' to the end.  The
694
 * new string is returned.
695
 */
696
static char *
697
add_glob(const char *str, int slen)
698
{
699
	char *toglob;
700
	char *s;
701
	bool saw_slash = false;
702
703
12
	if (slen < 0)
704
		return NULL;
705
706
6
	toglob = str_nsave(str, slen + 1, ATEMP); /* + 1 for "*" */
707
6
	toglob[slen] = '\0';
708
709
	/*
710
	 * If the pathname contains a wildcard (an unquoted '*',
711
	 * '?', or '[') or parameter expansion ('$'), or a ~username
712
	 * with no trailing slash, then it is globbed based on that
713
	 * value (i.e., without the appended '*').
714
	 */
715
60
	for (s = toglob; *s; s++) {
716

24
		if (*s == '\\' && s[1])
717
			s++;
718


96
		else if (*s == '*' || *s == '[' || *s == '?' || *s == '$' ||
719
24
		    (s[1] == '(' /*)*/ && strchr("+@!", *s)))
720
			break;
721
24
		else if (*s == '/')
722
3
			saw_slash = true;
723
	}
724

12
	if (!*s && (*toglob != '~' || saw_slash)) {
725
6
		toglob[slen] = '*';
726
6
		toglob[slen + 1] = '\0';
727
6
	}
728
729
6
	return toglob;
730
6
}
731
732
/*
733
 * Find longest common prefix
734
 */
735
int
736
x_longest_prefix(int nwords, char *const *words)
737
{
738
	int i, j;
739
	int prefix_len;
740
	char *p;
741
742
12
	if (nwords <= 0)
743
		return 0;
744
745
6
	prefix_len = strlen(words[0]);
746
14
	for (i = 1; i < nwords; i++)
747
8
		for (j = 0, p = words[i]; j < prefix_len; j++)
748
4
			if (p[j] != words[0][j]) {
749
				prefix_len = j;
750
1
				break;
751
			}
752
6
	return prefix_len;
753
6
}
754
755
void
756
x_free_words(int nwords, char **words)
757
{
758
	int i;
759
760
32
	for (i = 0; i < nwords; i++)
761
7
		afree(words[i], ATEMP);
762
6
	afree(words, ATEMP);
763
6
}
764
765
/* Return the offset of the basename of string s (which ends at se - need not
766
 * be null terminated).  Trailing slashes are ignored.  If s is just a slash,
767
 * then the offset is 0 (actually, length - 1).
768
 *	s		Return
769
 *	/etc		1
770
 *	/etc/		1
771
 *	/etc//		1
772
 *	/etc/fo		5
773
 *	foo		0
774
 *	///		2
775
 *			0
776
 */
777
int
778
x_basename(const char *s, const char *se)
779
{
780
	const char *p;
781
782
	if (se == NULL)
783
		se = s + strlen(s);
784
	if (s == se)
785
		return 0;
786
787
	/* Skip trailing slashes */
788
	for (p = se - 1; p > s && *p == '/'; p--)
789
		;
790
	for (; p > s && *p != '/'; p--)
791
		;
792
	if (*p == '/' && p + 1 < se)
793
		p++;
794
795
	return p - s;
796
}
797
798
/*
799
 *  Apply pattern matching to a table: all table entries that match a pattern
800
 * are added to wp.
801
 */
802
static void
803
glob_table(const char *pat, XPtrV *wp, struct table *tp)
804
{
805
24
	struct tstate ts;
806
	struct tbl *te;
807
808
462
	for (ktwalk(&ts, tp); (te = ktnext(&ts)); ) {
809
219
		if (gmatch(te->name, pat, false))
810
			XPput(*wp, str_save(te->name, ATEMP));
811
	}
812
12
}
813
814
static void
815
glob_path(int flags, const char *pat, XPtrV *wp, const char *path)
816
{
817
	const char *sp, *p;
818
	char *xp;
819
6
	int staterr;
820
	int pathlen;
821
	int patlen;
822
	int oldsize, newsize, i, j;
823
	char **words;
824
3
	XString xs;
825
826
3
	patlen = strlen(pat) + 1;
827
	sp = path;
828
3
	Xinit(xs, xp, patlen + 128, ATEMP);
829
42
	while (sp) {
830
21
		xp = Xstring(xs, xp);
831
21
		if (!(p = strchr(sp, ':')))
832
3
			p = sp + strlen(sp);
833
21
		pathlen = p - sp;
834
21
		if (pathlen) {
835
			/* Copy sp into xp, stuffing any MAGIC characters
836
			 * on the way
837
			 */
838
			const char *s = sp;
839
840
21
			XcheckN(xs, xp, pathlen * 2);
841
456
			while (s < p) {
842
207
				if (ISMAGIC(*s))
843
					*xp++ = MAGIC;
844
207
				*xp++ = *s++;
845
			}
846
21
			*xp++ = '/';
847
21
			pathlen++;
848
21
		}
849
		sp = p;
850
21
		XcheckN(xs, xp, patlen);
851
21
		memcpy(xp, pat, patlen);
852
853
21
		oldsize = XPsize(*wp);
854
21
		glob_str(Xstring(xs, xp), wp, 1); /* mark dirs */
855
21
		newsize = XPsize(*wp);
856
857
		/* Check that each match is executable... */
858
		words = (char **) XPptrv(*wp);
859
48
		for (i = j = oldsize; i < newsize; i++) {
860
3
			staterr = 0;
861
6
			if ((search_access(words[i], X_OK, &staterr) >= 0) ||
862
3
			    (staterr == EISDIR)) {
863
3
				words[j] = words[i];
864
3
				if (!(flags & XCF_FULLPATH))
865
6
					memmove(words[j], words[j] + pathlen,
866
3
					    strlen(words[j] + pathlen) + 1);
867
3
				j++;
868
3
			} else
869
				afree(words[i], ATEMP);
870
		}
871
21
		wp->cur = (void **) &words[j];
872
873
21
		if (!*sp++)
874
			break;
875
	}
876
3
	Xfree(xs, xp);
877
3
}
878
879
/*
880
 * if argument string contains any special characters, they will
881
 * be escaped and the result will be put into edit buffer by
882
 * keybinding-specific function
883
 */
884
int
885
x_escape(const char *s, size_t len, int (*putbuf_func) (const char *, size_t))
886
{
887
	size_t add, wlen;
888
12
	const char *ifs = str_val(local("IFS", 0));
889
	int rval = 0;
890
891
82
	for (add = 0, wlen = len; wlen - add > 0; add++) {
892

70
		if (strchr("!\"#$&'()*:;<=>?[\\]`{|}", s[add]) ||
893
35
		    strchr(ifs, s[add])) {
894
			if (putbuf_func(s, add) != 0) {
895
				rval = -1;
896
				break;
897
			}
898
899
			putbuf_func("\\", 1);
900
			putbuf_func(&s[add], 1);
901
902
			add++;
903
			wlen -= add;
904
			s += add;
905
			add = -1; /* after the increment it will go to 0 */
906
		}
907
	}
908
6
	if (wlen > 0 && rval == 0)
909
6
		rval = putbuf_func(s, wlen);
910
911
6
	return (rval);
912
}
913
#endif /* EDIT */