GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/rdist/expand.c Lines: 0 290 0.0 %
Date: 2017-11-07 Branches: 0 234 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: expand.c,v 1.15 2015/01/20 09:00:16 guenther Exp $	*/
2
3
/*
4
 * Copyright (c) 1983 Regents of the University of California.
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 * 1. Redistributions of source code must retain the above copyright
11
 *    notice, this list of conditions and the following disclaimer.
12
 * 2. Redistributions in binary form must reproduce the above copyright
13
 *    notice, this list of conditions and the following disclaimer in the
14
 *    documentation and/or other materials provided with the distribution.
15
 * 3. Neither the name of the University nor the names of its contributors
16
 *    may be used to endorse or promote products derived from this software
17
 *    without specific prior written permission.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29
 * SUCH DAMAGE.
30
 */
31
32
#include <dirent.h>
33
#include <errno.h>
34
#include <fcntl.h>
35
#include <stdlib.h>
36
#include <string.h>
37
#include <unistd.h>
38
39
#include "client.h"
40
41
#define	MAXEARGS	2048
42
#define LC 		'{'
43
#define RC 		'}'
44
45
static char	shchars[] = "${[*?";
46
47
int		which;		/* bit mask of types to expand */
48
int		eargc;		/* expanded arg count */
49
char	      **eargv;		/* expanded arg vectors */
50
char	       *path;
51
char	       *pathp;
52
char	       *lastpathp;
53
char	       *tilde;		/* "~user" if not expanding tilde, else "" */
54
char	       *tpathp;
55
56
int		expany;		/* any expansions done? */
57
char	       *entp;
58
char	      **sortbase;
59
char 	       *argvbuf[MAXEARGS];
60
61
#define sort()	qsort((char *)sortbase, &eargv[eargc] - sortbase, \
62
		      sizeof(*sortbase), argcmp), sortbase = &eargv[eargc]
63
64
static void Cat(u_char *, u_char *);
65
static void addpath(int);
66
static int argcmp(const void *, const void *);
67
68
static void
69
Cat(u_char *s1, u_char *s2)		/* quote in s1 and s2 */
70
{
71
	char *cp;
72
	int len = strlen((char *)s1) + strlen((char *)s2) + 2;
73
74
	if ((eargc + 1) >= MAXEARGS) {
75
		yyerror("Too many names");
76
		return;
77
	}
78
79
	eargv[++eargc] = NULL;
80
	eargv[eargc - 1] = cp = xmalloc(len);
81
82
	do {
83
		if (*s1 == QUOTECHAR)
84
			s1++;
85
	} while ((*cp++ = *s1++) != '\0');
86
	cp--;
87
	do {
88
		if (*s2 == QUOTECHAR)
89
			s2++;
90
	} while ((*cp++ = *s2++) != '\0');
91
}
92
93
static void
94
addpath(int c)
95
{
96
	if (pathp >= lastpathp) {
97
		yyerror("Pathname too long");
98
		return;
99
	} else {
100
		*pathp++ = c;
101
		*pathp = CNULL;
102
	}
103
}
104
105
/*
106
 * Take a list of names and expand any macros, etc.
107
 * wh = E_VARS if expanding variables.
108
 * wh = E_SHELL if expanding shell characters.
109
 * wh = E_TILDE if expanding `~'.
110
 * or any of these or'ed together.
111
 *
112
 * Major portions of this were snarfed from csh/sh.glob.c.
113
 */
114
struct namelist *
115
expand(struct namelist *list, int wh)		/* quote in list->n_name */
116
{
117
	struct namelist *nl, *prev;
118
	int n;
119
	char pathbuf[BUFSIZ];
120
121
	if (debug)
122
		debugmsg(DM_CALL, "expand(%p, %d) start, list = %s",
123
			 list, wh, getnlstr(list));
124
125
	if (wh == 0)
126
		fatalerr("expand() contains invalid 'wh' argument.");
127
128
	which = wh;
129
	path = tpathp = pathp = pathbuf;
130
	*pathp = CNULL;
131
	lastpathp = &pathbuf[sizeof pathbuf - 2];
132
	tilde = "";
133
	eargc = 0;
134
	eargv = sortbase = argvbuf;
135
	*eargv = NULL;
136
137
	/*
138
	 * Walk the name list and expand names into eargv[];
139
	 */
140
	for (nl = list; nl != NULL; nl = nl->n_next)
141
		expstr((u_char *)nl->n_name);
142
	/*
143
	 * Take expanded list of names from eargv[] and build a new list.
144
	 */
145
	list = prev = NULL;
146
	for (n = 0; n < eargc; n++) {
147
		nl = makenl(NULL);
148
		nl->n_name = eargv[n];
149
		if (prev == NULL)
150
			list = prev = nl;
151
		else {
152
			prev->n_next = nl;
153
			prev = nl;
154
		}
155
	}
156
157
	return(list);
158
}
159
160
/*
161
 * xstrchr() is a version of strchr() that
162
 * handles u_char buffers.
163
 */
