Home | History | Annotate | Download | only in ftp
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  *	Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
     24  *	Use is subject to license terms.
     25  */
     26 
     27 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
     28 /*	All Rights Reserved  	*/
     29 
     30 /*
     31  *	University Copyright- Copyright (c) 1982, 1986, 1988
     32  *	The Regents of the University of California
     33  *	All Rights Reserved
     34  *
     35  *	University Acknowledgment- Portions of this document are derived from
     36  *	software developed by the University of California, Berkeley, and its
     37  *	contributors.
     38  */
     39 
     40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     41 
     42 /*
     43  * C-shell glob for random programs.
     44  */
     45 
     46 #include "ftp_var.h"
     47 
     48 #ifndef NCARGS
     49 #define	NCARGS	5120
     50 #endif
     51 
     52 #define	QUOTE 0200
     53 #define	TRIM 0177
     54 #define	eq(a, b)	(strcmp(a, b) == 0)
     55 
     56 /*
     57  * According to the person who wrote the C shell "glob" code, a reasonable
     58  * limit on number of arguments would seem to be the maximum number of
     59  * characters in an arg list / 6.
     60  *
     61  * XXX:	With the new VM system, NCARGS has become enormous, making
     62  *	it impractical to allocate arrays with NCARGS / 6 entries on
     63  *	the stack.  The proper fix is to revamp code elsewhere (in
     64  *	sh.dol.c and sh.glob.c) to use a different technique for handling
     65  *	command line arguments.  In the meantime, we simply fall back
     66  *	on using the old value of NCARGS.
     67  */
     68 #ifdef	notyet
     69 #define	GAVSIZ	(NCARGS / 6)
     70 #else	/* notyet */
     71 #define	GAVSIZ	(10240 / 6)
     72 #endif	/* notyet */
     73 
     74 static	char **gargv;		/* Pointer to the (stack) arglist */
     75 static	char **agargv;
     76 static	int agargv_size;
     77 static	long gargc;		/* Number args in gargv */
     78 static	short gflag;
     79 static char *strspl();
     80 static char *strend(char *cp);
     81 static char *strspl(char *cp, char *dp);
     82 static int tglob(char c);
     83 static char **copyblk(char **v);
     84 static void ginit(char **agargv);
     85 static void addpath(char c);
     86 static int any(int c, char *s);
     87 static void Gcat(char *s1, char *s2);
     88 static void collect(char *as);
     89 static void acollect(char *as);
     90 static void sort(void);
     91 static void expand(char *as);
     92 static void matchdir(char *pattern);
     93 static int execbrc(char *p, char *s);
     94 static int ftp_fnmatch(wchar_t t_ch, wchar_t t_fch, wchar_t t_lch);
     95 static int gethdir(char *home);
     96 static void xfree(char *cp);
     97 static void rscan(char **t, int (*f)(char));
     98 static int letter(char c);
     99 static int digit(char c);
    100 static int match(char *s, char *p);
    101 static int amatch(char *s, char *p);
    102 static int blklen(char **av);
    103 static char **blkcpy(char **oav, char **bv);
    104 
    105 static	int globcnt;
    106 
    107 static char	*globchars = "`{[*?";
    108 
    109 static	char *gpath, *gpathp, *lastgpathp;
    110 static	int globbed;
    111 static	char *entp;
    112 static	char **sortbas;
    113 
    114 char **
    115 glob(char *v)
    116 {
    117 	char agpath[FTPBUFSIZ];
    118 	char *vv[2];
    119 
    120 	if (agargv == NULL) {
    121 		agargv = (char **)malloc(GAVSIZ * sizeof (char *));
    122 		agargv_size = GAVSIZ;
    123 		if (agargv == NULL) {
    124 			globerr = "Arguments too long.";
    125 			return (0);
    126 		}
    127 	}
    128 	vv[0] = v;
    129 	vv[1] = 0;
    130 	globerr = 0;
    131 	gflag = 0;
    132 	rscan(vv, tglob);
    133 	if (gflag == 0)
    134 		return (copyblk(vv));
    135 
    136 	gpath = agpath;
    137 	gpathp = gpath;
    138 	*gpathp = 0;
    139 	lastgpathp = &gpath[sizeof (agpath) - 2];
    140 	ginit(agargv);
    141 	globcnt = 0;
    142 	collect(v);
    143 	if (globcnt == 0 && (gflag&1)) {
    144 		blkfree(gargv);
    145 		if (gargv == agargv)
    146 			agargv = 0;
    147 		gargv = 0;
    148 		return (0);
    149 	} else
    150 		return (gargv = copyblk(gargv));
    151 }
    152 
    153 static void
    154 ginit(char **agargv)
    155 {
    156 
    157 	agargv[0] = 0;
    158 	gargv = agargv;
    159 	sortbas = agargv;
    160 	gargc = 0;
    161 }
    162 
    163 static void
    164 collect(char *as)
    165 {
    166 	if (eq(as, "{") || eq(as, "{}")) {
    167 		Gcat(as, "");
    168 		sort();
    169 	} else
    170 		acollect(as);
    171 }
    172 
    173 static void
    174 acollect(char *as)
    175 {
    176 	register long ogargc = gargc;
    177 
    178 	gpathp = gpath; *gpathp = 0; globbed = 0;
    179 	expand(as);
    180 	if (gargc != ogargc)
    181 		sort();
    182 }
    183 
    184 static void
    185 sort(void)
    186 {
    187 	register char **p1, **p2, *c;
    188 	char **Gvp = &gargv[gargc];
    189 
    190 	p1 = sortbas;
    191 	while (p1 < Gvp-1) {
    192 		p2 = p1;
    193 		while (++p2 < Gvp)
    194 			if (strcmp(*p1, *p2) > 0)
    195 				c = *p1, *p1 = *p2, *p2 = c;
    196 		p1++;
    197 	}
    198 	sortbas = Gvp;
    199 }
    200 
    201 static void
    202 expand(char *as)
    203 {
    204 	register char *cs;
    205 	register char *sgpathp, *oldcs;
    206 	struct stat stb;
    207 
    208 	sgpathp = gpathp;
    209 	cs = as;
    210 	if (*cs == '~' && gpathp == gpath) {
    211 		addpath('~');
    212 		cs++;
    213 		while (letter(*cs) || digit(*cs) || *cs == '-')
    214 			addpath(*cs++);
    215 		if (!*cs || *cs == '/') {
    216 			if (gpathp != gpath + 1) {
    217 				*gpathp = 0;
    218 				if (gethdir(gpath + 1))
    219 					globerr = "Unknown user name after ~";
    220 				(void) strcpy(gpath, gpath + 1);
    221 			} else
    222 				(void) strcpy(gpath, home);
    223 			gpathp = strend(gpath);
    224 		}
    225 	}
    226 	while (!any(*cs, globchars)) {
    227 		if (*cs == 0) {
    228 			if (!globbed)
    229 				Gcat(gpath, "");
    230 			else if (stat(gpath, &stb) >= 0) {
    231 				Gcat(gpath, "");
    232 				globcnt++;
    233 			}
    234 			goto endit;
    235 		}
    236 		addpath(*cs++);
    237 	}
    238 	oldcs = cs;
    239 	while (cs > as && *cs != '/')
    240 		cs--, gpathp--;
    241 	if (*cs == '/')
    242 		cs++, gpathp++;
    243 	*gpathp = 0;
    244 	if (*oldcs == '{') {
    245 		(void) execbrc(cs, ((char *)0));
    246 		return;
    247 	}
    248 	matchdir(cs);
    249 endit:
    250 	gpathp = sgpathp;
    251 	*gpathp = 0;
    252 }
    253 
    254 static void
    255 matchdir(char *pattern)
    256 {
    257 	struct stat stb;
    258 	register struct dirent *dp;
    259 	DIR *dirp;
    260 
    261 	/*
    262 	 * BSD/SunOS open() system call maps a null pathname into
    263 	 * "." while System V does not.
    264 	 */
    265 	if (*gpath == (char)0) {
    266 		dirp = opendir(".");
    267 	} else
    268 		dirp = opendir(gpath);
    269 	if (dirp == NULL) {
    270 		if (globbed)
    271 			return;
    272 		goto patherr2;
    273 	}
    274 	if (fstat(dirp->dd_fd, &stb) < 0)
    275 		goto patherr1;
    276 	if (!S_ISDIR(stb.st_mode)) {
    277 		errno = ENOTDIR;
    278 		goto patherr1;
    279 	}
    280 	while ((dp = readdir(dirp)) != NULL) {
    281 		if (dp->d_ino == 0)
    282 			continue;
    283 		if (match(dp->d_name, pattern)) {
    284 			Gcat(gpath, dp->d_name);
    285 			globcnt++;
    286 		}
    287 	}
    288 	closedir(dirp);
    289 	return;
    290 
    291 patherr1:
    292 	closedir(dirp);
    293 patherr2:
    294 	globerr = "Bad directory components";
    295 }
    296 
    297 static int
    298 execbrc(char *p, char *s)
    299 {
    300 	char restbuf[FTPBUFSIZ + 2];
    301 	register char *pe, *pm, *pl;
    302 	int brclev = 0;
    303 	char *lm, savec, *sgpathp;
    304 	int	len;
    305 
    306 	for (lm = restbuf; *p != '{'; *lm += len, p += len) {
    307 		if ((len = mblen(p, MB_CUR_MAX)) <= 0)
    308 			len = 1;
    309 		memcpy(lm, p, len);
    310 	}
    311 
    312 	for (pe = ++p; *pe; pe += len) {
    313 		if ((len = mblen(pe, MB_CUR_MAX)) <= 0)
    314 			len = 1;
    315 
    316 		switch (*pe) {
    317 
    318 		case '{':
    319 			brclev++;
    320 			continue;
    321 
    322 		case '}':
    323 			if (brclev == 0)
    324 				goto pend;
    325 			brclev--;
    326 			continue;
    327 
    328 		case '[':
    329 			for (pe++; *pe && *pe != ']'; pe += len) {
    330 				if ((len = mblen(pe, MB_CUR_MAX)) <= 0)
    331 					len = 1;
    332 			}
    333 			len = 1;
    334 			continue;
    335 		}
    336 	}
    337 pend:
    338 	brclev = 0;
    339 	for (pl = pm = p; pm <= pe; pm += len) {
    340 		if ((len = mblen(pm, MB_CUR_MAX)) <= 0)
    341 			len = 1;
    342 
    343 		switch (*pm & (QUOTE|TRIM)) {
    344 
    345 		case '{':
    346 			brclev++;
    347 			continue;
    348 
    349 		case '}':
    350 			if (brclev) {
    351 				brclev--;
    352 				continue;
    353 			}
    354 			goto doit;
    355 
    356 		case ','|QUOTE:
    357 		case ',':
    358 			if (brclev)
    359 				continue;
    360 doit:
    361 			savec = *pm;
    362 			*pm = 0;
    363 			(void) strcpy(lm, pl);
    364 			(void) strcat(restbuf, pe + 1);
    365 			*pm = savec;
    366 			if (s == 0) {
    367 				sgpathp = gpathp;
    368 				expand(restbuf);
    369 				gpathp = sgpathp;
    370 				*gpathp = 0;
    371 			} else if (amatch(s, restbuf))
    372 				return (1);
    373 			sort();
    374 			pl = pm + 1;
    375 			if (brclev)
    376 				return (0);
    377 			continue;
    378 
    379 		case '[':
    380 			for (pm++; *pm && *pm != ']'; pm += len) {
    381 				if ((len = mblen(pm, MB_CUR_MAX)) <= 0)
    382 					len = 1;
    383 			}
    384 			len = 1;
    385 			if (!*pm)
    386 				pm--;
    387 			continue;
    388 		}
    389 	}
    390 	if (brclev)
    391 		goto doit;
    392 	return (0);
    393 }
    394 
    395 static int
    396 match(char *s, char *p)
    397 {
    398 	register int c;
    399 	register char *sentp;
    400 	char sglobbed = globbed;
    401 
    402 	if (*s == '.' && *p != '.')
    403 		return (0);
    404 	sentp = entp;
    405 	entp = s;
    406 	c = amatch(s, p);
    407 	entp = sentp;
    408 	globbed = sglobbed;
    409 	return (c);
    410 }
    411 
    412 static int
    413 amatch(char *s, char *p)
    414 {
    415 	wchar_t scc;
    416 	int ok;
    417 	wchar_t lc1, lc2;
    418 	char *sgpathp;
    419 	struct stat stb;
    420 	wchar_t c, cc;
    421 	int	len_s, len_p;
    422 
    423 	globbed = 1;
    424 	for (;;) {
    425 		if ((len_s = mbtowc(&scc, s, MB_CUR_MAX)) <= 0) {
    426 			scc = (unsigned char)*s;
    427 			len_s = 1;
    428 		}
    429 		/* scc = *s++ & TRIM; */
    430 		s += len_s;
    431 
    432 		if ((len_p = mbtowc(&c, p, MB_CUR_MAX)) <= 0) {
    433 			c = (unsigned char)*p;
    434 			len_p = 1;
    435 		}
    436 		p += len_p;
    437 		switch (c) {
    438 
    439 		case '{':
    440 			return (execbrc(p - len_p, s - len_s));
    441 
    442 		case '[':
    443 			ok = 0;
    444 			lc1 = 0;
    445 			while ((cc = *p) != '\0') {
    446 				if ((len_p = mbtowc(&cc, p, MB_CUR_MAX)) <= 0) {
    447 					cc = (unsigned char)*p;
    448 					len_p = 1;
    449 				}
    450 				p += len_p;
    451 				if (cc == ']') {
    452 					if (ok)
    453 						break;
    454 					return (0);
    455 				}
    456 				if (cc == '-') {
    457 					if ((len_p = mbtowc(&lc2, p,
    458 					    MB_CUR_MAX)) <= 0) {
    459 						lc2 = (unsigned char)*p;
    460 						len_p = 1;
    461 					}
    462 					p += len_p;
    463 					if (ftp_fnmatch(scc, lc1, lc2))
    464 						ok++;
    465 				} else
    466 					if (scc == (lc1 = cc))
    467 						ok++;
    468 			}
    469 			if (cc == 0)
    470 				if (!ok)
    471 					return (0);
    472 			continue;
    473 
    474 		case '*':
    475 			if (!*p)
    476 				return (1);
    477 			if (*p == '/') {
    478 				p++;
    479 				goto slash;
    480 			}
    481 			s -= len_s;
    482 			do {
    483 				if (amatch(s, p))
    484 					return (1);
    485 			} while (*s++);
    486 			return (0);
    487 
    488 		case 0:
    489 			return (scc == 0);
    490 
    491 		default:
    492 			if (c != scc)
    493 				return (0);
    494 			continue;
    495 
    496 		case '?':
    497 			if (scc == 0)
    498 				return (0);
    499 			continue;
    500 
    501 		case '/':
    502 			if (scc)
    503 				return (0);
    504 slash:
    505 			s = entp;
    506 			sgpathp = gpathp;
    507 			while (*s)
    508 				addpath(*s++);
    509 			addpath('/');
    510 			if (stat(gpath, &stb) == 0 && S_ISDIR(stb.st_mode))
    511 				if (*p == 0) {
    512 					Gcat(gpath, "");
    513 					globcnt++;
    514 				} else
    515 					expand(p);
    516 			gpathp = sgpathp;
    517 			*gpathp = 0;
    518 			return (0);
    519 		}
    520 	}
    521 }
    522 
    523 #ifdef notdef
    524 static
    525 Gmatch(s, p)
    526 	register char *s, *p;
    527 {
    528 	register int scc;
    529 	int ok, lc;
    530 	int c, cc;
    531 
    532 	for (;;) {
    533 		scc = *s++ & TRIM;
    534 		switch (c = *p++) {
    535 
    536 		case '[':
    537 			ok = 0;
    538 			lc = 077777;
    539 			while (cc = *p++) {
    540 				if (cc == ']') {
    541 					if (ok)
    542 						break;
    543 					return (0);
    544 				}
    545 				if (cc == '-') {
    546 					if (lc <= scc && scc <= *p++)
    547 						ok++;
    548 				} else
    549 					if (scc == (lc = cc))
    550 						ok++;
    551 			}
    552 			if (cc == 0)
    553 				if (ok)
    554 					p--;
    555 				else
    556 					return (0);
    557 			continue;
    558 
    559 		case '*':
    560 			if (!*p)
    561 				return (1);
    562 			for (s--; *s; s++)
    563 				if (Gmatch(s, p))
    564 					return (1);
    565 			return (0);
    566 
    567 		case 0:
    568 			return (scc == 0);
    569 
    570 		default:
    571 			if ((c & TRIM) != scc)
    572 				return (0);
    573 			continue;
    574 
    575 		case '?':
    576 			if (scc == 0)
    577 				return (0);
    578 			continue;
    579 
    580 		}
    581 	}
    582 }
    583 #endif
    584 
    585 static void
    586 Gcat(char *s1, char *s2)
    587 {
    588 	if (gargc >= agargv_size - 1) {
    589 		char **tmp;
    590 
    591 		if (globerr) {
    592 			return;
    593 		}
    594 		tmp = (char **)realloc(agargv,
    595 		    (agargv_size + GAVSIZ) * sizeof (char *));
    596 		if (tmp == NULL) {
    597 			globerr = "Arguments too long";
    598 			return;
    599 		} else {
    600 			agargv = tmp;
    601 			agargv_size += GAVSIZ;
    602 		}
    603 		gargv = agargv;
    604 		sortbas = agargv;
    605 	}
    606 	gargc++;
    607 	gargv[gargc] = 0;
    608 	gargv[gargc - 1] = strspl(s1, s2);
    609 }
    610 
    611 static void
    612 addpath(char c)
    613 {
    614 
    615 	if (gpathp >= lastgpathp)
    616 		globerr = "Pathname too long";
    617 	else {
    618 		*gpathp++ = c;
    619 		*gpathp = 0;
    620 	}
    621 }
    622 
    623 static void
    624 rscan(char **t, int (*f)(char))
    625 {
    626 	register char *p, c;
    627 	int	len;
    628 
    629 	while (p = *t++) {
    630 		if (f == tglob)
    631 			if (*p == '~')
    632 				gflag |= 2;
    633 			else if (eq(p, "{") || eq(p, "{}"))
    634 				continue;
    635 		while ((c = *p) != '\0') {
    636 			(void) (*f)(c);
    637 			if ((len = mblen(p, MB_CUR_MAX)) <= 0)
    638 				len = 1;
    639 			p += len;
    640 		}
    641 	}
    642 }
    643 
    644 static int
    645 tglob(char c)
    646 {
    647 	if (any(c, globchars))
    648 		gflag |= c == '{' ? 2 : 1;
    649 	return (c);
    650 }
    651 
    652 static int
    653 letter(char c)
    654 {
    655 	return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_');
    656 }
    657 
    658 static int
    659 digit(char c)
    660 {
    661 	return (c >= '0' && c <= '9');
    662 }
    663 
    664 static int
    665 any(int c, char *s)
    666 {
    667 	int	len;
    668 
    669 	while (*s) {
    670 		if (*s == c)
    671 			return (1);
    672 		if ((len = mblen(s, MB_CUR_MAX)) <= 0)
    673 			len = 1;
    674 		s += len;
    675 	}
    676 	return (0);
    677 }
    678 
    679 static int
    680 blklen(char **av)
    681 {
    682 	register int i = 0;
    683 
    684 	while (*av++)
    685 		i++;
    686 	return (i);
    687 }
    688 
    689 static char **
    690 blkcpy(char **oav, char **bv)
    691 {
    692 	register char **av = oav;
    693 
    694 	while (*av++ = *bv++)
    695 		continue;
    696 	return (oav);
    697 }
    698 
    699 void
    700 blkfree(char **av0)
    701 {
    702 	register char **av = av0;
    703 
    704 	while (*av)
    705 		xfree(*av++);
    706 	free(av0);
    707 }
    708 
    709 static void
    710 xfree(char *cp)
    711 {
    712 	extern char end[];
    713 
    714 	if (cp >= end && cp < (char *)&cp)
    715 		free(cp);
    716 }
    717 
    718 static char *
    719 strspl(char *cp, char *dp)
    720 {
    721 	register char *ep = malloc((unsigned)(strlen(cp) + strlen(dp) + 1));
    722 
    723 	if (ep == (char *)0)
    724 		fatal("Out of memory");
    725 	(void) strcpy(ep, cp);
    726 	(void) strcat(ep, dp);
    727 	return (ep);
    728 }
    729 
    730 static char **
    731 copyblk(char **v)
    732 {
    733 	register char **nv = (char **)malloc((unsigned)((blklen(v) + 1) *
    734 	    sizeof (char **)));
    735 
    736 	if (nv == (char **)0)
    737 		fatal("Out of memory");
    738 
    739 	return (blkcpy(nv, v));
    740 }
    741 
    742 static char *
    743 strend(char *cp)
    744 {
    745 
    746 	while (*cp)
    747 		cp++;
    748 	return (cp);
    749 }
    750 /*
    751  * Extract a home directory from the password file
    752  * The argument points to a buffer where the name of the
    753  * user whose home directory is sought is currently.
    754  * We write the home directory of the user back there.
    755  */
    756 static int
    757 gethdir(char *home)
    758 {
    759 	register struct passwd *pp = getpwnam(home);
    760 
    761 	if (!pp || home + strlen(pp->pw_dir) >= lastgpathp)
    762 		return (1);
    763 	(void) strcpy(home, pp->pw_dir);
    764 	return (0);
    765 }
    766 
    767 static int
    768 ftp_fnmatch(wchar_t t_ch, wchar_t t_fch, wchar_t t_lch)
    769 {
    770 	char	t_char[MB_LEN_MAX + 1];
    771 	char	t_patan[MB_LEN_MAX * 2 + 8];
    772 	char	*p;
    773 	int	i;
    774 
    775 	if ((t_ch == t_fch) || (t_ch == t_lch))
    776 		return (1);
    777 
    778 	p = t_patan;
    779 	if ((i = wctomb(t_char, (wchar_t)t_ch)) <= 0)
    780 		return (0);
    781 	t_char[i] = 0;
    782 
    783 	*p++ = '[';
    784 	if ((i = wctomb(p, (wchar_t)t_fch)) <= 0)
    785 		return (0);
    786 	p += i;
    787 	*p++ = '-';
    788 	if ((i = wctomb(p, (wchar_t)t_lch)) <= 0)
    789 		return (0);
    790 	p += i;
    791 	*p++ = ']';
    792 	*p = 0;
    793 
    794 	if (fnmatch(t_patan, t_char, FNM_NOESCAPE))
    795 		return (0);
    796 	return (1);
    797 }
    798