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