xref: /original-bsd/bin/csh/glob.c (revision 87a44d1b)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.  The Berkeley Software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 static char *sccsid = "@(#)glob.c	5.5 (Berkeley) 12/22/87";
9 #endif
10 
11 #include "sh.h"
12 #include <sys/dir.h>
13 
14 /*
15  * C Shell
16  */
17 
18 int	globcnt;
19 
20 char	*gpath, *gpathp, *lastgpathp;
21 int	globbed;
22 bool	noglob;
23 bool	nonomatch;
24 char	*entp;
25 char	**sortbas;
26 int	sortscmp();
27 
28 #define sort()	qsort((char *)sortbas, &gargv[gargc] - sortbas, \
29 		      sizeof(*sortbas), sortscmp), sortbas = &gargv[gargc]
30 
31 
32 char **
33 glob(v)
34 	register char **v;
35 {
36 	char agpath[BUFSIZ];
37 	char *agargv[GAVSIZ];
38 
39 	gpath = agpath; gpathp = gpath; *gpathp = 0;
40 	lastgpathp = &gpath[sizeof agpath - 2];
41 	ginit(agargv); globcnt = 0;
42 #ifdef GDEBUG
43 	printf("glob entered: "); blkpr(v); printf("\n");
44 #endif
45 	noglob = adrof("noglob") != 0;
46 	nonomatch = adrof("nonomatch") != 0;
47 	globcnt = noglob | nonomatch;
48 	while (*v)
49 		collect(*v++);
50 #ifdef GDEBUG
51 	printf("glob done, globcnt=%d, gflag=%d: ", globcnt, gflag); blkpr(gargv); printf("\n");
52 #endif
53 	if (globcnt == 0 && (gflag&1)) {
54 		blkfree(gargv), gargv = 0;
55 		return (0);
56 	} else
57 		return (gargv = copyblk(gargv));
58 }
59 
60 ginit(agargv)
61 	char **agargv;
62 {
63 
64 	agargv[0] = 0; gargv = agargv; sortbas = agargv; gargc = 0;
65 	gnleft = NCARGS - 4;
66 }
67 
68 collect(as)
69 	register char *as;
70 {
71 	register int i;
72 
73 	if (any('`', as)) {
74 #ifdef GDEBUG
75 		printf("doing backp of %s\n", as);
76 #endif
77 		(void) dobackp(as, 0);
78 #ifdef GDEBUG
79 		printf("backp done, acollect'ing\n");
80 #endif
81 		for (i = 0; i < pargc; i++)
82 			if (noglob) {
83 				Gcat(pargv[i], "");
84 				sortbas = &gargv[gargc];
85 			} else
86 				acollect(pargv[i]);
87 		if (pargv)
88 			blkfree(pargv), pargv = 0;
89 #ifdef GDEBUG
90 		printf("acollect done\n");
91 #endif
92 	} else if (noglob || eq(as, "{") || eq(as, "{}")) {
93 		Gcat(as, "");
94 		sort();
95 	} else
96 		acollect(as);
97 }
98 
99 acollect(as)
100 	register char *as;
101 {
102 	register int ogargc = gargc;
103 
104 	gpathp = gpath; *gpathp = 0; globbed = 0;
105 	expand(as);
106 	if (gargc == ogargc) {
107 		if (nonomatch) {
108 			Gcat(as, "");
109 			sort();
110 		}
111 	} else
112 		sort();
113 }
114 
115 /*
116  * String compare for qsort.  Also used by filec code in sh.file.c.
117  */
118 sortscmp(a1, a2)
119 	char **a1, **a2;
120 {
121 
122 	 return (strcmp(*a1, *a2));
123 }
124 
125 expand(as)
126 	char *as;
127 {
128 	register char *cs;
129 	register char *sgpathp, *oldcs;
130 	struct stat stb;
131 
132 	sgpathp = gpathp;
133 	cs = as;
134 	if (*cs == '~' && gpathp == gpath) {
135 		addpath('~');
136 		for (cs++; letter(*cs) || digit(*cs) || *cs == '-';)
137 			addpath(*cs++);
138 		if (!*cs || *cs == '/') {
139 			if (gpathp != gpath + 1) {
140 				*gpathp = 0;
141 				if (gethdir(gpath + 1))
142 					error("Unknown user: %s", gpath + 1);
143 				(void) strcpy(gpath, gpath + 1);
144 			} else
145 				(void) strcpy(gpath, value("home"));
146 			gpathp = strend(gpath);
147 		}
148 	}
149 	while (!isglob(*cs)) {
150 		if (*cs == 0) {
151 			if (!globbed)
152 				Gcat(gpath, "");
153 			else if (stat(gpath, &stb) >= 0) {
154 				Gcat(gpath, "");
155 				globcnt++;
156 			}
157 			goto endit;
158 		}
159 		addpath(*cs++);
160 	}
161 	oldcs = cs;
162 	while (cs > as && *cs != '/')
163 		cs--, gpathp--;
164 	if (*cs == '/')
165 		cs++, gpathp++;
166 	*gpathp = 0;
167 	if (*oldcs == '{') {
168 		(void) execbrc(cs, NOSTR);
169 		return;
170 	}
171 	matchdir(cs);
172 endit:
173 	gpathp = sgpathp;
174 	*gpathp = 0;
175 }
176 
177 matchdir(pattern)
178 	char *pattern;
179 {
180 	struct stat stb;
181 	register struct direct *dp;
182 	register DIR *dirp;
183 
184 	dirp = opendir(gpath);
185 	if (dirp == NULL) {
186 		if (globbed)
187 			return;
188 		goto patherr2;
189 	}
190 	if (fstat(dirp->dd_fd, &stb) < 0)
191 		goto patherr1;
192 	if (!isdir(stb)) {
193 		errno = ENOTDIR;
194 		goto patherr1;
195 	}
196 	while ((dp = readdir(dirp)) != NULL) {
197 		if (dp->d_ino == 0)
198 			continue;
199 		if (match(dp->d_name, pattern)) {
200 			Gcat(gpath, dp->d_name);
201 			globcnt++;
202 		}
203 	}
204 	closedir(dirp);
205 	return;
206 
207 patherr1:
208 	closedir(dirp);
209 patherr2:
210 	Perror(gpath);
211 }
212 
213 execbrc(p, s)
214 	char *p, *s;
215 {
216 	char restbuf[BUFSIZ + 2];
217 	register char *pe, *pm, *pl;
218 	int brclev = 0;
219 	char *lm, savec, *sgpathp;
220 
221 	for (lm = restbuf; *p != '{'; *lm++ = *p++)
222 		continue;
223 	for (pe = ++p; *pe; pe++)
224 	switch (*pe) {
225 
226 	case '{':
227 		brclev++;
228 		continue;
229 
230 	case '}':
231 		if (brclev == 0)
232 			goto pend;
233 		brclev--;
234 		continue;
235 
236 	case '[':
237 		for (pe++; *pe && *pe != ']'; pe++)
238 			continue;
239 		if (!*pe)
240 			error("Missing ]");
241 		continue;
242 	}
243 pend:
244 	if (brclev || !*pe)
245 		error("Missing }");
246 	for (pl = pm = p; pm <= pe; pm++)
247 	switch (*pm & (QUOTE|TRIM)) {
248 
249 	case '{':
250 		brclev++;
251 		continue;
252 
253 	case '}':
254 		if (brclev) {
255 			brclev--;
256 			continue;
257 		}
258 		goto doit;
259 
260 	case ',':
261 		if (brclev)
262 			continue;
263 doit:
264 		savec = *pm;
265 		*pm = 0;
266 		(void) strcpy(lm, pl);
267 		(void) strcat(restbuf, pe + 1);
268 		*pm = savec;
269 		if (s == 0) {
270 			sgpathp = gpathp;
271 			expand(restbuf);
272 			gpathp = sgpathp;
273 			*gpathp = 0;
274 		} else if (amatch(s, restbuf))
275 			return (1);
276 		sort();
277 		pl = pm + 1;
278 		continue;
279 
280 	case '[':
281 		for (pm++; *pm && *pm != ']'; pm++)
282 			continue;
283 		if (!*pm)
284 			error("Missing ]");
285 		continue;
286 	}
287 	return (0);
288 }
289 
290 match(s, p)
291 	char *s, *p;
292 {
293 	register int c;
294 	register char *sentp;
295 	char sglobbed = globbed;
296 
297 	if (*s == '.' && *p != '.')
298 		return (0);
299 	sentp = entp;
300 	entp = s;
301 	c = amatch(s, p);
302 	entp = sentp;
303 	globbed = sglobbed;
304 	return (c);
305 }
306 
307 amatch(s, p)
308 	register char *s, *p;
309 {
310 	register int scc;
311 	int ok, lc;
312 	char *sgpathp;
313 	struct stat stb;
314 	int c, cc;
315 
316 	globbed = 1;
317 	for (;;) {
318 		scc = *s++ & TRIM;
319 		switch (c = *p++) {
320 
321 		case '{':
322 			return (execbrc(p - 1, s - 1));
323 
324 		case '[':
325 			ok = 0;
326 			lc = 077777;
327 			while (cc = *p++) {
328 				if (cc == ']') {
329 					if (ok)
330 						break;
331 					return (0);
332 				}
333 				if (cc == '-') {
334 					if (lc <= scc && scc <= *p++)
335 						ok++;
336 				} else
337 					if (scc == (lc = cc))
338 						ok++;
339 			}
340 			if (cc == 0)
341 				error("Missing ]");
342 			continue;
343 
344 		case '*':
345 			if (!*p)
346 				return (1);
347 			if (*p == '/') {
348 				p++;
349 				goto slash;
350 			}
351 			for (s--; *s; s++)
352 				if (amatch(s, p))
353 					return (1);
354 			return (0);
355 
356 		case 0:
357 			return (scc == 0);
358 
359 		default:
360 			if ((c & TRIM) != scc)
361 				return (0);
362 			continue;
363 
364 		case '?':
365 			if (scc == 0)
366 				return (0);
367 			continue;
368 
369 		case '/':
370 			if (scc)
371 				return (0);
372 slash:
373 			s = entp;
374 			sgpathp = gpathp;
375 			while (*s)
376 				addpath(*s++);
377 			addpath('/');
378 			if (stat(gpath, &stb) == 0 && isdir(stb))
379 				if (*p == 0) {
380 					Gcat(gpath, "");
381 					globcnt++;
382 				} else
383 					expand(p);
384 			gpathp = sgpathp;
385 			*gpathp = 0;
386 			return (0);
387 		}
388 	}
389 }
390 
391 Gmatch(s, p)
392 	register char *s, *p;
393 {
394 	register int scc;
395 	int ok, lc;
396 	int c, cc;
397 
398 	for (;;) {
399 		scc = *s++ & TRIM;
400 		switch (c = *p++) {
401 
402 		case '[':
403 			ok = 0;
404 			lc = 077777;
405 			while (cc = *p++) {
406 				if (cc == ']') {
407 					if (ok)
408 						break;
409 					return (0);
410 				}
411 				if (cc == '-') {
412 					if (lc <= scc && scc <= *p++)
413 						ok++;
414 				} else
415 					if (scc == (lc = cc))
416 						ok++;
417 			}
418 			if (cc == 0)
419 				bferr("Missing ]");
420 			continue;
421 
422 		case '*':
423 			if (!*p)
424 				return (1);
425 			for (s--; *s; s++)
426 				if (Gmatch(s, p))
427 					return (1);
428 			return (0);
429 
430 		case 0:
431 			return (scc == 0);
432 
433 		default:
434 			if ((c & TRIM) != scc)
435 				return (0);
436 			continue;
437 
438 		case '?':
439 			if (scc == 0)
440 				return (0);
441 			continue;
442 
443 		}
444 	}
445 }
446 
447 Gcat(s1, s2)
448 	char *s1, *s2;
449 {
450 	register char *p, *q;
451 	int n;
452 
453 	for (p = s1; *p++;)
454 		;
455 	for (q = s2; *q++;)
456 		;
457 	gnleft -= (n = (p - s1) + (q - s2) - 1);
458 	if (gnleft <= 0 || ++gargc >= GAVSIZ)
459 		error("Arguments too long");
460 	gargv[gargc] = 0;
461 	p = gargv[gargc - 1] = xalloc((unsigned)n);
462 	for (q = s1; *p++ = *q++;)
463 		;
464 	for (p--, q = s2; *p++ = *q++;)
465 		;
466 }
467 
468 addpath(c)
469 	char c;
470 {
471 
472 	if (gpathp >= lastgpathp)
473 		error("Pathname too long");
474 	*gpathp++ = c & TRIM;
475 	*gpathp = 0;
476 }
477 
478 rscan(t, f)
479 	register char **t;
480 	int (*f)();
481 {
482 	register char *p;
483 
484 	while (p = *t++)
485 		while (*p)
486 			(*f)(*p++);
487 }
488 
489 trim(t)
490 	register char **t;
491 {
492 	register char *p;
493 
494 	while (p = *t++)
495 		while (*p)
496 			*p++ &= TRIM;
497 }
498 
499 tglob(t)
500 	register char **t;
501 {
502 	register char *p, c;
503 
504 	while (p = *t++) {
505 		if (*p == '~')
506 			gflag |= 2;
507 		else if (*p == '{' && (p[1] == '\0' || p[1] == '}' && p[2] == '\0'))
508 			continue;
509 		while (c = *p++)
510 			if (isglob(c))
511 				gflag |= c == '{' ? 2 : 1;
512 	}
513 }
514 
515 char *
516 globone(str)
517 	register char *str;
518 {
519 	char *gv[2];
520 	register char **gvp;
521 	register char *cp;
522 
523 	gv[0] = str;
524 	gv[1] = 0;
525 	gflag = 0;
526 	tglob(gv);
527 	if (gflag) {
528 		gvp = glob(gv);
529 		if (gvp == 0) {
530 			setname(str);
531 			bferr("No match");
532 		}
533 		cp = *gvp++;
534 		if (cp == 0)
535 			cp = "";
536 		else if (*gvp) {
537 			setname(str);
538 			bferr("Ambiguous");
539 		} else
540 			cp = strip(cp);
541 /*
542 		if (cp == 0 || *gvp) {
543 			setname(str);
544 			bferr(cp ? "Ambiguous" : "No output");
545 		}
546 */
547 		xfree((char *)gargv); gargv = 0;
548 	} else {
549 		trim(gv);
550 		cp = savestr(gv[0]);
551 	}
552 	return (cp);
553 }
554 
555 /*
556  * Command substitute cp.  If literal, then this is
557  * a substitution from a << redirection, and so we should
558  * not crunch blanks and tabs, separating words only at newlines.
559  */
560 char **
561 dobackp(cp, literal)
562 	char *cp;
563 	bool literal;
564 {
565 	register char *lp, *rp;
566 	char *ep;
567 	char word[BUFSIZ];
568 	char *apargv[GAVSIZ + 2];
569 
570 	if (pargv) {
571 		abort();
572 		blkfree(pargv);
573 	}
574 	pargv = apargv;
575 	pargv[0] = NOSTR;
576 	pargcp = pargs = word;
577 	pargc = 0;
578 	pnleft = BUFSIZ - 4;
579 	for (;;) {
580 		for (lp = cp; *lp != '`'; lp++) {
581 			if (*lp == 0) {
582 				if (pargcp != pargs)
583 					pword();
584 #ifdef GDEBUG
585 				printf("leaving dobackp\n");
586 #endif
587 				return (pargv = copyblk(pargv));
588 			}
589 			psave(*lp);
590 		}
591 		lp++;
592 		for (rp = lp; *rp && *rp != '`'; rp++)
593 			if (*rp == '\\') {
594 				rp++;
595 				if (!*rp)
596 					goto oops;
597 			}
598 		if (!*rp)
599 oops:
600 			error("Unmatched `");
601 		ep = savestr(lp);
602 		ep[rp - lp] = 0;
603 		backeval(ep, literal);
604 #ifdef GDEBUG
605 		printf("back from backeval\n");
606 #endif
607 		cp = rp + 1;
608 	}
609 }
610 
611 backeval(cp, literal)
612 	char *cp;
613 	bool literal;
614 {
615 	int pvec[2];
616 	int quoted = (literal || (cp[0] & QUOTE)) ? QUOTE : 0;
617 	char ibuf[BUFSIZ];
618 	register int icnt = 0, c;
619 	register char *ip;
620 	bool hadnl = 0;
621 	char *fakecom[2];
622 	struct command faket;
623 
624 	faket.t_dtyp = TCOM;
625 	faket.t_dflg = 0;
626 	faket.t_dlef = 0;
627 	faket.t_drit = 0;
628 	faket.t_dspr = 0;
629 	faket.t_dcom = fakecom;
630 	fakecom[0] = "` ... `";
631 	fakecom[1] = 0;
632 	/*
633 	 * We do the psave job to temporarily change the current job
634 	 * so that the following fork is considered a separate job.
635 	 * This is so that when backquotes are used in a
636 	 * builtin function that calls glob the "current job" is not corrupted.
637 	 * We only need one level of pushed jobs as long as we are sure to
638 	 * fork here.
639 	 */
640 	psavejob();
641 	/*
642 	 * It would be nicer if we could integrate this redirection more
643 	 * with the routines in sh.sem.c by doing a fake execute on a builtin
644 	 * function that was piped out.
645 	 */
646 	mypipe(pvec);
647 	if (pfork(&faket, -1) == 0) {
648 		struct wordent paraml;
649 		struct command *t;
650 
651 		(void) close(pvec[0]);
652 		(void) dmove(pvec[1], 1);
653 		(void) dmove(SHDIAG, 2);
654 		initdesc();
655 		arginp = cp;
656 		while (*cp)
657 			*cp++ &= TRIM;
658 		(void) lex(&paraml);
659 		if (err)
660 			error(err);
661 		alias(&paraml);
662 		t = syntax(paraml.next, &paraml, 0);
663 		if (err)
664 			error(err);
665 		if (t)
666 			t->t_dflg |= FPAR;
667 		(void) signal(SIGTSTP, SIG_IGN);
668 		(void) signal(SIGTTIN, SIG_IGN);
669 		(void) signal(SIGTTOU, SIG_IGN);
670 		execute(t, -1);
671 		exitstat();
672 	}
673 	xfree(cp);
674 	(void) close(pvec[1]);
675 	do {
676 		int cnt = 0;
677 		for (;;) {
678 			if (icnt == 0) {
679 				ip = ibuf;
680 				icnt = read(pvec[0], ip, BUFSIZ);
681 				if (icnt <= 0) {
682 					c = -1;
683 					break;
684 				}
685 			}
686 			if (hadnl)
687 				break;
688 			--icnt;
689 			c = (*ip++ & TRIM);
690 			if (c == 0)
691 				break;
692 			if (c == '\n') {
693 				/*
694 				 * Continue around the loop one
695 				 * more time, so that we can eat
696 				 * the last newline without terminating
697 				 * this word.
698 				 */
699 				hadnl = 1;
700 				continue;
701 			}
702 			if (!quoted && (c == ' ' || c == '\t'))
703 				break;
704 			cnt++;
705 			psave(c | quoted);
706 		}
707 		/*
708 		 * Unless at end-of-file, we will form a new word
709 		 * here if there were characters in the word, or in
710 		 * any case when we take text literally.  If
711 		 * we didn't make empty words here when literal was
712 		 * set then we would lose blank lines.
713 		 */
714 		if (c != -1 && (cnt || literal))
715 			pword();
716 		hadnl = 0;
717 	} while (c >= 0);
718 #ifdef GDEBUG
719 	printf("done in backeval, pvec: %d %d\n", pvec[0], pvec[1]);
720 	printf("also c = %c <%o>\n", c, c);
721 #endif
722 	(void) close(pvec[0]);
723 	pwait();
724 	prestjob();
725 }
726 
727 psave(c)
728 	char c;
729 {
730 
731 	if (--pnleft <= 0)
732 		error("Word too long");
733 	*pargcp++ = c;
734 }
735 
736 pword()
737 {
738 
739 	psave(0);
740 	if (pargc == GAVSIZ)
741 		error("Too many words from ``");
742 	pargv[pargc++] = savestr(pargs);
743 	pargv[pargc] = NOSTR;
744 #ifdef GDEBUG
745 	printf("got word %s\n", pargv[pargc-1]);
746 #endif
747 	pargcp = pargs;
748 	pnleft = BUFSIZ - 4;
749 }
750