xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.bin/ftp/glob.c (revision 2aeafac3612e19716bf8164f89c3c9196342979c)
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