Home | History | Annotate | Download | only in rdist
      1 /*
      2  * Copyright (c) 1983 Regents of the University of California.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms are permitted
      6  * provided that the above copyright notice and this paragraph are
      7  * duplicated in all such forms and that any documentation,
      8  * advertising materials, and other materials related to such
      9  * distribution and use acknowledge that the software was developed
     10  * by the University of California, Berkeley.  The name of the
     11  * University may not be used to endorse or promote products derived
     12  * from this software without specific prior written permission.
     13  *
     14  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
     15  * Use is subject to license terms.
     16  */
     17 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     18 
     19 #include "defs.h"
     20 #include <string.h>
     21 
     22 #define	GAVSIZ	NCARGS / 6
     23 #define	LC '{'
     24 #define	RC '}'
     25 
     26 static char	shchars[] = "${[*?";
     27 
     28 int	which;		/* bit mask of types to expand */
     29 int	eargc;		/* expanded arg count */
     30 char	**eargv;	/* expanded arg vectors */
     31 char	*path;
     32 char	*pathp;
     33 char	*lastpathp;
     34 char	*tilde;		/* "~user" if not expanding tilde, else "" */
     35 char	*tpathp;
     36 int	nleft;
     37 
     38 int	expany;		/* any expansions done? */
     39 char	*entp;
     40 char	**sortbase;
     41 
     42 char	*index();
     43 
     44 static int argcmp(const void *arg1, const void *arg2);
     45 static void addpath(char c);
     46 static void Cat(char *s1, char *s2);
     47 static void matchdir(char *pattern);
     48 static void expsh(char *s);
     49 static void expstr(char *s);
     50 static int execbrc(char *p, char *s);
     51 
     52 #define	sort()	qsort((char *)sortbase, &eargv[eargc] - sortbase, \
     53 		sizeof (*sortbase), argcmp), sortbase = &eargv[eargc]
     54 
     55 #define	MIN(a, b)	((a) < (b) ? (a) : (b))
     56 
     57 /*
     58  * Take a list of names and expand any macros, etc.
     59  * wh = E_VARS if expanding variables.
     60  * wh = E_SHELL if expanding shell characters.
     61  * wh = E_TILDE if expanding `~'.
     62  * or any of these or'ed together.
     63  *
     64  * Major portions of this were snarfed from csh/sh.glob.c.
     65  */
     66 struct namelist *
     67 expand(list, wh)
     68 	struct namelist *list;
     69 	int wh;
     70 {
     71 	register struct namelist *nl, *prev;
     72 	register int n;
     73 	char pathbuf[LINESIZE];
     74 	char *argvbuf[GAVSIZ];
     75 
     76 	if (debug) {
     77 		printf("expand(%x, %d)\nlist = ", list, wh);
     78 		prnames(list);
     79 	}
     80 
     81 	if (wh == 0) {
     82 		register char *cp;
     83 
     84 		for (nl = list; nl != NULL; nl = nl->n_next)
     85 			for (cp = nl->n_name; *cp; cp++)
     86 				*cp = *cp & TRIM;
     87 		return (list);
     88 	}
     89 
     90 	which = wh;
     91 	path = tpathp = pathp = pathbuf;
     92 	*pathp = '\0';
     93 	lastpathp = &path[sizeof pathbuf - 2];
     94 	tilde = "";
     95 	eargc = 0;
     96 	eargv = sortbase = argvbuf;
     97 	*eargv = 0;
     98 	nleft = NCARGS - 4;
     99 	/*
    100 	 * Walk the name list and expand names into eargv[];
    101 	 */
    102 	for (nl = list; nl != NULL; nl = nl->n_next)
    103 		expstr(nl->n_name);
    104 	/*
    105 	 * Take expanded list of names from eargv[] and build a new list.
    106 	 */
    107 	list = prev = NULL;
    108 	for (n = 0; n < eargc; n++) {
    109 		nl = makenl(NULL);
    110 		nl->n_name = eargv[n];
    111 		if (prev == NULL)
    112 			list = prev = nl;
    113 		else {
    114 			prev->n_next = nl;
    115 			prev = nl;
    116 		}
    117 	}
    118 	if (debug) {
    119 		printf("expanded list = ");
    120 		prnames(list);
    121 	}
    122 	return (list);
    123 }
    124 
    125 static void
    126 expstr(s)
    127 	char *s;
    128 {
    129 	register char *cp, *cp1;
    130 	register struct namelist *tp;
    131 	char *tail;
    132 	char buf[LINESIZE];
    133 	int savec, oeargc;
    134 	extern char homedir[];
    135 
    136 	if (s == NULL || *s == '\0')
    137 		return;
    138 
    139 	if ((which & E_VARS) && (cp = index(s, '$')) != NULL) {
    140 		*cp++ = '\0';
    141 		if (*cp == '\0') {
    142 			yyerror("no variable name after '$'");
    143 			return;
    144 		}
    145 		if (*cp == LC) {
    146 			cp++;
    147 			if ((tail = index(cp, RC)) == NULL) {
    148 				yyerror("unmatched '{'");
    149 				return;
    150 			}
    151 			*tail++ = savec = '\0';
    152 			if (*cp == '\0') {
    153 				yyerror("no variable name after '$'");
    154 				return;
    155 			}
    156 		} else {
    157 			tail = cp + 1;
    158 			savec = *tail;
    159 			*tail = '\0';
    160 		}
    161 		tp = lookup(cp, NULL, 0);
    162 		if (savec != '\0')
    163 			*tail = savec;
    164 		if (tp != NULL) {
    165 			for (; tp != NULL; tp = tp->n_next) {
    166 				(void) snprintf(buf, sizeof (buf), "%s%s%s", s,
    167 				    tp->n_name, tail);
    168 				expstr(buf);
    169 			}
    170 			return;
    171 		}
    172 		(void) snprintf(buf, sizeof (buf), "%s%s", s, tail);
    173 		expstr(buf);
    174 		return;
    175 	}
    176 	if ((which & ~E_VARS) == 0 || !strcmp(s, "{") || !strcmp(s, "{}")) {
    177 		Cat(s, "");
    178 		sort();
    179 		return;
    180 	}
    181 	if (*s == '~') {
    182 		cp = ++s;
    183 		if (*cp == '\0' || *cp == '/') {
    184 			tilde = "~";
    185 			cp1 = homedir;
    186 		} else {
    187 			tilde = cp1 = buf;
    188 			*cp1++ = '~';
    189 			do {
    190 				if (cp1 >= &buf[sizeof (buf)]) {
    191 					yyerror("User name too long");
    192 					return;
    193 				}
    194 				*cp1++ = *cp++;
    195 			} while (*cp && *cp != '/');
    196 			*cp1 = '\0';
    197 			if (pw == NULL || strcmp(pw->pw_name, buf+1) != 0) {
    198 				if ((pw = getpwnam(buf+1)) == NULL) {
    199 					static char unknown_user[] =
    200 					    ": unknown user name";
    201 
    202 					cp1 = MIN(cp1,
    203 					    &buf[sizeof (buf)] -
    204 					    sizeof (unknown_user));
    205 					strcpy(cp1, unknown_user);
    206 					yyerror(buf+1);
    207 					return;
    208 				}
    209 			}
    210 			cp1 = pw->pw_dir;
    211 			s = cp;
    212 		}
    213 		for (cp = path; cp <= lastpathp + 1 && (*cp++ = *cp1++); )
    214 			;
    215 		tpathp = pathp = cp - 1;
    216 	} else {
    217 		tpathp = pathp = path;
    218 		tilde = "";
    219 	}
    220 	*pathp = '\0';
    221 	if (!(which & E_SHELL)) {
    222 		if (which & E_TILDE)
    223 			Cat(path, s);
    224 		else
    225 			Cat(tilde, s);
    226 		sort();
    227 		return;
    228 	}
    229 	oeargc = eargc;
    230 	expany = 0;
    231 	expsh(s);
    232 	if (eargc == oeargc)
    233 		Cat(s, "");		/* "nonomatch" is set */
    234 	sort();
    235 }
    236 
    237 static int
    238 argcmp(const void *arg1, const void *arg2)
    239 {
    240 	char *a1 = *(char **)arg1;
    241 	char *a2 = *(char **)arg2;
    242 
    243 	return (strcmp(a1, a2));
    244 }
    245 
    246 /*
    247  * If there are any Shell meta characters in the name,
    248  * expand into a list, after searching directory
    249  */
    250 static void
    251 expsh(s)
    252 	char *s;
    253 {
    254 	register char *cp;
    255 	register char *spathp, *oldcp;
    256 	struct stat stb;
    257 
    258 	spathp = pathp;
    259 	cp = s;
    260 	while (!any(*cp, shchars)) {
    261 		if (*cp == '\0') {
    262 			if (!expany || stat(path, &stb) >= 0) {
    263 				if (which & E_TILDE)
    264 					Cat(path, "");
    265 				else
    266 					Cat(tilde, tpathp);
    267 			}
    268 			goto endit;
    269 		}
    270 		addpath(*cp++);
    271 	}
    272 	oldcp = cp;
    273 	while (cp > s && *cp != '/')
    274 		cp--, pathp--;
    275 	if (*cp == '/')
    276 		cp++, pathp++;
    277 	*pathp = '\0';
    278 	if (*oldcp == '{') {
    279 		execbrc(cp, NULL);
    280 		return;
    281 	}
    282 	matchdir(cp);
    283 endit:
    284 	pathp = spathp;
    285 	*pathp = '\0';
    286 }
    287 
    288 static void
    289 matchdir(pattern)
    290 	char *pattern;
    291 {
    292 	struct stat stb;
    293 	register struct dirent *dp;
    294 	DIR *dirp;
    295 
    296 	dirp = opendir(path);
    297 	if (dirp == NULL) {
    298 		if (expany)
    299 			return;
    300 		goto patherr2;
    301 	}
    302 	if (fstat(dirp->dd_fd, &stb) < 0)
    303 		goto patherr1;
    304 	if (!ISDIR(stb.st_mode)) {
    305 		errno = ENOTDIR;
    306 		goto patherr1;
    307 	}
    308 	while ((dp = readdir(dirp)) != NULL)
    309 		if (match(dp->d_name, pattern)) {
    310 			if (which & E_TILDE)
    311 				Cat(path, dp->d_name);
    312 			else {
    313 				if (pathp + strlen(dp->d_name) - 1 >
    314 				    lastpathp) {
    315 					errno = ENAMETOOLONG;
    316 					goto patherr1;
    317 				}
    318 				strcpy(pathp, dp->d_name);
    319 				Cat(tilde, tpathp);
    320 				*pathp = '\0';
    321 			}
    322 		}
    323 	closedir(dirp);
    324 	return;
    325 
    326 patherr1:
    327 	closedir(dirp);
    328 patherr2:
    329 	{
    330 		char *strerr = strerror(errno);
    331 
    332 		if (path + strlen(path) + strlen(strerr) + 1 > lastpathp)
    333 			strcpy(lastpathp - strlen(strerr) - 1, ": ");
    334 		else
    335 			strcat(path, ": ");
    336 		strcat(path, strerr);
    337 	}
    338 	yyerror(path);
    339 }
    340 
    341 static int
    342 execbrc(p, s)
    343 	char *p, *s;
    344 {
    345 	char restbuf[LINESIZE + 2];
    346 	register char *pe, *pm, *pl;
    347 	int brclev = 0;
    348 	char *lm, savec, *spathp;
    349 
    350 	for (lm = restbuf; *p != '{'; *lm++ = *p++) {
    351 		if (lm >= &restbuf[sizeof (restbuf)]) {
    352 			yyerror("Pathname too long");
    353 			return (0);
    354 		}
    355 	}
    356 	for (pe = ++p; *pe; pe++)
    357 		switch (*pe) {
    358 
    359 		case '{':
    360 			brclev++;
    361 			continue;
    362 
    363 		case '}':
    364 			if (brclev == 0)
    365 				goto pend;
    366 			brclev--;
    367 			continue;
    368 
    369 		case '[':
    370 			for (pe++; *pe && *pe != ']'; pe++)
    371 				continue;
    372 			if (!*pe)
    373 				yyerror("Missing ']'");
    374 			continue;
    375 		}
    376 pend:
    377 	if (brclev || !*pe) {
    378 		yyerror("Missing '}'");
    379 		return (0);
    380 	}
    381 	for (pl = pm = p; pm <= pe; pm++)
    382 		switch (*pm & (QUOTE|TRIM)) {
    383 
    384 		case '{':
    385 			brclev++;
    386 			continue;
    387 
    388 		case '}':
    389 			if (brclev) {
    390 				brclev--;
    391 				continue;
    392 			}
    393 			goto doit;
    394 
    395 		case ',':
    396 			if (brclev)
    397 				continue;
    398 doit:
    399 			savec = *pm;
    400 			*pm = 0;
    401 			if (lm + strlen(pl) + strlen(pe + 1) >=
    402 			    &restbuf[sizeof (restbuf)]) {
    403 				yyerror("Pathname too long");
    404 				return (0);
    405 			}
    406 			strcpy(lm, pl);
    407 			strcat(restbuf, pe + 1);
    408 			*pm = savec;
    409 			if (s == 0) {
    410 				spathp = pathp;
    411 				expsh(restbuf);
    412 				pathp = spathp;
    413 				*pathp = 0;
    414 			} else if (amatch(s, restbuf))
    415 				return (1);
    416 			sort();
    417 			pl = pm + 1;
    418 			continue;
    419 
    420 		case '[':
    421 			for (pm++; *pm && *pm != ']'; pm++)
    422 				continue;
    423 			if (!*pm)
    424 				yyerror("Missing ']'");
    425 			continue;
    426 		}
    427 	return (0);
    428 }
    429 
    430 int
    431 match(s, p)
    432 	char *s, *p;
    433 {
    434 	register int c;
    435 	register char *sentp;
    436 	char sexpany = expany;
    437 
    438 	if (*s == '.' && *p != '.')
    439 		return (0);
    440 	sentp = entp;
    441 	entp = s;
    442 	c = amatch(s, p);
    443 	entp = sentp;
    444 	expany = sexpany;
    445 	return (c);
    446 }
    447 
    448 int
    449 amatch(s, p)
    450 	register char *s, *p;
    451 {
    452 	register int scc;
    453 	int ok, lc;
    454 	char *spathp;
    455 	struct stat stb;
    456 	int c, cc;
    457 
    458 	expany = 1;
    459 	for (;;) {
    460 		scc = *s++ & TRIM;
    461 		switch (c = *p++) {
    462 
    463 		case '{':
    464 			return (execbrc(p - 1, s - 1));
    465 
    466 		case '[':
    467 			ok = 0;
    468 			lc = 077777;
    469 			while (cc = *p++) {
    470 				if (cc == ']') {
    471 					if (ok)
    472 						break;
    473 					return (0);
    474 				}
    475 				if (cc == '-') {
    476 					if (lc <= scc && scc <= *p++)
    477 						ok++;
    478 				} else
    479 					if (scc == (lc = cc))
    480 						ok++;
    481 			}
    482 			if (cc == 0) {
    483 				yyerror("Missing ']'");
    484 				return (0);
    485 			}
    486 			continue;
    487 
    488 		case '*':
    489 			if (!*p)
    490 				return (1);
    491 			if (*p == '/') {
    492 				p++;
    493 				goto slash;
    494 			}
    495 			for (s--; *s; s++)
    496 				if (amatch(s, p))
    497 					return (1);
    498 			return (0);
    499 
    500 		case '\0':
    501 			return (scc == '\0');
    502 
    503 		default:
    504 			if ((c & TRIM) != scc)
    505 				return (0);
    506 			continue;
    507 
    508 		case '?':
    509 			if (scc == '\0')
    510 				return (0);
    511 			continue;
    512 
    513 		case '/':
    514 			if (scc)
    515 				return (0);
    516 slash:
    517 			s = entp;
    518 			spathp = pathp;
    519 			while (*s)
    520 				addpath(*s++);
    521 			addpath('/');
    522 			if (stat(path, &stb) == 0 && ISDIR(stb.st_mode))
    523 				if (*p == '\0') {
    524 					if (which & E_TILDE)
    525 						Cat(path, "");
    526 					else
    527 						Cat(tilde, tpathp);
    528 				} else
    529 					expsh(p);
    530 			pathp = spathp;
    531 			*pathp = '\0';
    532 			return (0);
    533 		}
    534 	}
    535 }
    536 
    537 int
    538 smatch(s, p)
    539 	register char *s, *p;
    540 {
    541 	register int scc;
    542 	int ok, lc;
    543 	int c, cc;
    544 
    545 	for (;;) {
    546 		scc = *s++ & TRIM;
    547 		switch (c = *p++) {
    548 
    549 		case '[':
    550 			ok = 0;
    551 			lc = 077777;
    552 			while (cc = *p++) {
    553 				if (cc == ']') {
    554 					if (ok)
    555 						break;
    556 					return (0);
    557 				}
    558 				if (cc == '-') {
    559 					if (lc <= scc && scc <= *p++)
    560 						ok++;
    561 				} else
    562 					if (scc == (lc = cc))
    563 						ok++;
    564 			}
    565 			if (cc == 0) {
    566 				yyerror("Missing ']'");
    567 				return (0);
    568 			}
    569 			continue;
    570 
    571 		case '*':
    572 			if (!*p)
    573 				return (1);
    574 			for (s--; *s; s++)
    575 				if (smatch(s, p))
    576 					return (1);
    577 			return (0);
    578 
    579 		case '\0':
    580 			return (scc == '\0');
    581 
    582 		default:
    583 			if ((c & TRIM) != scc)
    584 				return (0);
    585 			continue;
    586 
    587 		case '?':
    588 			if (scc == 0)
    589 				return (0);
    590 			continue;
    591 
    592 		}
    593 	}
    594 }
    595 
    596 static void
    597 Cat(s1, s2)
    598 	register char *s1, *s2;
    599 {
    600 	int len = strlen(s1) + strlen(s2) + 1;
    601 	register char *s;
    602 
    603 	nleft -= len;
    604 	if (nleft <= 0 || ++eargc >= GAVSIZ)
    605 		fatal("Arguments too long\n");
    606 	eargv[eargc] = 0;
    607 	eargv[eargc - 1] = s = (char *)malloc(len);
    608 	if (s == NULL)
    609 		fatal("ran out of memory\n");
    610 	while (*s++ = *s1++ & TRIM)
    611 		;
    612 	s--;
    613 	while (*s++ = *s2++ & TRIM)
    614 		;
    615 }
    616 
    617 static void
    618 addpath(char c)
    619 {
    620 
    621 	if (pathp > lastpathp)
    622 		yyerror("Pathname too long");
    623 	else {
    624 		*pathp++ = c & TRIM;
    625 		*pathp = '\0';
    626 	}
    627 }
    628 
    629 /*
    630  * Expand file names beginning with `~' into the
    631  * user's home directory path name. Return a pointer in buf to the
    632  * part corresponding to `file'.
    633  */
    634 char *
    635 exptilde(buf, len, file)
    636 	char buf[];
    637 	unsigned int len;
    638 	register char *file;
    639 {
    640 	register char *s1, *s2, *s3;
    641 	extern char homedir[];
    642 
    643 	if (*file != '~') {
    644 		if (strlen(file) + 1 > len) {
    645 			error("pathname too long: %s\n", file);
    646 			return (NULL);
    647 		}
    648 		strcpy(buf, file);
    649 		return (buf);
    650 	}
    651 	if (*++file == '\0') {
    652 		s2 = homedir;
    653 		s3 = NULL;
    654 	} else if (*file == '/') {
    655 		s2 = homedir;
    656 		s3 = file;
    657 	} else {
    658 		s3 = file;
    659 		while (*s3 && *s3 != '/')
    660 			s3++;
    661 		if (*s3 == '/')
    662 			*s3 = '\0';
    663 		else
    664 			s3 = NULL;
    665 		if (pw == NULL || strcmp(pw->pw_name, file) != 0) {
    666 			if ((pw = getpwnam(file)) == NULL) {
    667 				error("%s: unknown user name\n", file);
    668 				if (s3 != NULL)
    669 					*s3 = '/';
    670 				return (NULL);
    671 			}
    672 		}
    673 		if (s3 != NULL)
    674 			*s3 = '/';
    675 		s2 = pw->pw_dir;
    676 	}
    677 	for (s1 = buf; s1 < &buf[len] && (*s1++ = *s2++); )
    678 		;
    679 	s2 = --s1;
    680 	if (s3 != NULL) {
    681 		s2++;
    682 		while (s1 < &buf[len] && (*s1++ = *s3++))
    683 			;
    684 	}
    685 	if (s1 == &buf[len]) {
    686 		error("pathname too long: %s\n", file - 1);
    687 		return (NULL);
    688 	}
    689 	return (s2);
    690 }
    691