164
u_char *
165
xstrchr(u_char *str, int ch)
166
{
167
	u_char *cp;
168
169
	for (cp = str; cp && *cp != CNULL; ++cp)
170
		if (ch == *cp)
171
			return(cp);
172
173
	return(NULL);
174
}
175
176
void
177
expstr(u_char *s)
178
{
179
	u_char *cp, *cp1;
180
	struct namelist *tp;
181
	u_char *tail;
182
	u_char ebuf[BUFSIZ];
183
	u_char varbuff[BUFSIZ];
184
	int savec, oeargc;
185
186
	if (s == NULL || *s == CNULL)
187
		return;
188
189
	/*
190
	 * Remove quoted characters
191
	 */
192
	if (IS_ON(which, E_VARS)) {
193
		if (strlen((char *)s) > sizeof(varbuff)) {
194
			yyerror("Variable is too large.");
195
			return;
196
		}
197
		for (cp = s, cp1 = varbuff; cp && *cp; ++cp) {
198
			/*
199
			 * remove quoted character if the next
200
			 * character is not $
201
			 */
202
			if (*cp == QUOTECHAR && *(cp+1) != '$')
203
				++cp;
204
			else
205
				*cp1++ = *cp;
206
		}
207
		*cp1 = CNULL;
208
		s = varbuff;
209
	}
210
211
	/*
212
	 * Consider string 's' a variable that should be expanded if
213
	 * there is a '$' in 's' that is not quoted.
214
	 */
215
	if (IS_ON(which, E_VARS) &&
216
	    ((cp = xstrchr(s, '$')) && !(cp > s && *(cp-1) == QUOTECHAR))) {
217
		*cp++ = CNULL;
218
		if (*cp == CNULL) {
219
			yyerror("no variable name after '$'");
220
			return;
221
		}
222
		if (*cp == LC) {
223
			cp++;
224
			for (cp1 = cp; ; cp1 = tail + 1) {
225
				if ((tail = xstrchr(cp1, RC)) == NULL) {
226
					yyerror("unmatched '{'");
227
					return;
228
				}
229
				if (tail[-1] != QUOTECHAR) break;
230
			}
231
			*tail++ = savec = CNULL;
232
			if (*cp == CNULL) {
233
				yyerror("no variable name after '$'");
234
				return;
235
			}
236
		} else {
237
			tail = cp + 1;
238
			savec = *tail;
239
			*tail = CNULL;
240
		}
241
		tp = lookup((char *)cp, LOOKUP, NULL);
242
		if (savec != CNULL)
243
			*tail = savec;
244
		if (tp != NULL) {
245
			for (; tp != NULL; tp = tp->n_next) {
246
				(void) snprintf((char *)ebuf, sizeof(ebuf),
247
					        "%s%s%s", s, tp->n_name, tail);
248
				expstr(ebuf);
249
			}
250
			return;
251
		}
252
		(void) snprintf((char *)ebuf, sizeof(ebuf), "%s%s", s, tail);
253
		expstr(ebuf);
254
		return;
255
	}
256
	if ((which & ~E_VARS) == 0 || !strcmp((char *)s, "{") ||
257
	    !strcmp((char *)s, "{}")) {
258
		Cat(s, (u_char *)"");
259
		sort();
260
		return;
261
	}
262
	if (*s == '~') {
263
		cp = ++s;
264
		if (*cp == CNULL || *cp == '/') {
265
			tilde = "~";
266
			cp1 = (u_char *)homedir;
267
		} else {
268
			tilde = (char *)(cp1 = ebuf);
269
			*cp1++ = '~';
270
			do
271
				*cp1++ = *cp++;
272
			while (*cp && *cp != '/');
273
			*cp1 = CNULL;
274
			if (pw == NULL || strcmp(pw->pw_name,
275
						 (char *)ebuf+1) != 0) {
276
				if ((pw = getpwnam((char *)ebuf+1)) == NULL) {
277
					strlcat((char *)ebuf,
278
					        ": unknown user name",
279
					        sizeof(ebuf));
280
					yyerror((char *)ebuf+1);
281
					return;
282
				}
283
			}
284
			cp1 = (u_char *)pw->pw_dir;
285
			s = cp;
286
		}
287
		for (cp = (u_char *)path; (*cp++ = *cp1++) != '\0'; )
288
			continue;
289
		tpathp = pathp = (char *)cp - 1;
290
	} else {
291
		tpathp = pathp = path;
292
		tilde = "";
293
	}
294
	*pathp = CNULL;
295
	if (!(which & E_SHELL)) {
296
		if (which & E_TILDE)
297
			Cat((u_char *)path, s);
298
		else
299
			Cat((u_char *)tilde, s);
300
		sort();
301
		return;
302
	}
303
	oeargc = eargc;
304
	expany = 0;
305
	expsh(s);
306
	if (eargc == oeargc)
307
		Cat(s, (u_char *)"");		/* "nonomatch" is set */
308
	sort();
309
}
310
311
static int
312
argcmp(const void *v1, const void *v2)
313
{
314
	const char *const *a1 = v1, *const *a2 = v2;
315
316
	return (strcmp(*a1, *a2));
317
}
318
319
/*
320
 * If there are any Shell meta characters in the name,
321
 * expand into a list, after searching directory
322
 */
323
void
324
expsh(u_char *s)			/* quote in s */
325
{
326
	u_char *cp, *oldcp;
327
	char *spathp;
328
	struct stat stb;
329
330
	spathp = pathp;
331
	cp = s;
332
	while (!any(*cp, shchars)) {
333
		if (*cp == CNULL) {
334
			if (!expany || stat(path, &stb) >= 0) {
335
				if (which & E_TILDE)
336
					Cat((u_char *)path, (u_char *)"");
337
				else
338
					Cat((u_char *)tilde, (u_char *)tpathp);
339
			}
340
			goto endit;
341
		}
342
		if (*cp == QUOTECHAR) cp++;
343
		addpath(*cp++);
344
	}
345
	oldcp = cp;
346
	while (cp > s && *cp != '/')
347
		cp--, pathp--;
348
	if (*cp == '/')
349
		cp++, pathp++;
350
	*pathp = CNULL;
351
	if (*oldcp == '{') {
352
		(void) execbrc(cp, NULL);
353
		return;
354
	}
355
	matchdir((char *)cp);
356
endit:
357
	pathp = spathp;
358
	*pathp = CNULL;
359
}
360
361
void
362
matchdir(char *pattern)			/* quote in pattern */
363
{
364
	struct stat stb;
365
	struct dirent *dp;
366
	DIR *dirp;
367
368
	dirp = opendir(path);
369
	if (dirp == NULL) {
370
		if (expany)
371
			return;
372
		goto patherr2;
373
	}
374
	if (fstat(dirfd(dirp), &stb) < 0)
375
		goto patherr1;
376
	if (!S_ISDIR(stb.st_mode)) {
377
		errno = ENOTDIR;
378
		goto patherr1;
379
	}
380
	while ((dp = readdir(dirp)) != NULL)
381
		if (match(dp->d_name, pattern)) {
382
			if (which & E_TILDE)
383
				Cat((u_char *)path, (u_char *)dp->d_name);
384
			else {
385
				(void) strlcpy(pathp, dp->d_name,
386
				    lastpathp - pathp + 2);
387
				Cat((u_char *)tilde, (u_char *)tpathp);
388
				*pathp = CNULL;
389
			}
390
		}
391
	closedir(dirp);
392
	return;
393
394
patherr1:
395
	closedir(dirp);
396
patherr2:
397
	(void) strlcat(path, ": ", lastpathp - path + 2);
398
	(void) strlcat(path, SYSERR, lastpathp - path + 2);
399
	yyerror(path);
400
}
401
402
int
403
execbrc(u_char *p, u_char *s)		/* quote in p */
404
{
405
	u_char restbuf[BUFSIZ + 2];
406
	u_char *pe, *pm, *pl;
407
	int brclev = 0;
408
	u_char *lm, savec;
409
	char *spathp;
410
411
	for (lm = restbuf; *p != '{'; *lm++ = *p++)
412
		if (*p == QUOTECHAR) *lm++ = *p++;
413
414
	for (pe = ++p; *pe; pe++)
415
		switch (*pe) {
416
417
		case '{':
418
			brclev++;
419
			continue;
420
421
		case '}':
422
			if (brclev == 0)
423
				goto pend;
424
			brclev--;
425
			continue;
426
427
		case '[':
428
			for (pe++; *pe && *pe != ']'; pe++)
429
				if (*p == QUOTECHAR) pe++;
430
			if (!*pe)
431
				yyerror("Missing ']'");
432
			continue;
433
434
		case QUOTECHAR:		/* skip this character */
435
			pe++;
436
			continue;
437
		}
438
pend:
439
	if (brclev || !*pe) {
440
		yyerror("Missing '}'");
441
		return (0);
442
	}
443
	for (pl = pm = p; pm <= pe; pm++)
444
		/* the strip code was a noop */
445
		switch (*pm) {
446
447
		case '{':
448
			brclev++;
449
			continue;
450
451
		case '}':
452
			if (brclev) {
453
				brclev--;
454
				continue;
455
			}
456
			goto doit;
457
458
		case ',':
459
			if (brclev)
460
				continue;
461
doit:
462
			savec = *pm;
463
			*pm = 0;
464
			*lm = 0;
465
			(void) strlcat((char *)restbuf, (char *)pl,
466
			    sizeof(restbuf));
467
			(void) strlcat((char *)restbuf, (char *)pe + 1,
468
			    sizeof(restbuf));
469
			*pm = savec;
470
			if (s == 0) {
471
				spathp = pathp;
472
				expsh(restbuf);
473
				pathp = spathp;
474
				*pathp = 0;
475
			} else if (amatch((char *)s, restbuf))
476
				return (1);
477
			sort();
478
			pl = pm + 1;
479
			continue;
480
481
		case '[':
482
			for (pm++; *pm && *pm != ']'; pm++)
483
				if (*pm == QUOTECHAR) pm++;
484
			if (!*pm)
485
				yyerror("Missing ']'");
486
			continue;
487
488
		case QUOTECHAR:			/* skip one character */
489
			pm++;
490
			continue;
491
		}
492
	return (0);
493
}
494
495
int
496
match(char *s, char *p)				/* quote in p */
497
{
498
	int c;
499
	char *sentp;
500
	char sexpany = expany;
501
502
	if (*s == '.' && *p != '.')
503
		return (0);
504
	sentp = entp;
505
	entp = s;
506
	c = amatch(s, p);
507
	entp = sentp;
508
	expany = sexpany;
509
	return (c);
510
}
511
512
int
513
amatch(char *s, u_char *p)			/* quote in p */
514
{
515
	int scc;
516
	int ok, lc;
517
	char *spathp;
518
	struct stat stb;
519
	int c, cc;
520
521
	expany = 1;
522
	for (;;) {
523
		scc = *s++;
524
		switch (c = *p++) {
525
526
		case '{':
527
			return (execbrc((u_char *)p - 1, (u_char *)s - 1));
528
529
		case '[':
530
			ok = 0;
531
			lc = 077777;
532
			while ((cc = *p++) != '\0') {
533
				if (cc == ']') {
534
					if (ok)
535
						break;
536
					return (0);
537
				}
538
				if (cc == QUOTECHAR) cc = *p++;
539
				if (cc == '-') {
540
					if (lc <= scc && scc <= (int)*p++)
541
						ok++;
542
				} else
543
					if (scc == (lc = cc))
544
						ok++;
545
			}
546
			if (cc == 0) {
547
				yyerror("Missing ']'");
548
				return (0);
549
			}
550
			continue;
551
552
		case '*':
553
			if (!*p)
554
				return (1);
555
			if (*p == '/') {
556
				p++;
557
				goto slash;
558
			}
559
			for (s--; *s; s++)
560
				if (amatch(s, p))
561
					return (1);
562
			return (0);
563
564
		case CNULL:
565
			return (scc == CNULL);
566
567
		default:
568
			if (c != scc)
569
				return (0);
570
			continue;
571
572
		case '?':
573
			if (scc == CNULL)
574
				return (0);
575
			continue;
576
577
		case '/':
578
			if (scc)
579
				return (0);
580
slash:
581
			s = entp;
582
			spathp = pathp;
583
			while (*s)
584
				addpath(*s++);
585
			addpath('/');
586
			if (stat(path, &stb) == 0 && S_ISDIR(stb.st_mode)) {
587
				if (*p == CNULL) {
588
					if (which & E_TILDE) {
589
						Cat((u_char *)path,
590
						    (u_char *)"");
591
					} else {
592
						Cat((u_char *)tilde,
593
						    (u_char *)tpathp);
594
					}
595
				} else
596
					expsh(p);
597
			}
598
			pathp = spathp;
599
			*pathp = CNULL;
600
			return (0);
601
		}
602
	}
603
}