xref: /original-bsd/bin/csh/glob.c (revision b6a2a1ed)
1 /*-
2  * Copyright (c) 1980, 1991 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)glob.c	5.21 (Berkeley) 06/24/91";
10 #endif /* not lint */
11 
12 #include <sys/param.h>
13 #include <glob.h>
14 #include <errno.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18 #if __STDC__
19 # include <stdarg.h>
20 #else
21 # include <varargs.h>
22 #endif
23 
24 #include "csh.h"
25 #include "extern.h"
26 
27 static int noglob, nonomatch;
28 static int pargsiz, gargsiz;
29 
30 /*
31  * Values for gflag
32  */
33 #define	G_NONE	0		/* No globbing needed			*/
34 #define	G_GLOB	1		/* string contains *?[] characters	*/
35 #define	G_CSH	2		/* string contains ~`{ characters	*/
36 
37 #define	GLOBSPACE	100	/* Alloc increment			*/
38 
39 #define LBRC '{'
40 #define RBRC '}'
41 #define LBRK '['
42 #define RBRK ']'
43 #define EOS '\0'
44 
45 Char  **gargv = NULL;
46 long    gargc = 0;
47 Char  **pargv = NULL;
48 long    pargc = 0;
49 
50 /*
51  * globbing is now done in two stages. In the first pass we expand
52  * csh globbing idioms ~`{ and then we proceed doing the normal
53  * globbing if needed ?*[
54  *
55  * Csh type globbing is handled in globexpand() and the rest is
56  * handled in glob() which is part of the 4.4BSD libc.
57  *
58  */
59 static Char	*globtilde __P((Char **, Char *));
60 static Char	**libglob __P((Char **));
61 static Char	**globexpand __P((Char **));
62 static int	globbrace __P((Char *, Char *, Char ***));
63 static void	pword __P((void));
64 static void	psave __P((int));
65 static void	backeval __P((Char *, bool));
66 
67 
68 static Char *
69 globtilde(nv, s)
70     Char  **nv, *s;
71 {
72     Char    gbuf[MAXPATHLEN], *gstart, *b, *u, *e;
73 
74     gstart = gbuf;
75     *gstart++ = *s++;
76     u = s;
77     for (b = gstart, e = &gbuf[MAXPATHLEN - 1]; *s && *s != '/' && b < e;
78 	 *b++ = *s++);
79     *b = EOS;
80     if (gethdir(gstart)) {
81 	blkfree(nv);
82 	if (*gstart)
83 	    stderror(ERR_UNKUSER, short2str(gstart));
84 	else
85 	    stderror(ERR_NOHOME);
86     }
87     b = &gstart[Strlen(gstart)];
88     while (*s)
89 	*b++ = *s++;
90     *b = EOS;
91     --u;
92     xfree((ptr_t) u);
93     return (Strsave(gstart));
94 }
95 
96 static int
97 globbrace(s, p, bl)
98     Char   *s, *p, ***bl;
99 {
100     int     i, len;
101     Char   *pm, *pe, *lm, *pl;
102     Char  **nv, **vl;
103     Char    gbuf[MAXPATHLEN];
104     int     size = GLOBSPACE;
105 
106     nv = vl = (Char **) xmalloc((size_t) sizeof(Char *) * size);
107     *vl = NULL;
108 
109     len = 0;
110     /* copy part up to the brace */
111     for (lm = gbuf, p = s; *p != LBRC; *lm++ = *p++)
112 	continue;
113 
114     /* check for balanced braces */
115     for (i = 0, pe = ++p; *pe; pe++)
116 	if (*pe == LBRK) {
117 	    /* Ignore everything between [] */
118 	    for (++pe; *pe != RBRK && *pe != EOS; pe++)
119 		continue;
120 	    if (*pe == EOS) {
121 		blkfree(nv);
122 		return (-LBRK);
123 	    }
124 	}
125 	else if (*pe == LBRC)
126 	    i++;
127 	else if (*pe == RBRC) {
128 	    if (i == 0)
129 		break;
130 	    i--;
131 	}
132 
133     if (i != 0) {
134 	blkfree(nv);
135 	return (-LBRC);
136     }
137 
138     for (i = 0, pl = pm = p; pm <= pe; pm++)
139 	switch (*pm) {
140 	case LBRK:
141 	    for (++pm; *pm != RBRK && *pm != EOS; pm++)
142 		continue;
143 	    if (*pm == EOS) {
144 		*vl = NULL;
145 		blkfree(nv);
146 		return (-RBRK);
147 	    }
148 	    break;
149 	case LBRC:
150 	    i++;
151 	    break;
152 	case RBRC:
153 	    if (i) {
154 		i--;
155 		break;
156 	    }
157 	    /* FALLTHROUGH */
158 	case ',':
159 	    if (i && *pm == ',')
160 		break;
161 	    else {
162 		Char    savec = *pm;
163 
164 		*pm = EOS;
165 		(void) Strcpy(lm, pl);
166 		(void) Strcat(gbuf, pe + 1);
167 		*pm = savec;
168 		*vl++ = Strsave(gbuf);
169 		len++;
170 		pl = pm + 1;
171 		if (vl == &nv[size]) {
172 		    size += GLOBSPACE;
173 		    nv = (Char **) xrealloc((ptr_t) nv, (size_t)
174 					    size * sizeof(Char *));
175 		    vl = &nv[size - GLOBSPACE];
176 		}
177 	    }
178 	    break;
179 	}
180     *vl = NULL;
181     *bl = nv;
182     return (len);
183 }
184 
185 static Char **
186 globexpand(v)
187     Char  **v;
188 {
189     Char   *s;
190     Char  **nv, **vl, **el;
191     int     size = GLOBSPACE;
192 
193 
194     nv = vl = (Char **) xmalloc((size_t) sizeof(Char *) * size);
195     *vl = NULL;
196 
197     /*
198      * Step 1: expand backquotes.
199      */
200     while (s = *v++) {
201 	if (Strchr(s, '`')) {
202 	    int     i;
203 
204 	    (void) dobackp(s, 0);
205 	    for (i = 0; i < pargc; i++) {
206 		*vl++ = pargv[i];
207 		if (vl == &nv[size]) {
208 		    size += GLOBSPACE;
209 		    nv = (Char **) xrealloc((ptr_t) nv,
210 					    (size_t) size * sizeof(Char *));
211 		    vl = &nv[size - GLOBSPACE];
212 		}
213 	    }
214 	    xfree((ptr_t) pargv);
215 	    pargv = NULL;
216 	}
217 	else {
218 	    *vl++ = Strsave(s);
219 	    if (vl == &nv[size]) {
220 		size += GLOBSPACE;
221 		nv = (Char **) xrealloc((ptr_t) nv, (size_t)
222 					size * sizeof(Char *));
223 		vl = &nv[size - GLOBSPACE];
224 	    }
225 	}
226     }
227     *vl = NULL;
228 
229     if (noglob)
230 	return (nv);
231 
232     /*
233      * Step 2: expand braces
234      */
235     el = vl;
236     vl = nv;
237     for (s = *vl; s; s = *++vl) {
238 	Char   *b;
239 	Char  **vp, **bp;
240 
241 	if (b = Strchr(s, LBRC)) {
242 	    Char  **bl;
243 	    int     len;
244 
245 	    if ((len = globbrace(s, b, &bl)) < 0) {
246 		blkfree(nv);
247 		stderror(ERR_MISSING, -len);
248 	    }
249 	    xfree((ptr_t) s);
250 	    if (len == 1) {
251 		*vl-- = *bl;
252 		xfree((ptr_t) bl);
253 		continue;
254 	    }
255 	    len = blklen(bl);
256 	    if (&el[len] >= &nv[size]) {
257 		int     l, e;
258 
259 		l = &el[len] - &nv[size];
260 		size += GLOBSPACE > l ? GLOBSPACE : l;
261 		l = vl - nv;
262 		e = el - nv;
263 		nv = (Char **) xrealloc((ptr_t) nv, (size_t)
264 					size * sizeof(Char *));
265 		vl = nv + l;
266 		el = nv + e;
267 	    }
268 	    vp = vl--;
269 	    *vp = *bl;
270 	    len--;
271 	    for (bp = el; bp != vp; bp--)
272 		bp[len] = *bp;
273 	    el += len;
274 	    vp++;
275 	    for (bp = bl + 1; *bp; *vp++ = *bp++)
276 		continue;
277 	    xfree((ptr_t) bl);
278 	}
279 
280     }
281 
282     /*
283      * Step 3: expand ~
284      */
285     vl = nv;
286     for (s = *vl; s; s = *++vl)
287 	if (*s == '~')
288 	    *vl = globtilde(nv, s);
289     vl = nv;
290     return (vl);
291 }
292 
293 static Char *
294 handleone(str, vl, action)
295     Char   *str, **vl;
296     int     action;
297 {
298 
299     Char   *cp, **vlp = vl;
300 
301     switch (action) {
302     case G_ERROR:
303 	setname(short2str(str));
304 	blkfree(vl);
305 	stderror(ERR_NAME | ERR_AMBIG);
306 	break;
307     case G_APPEND:
308 	trim(vlp);
309 	str = Strsave(*vlp++);
310 	do {
311 	    cp = Strspl(str, STRspace);
312 	    xfree((ptr_t) str);
313 	    str = Strspl(cp, *vlp);
314 	    xfree((ptr_t) cp);
315 	}
316 	while (*++vlp);
317 	blkfree(vl);
318 	break;
319     case G_IGNORE:
320 	str = Strsave(strip(*vlp));
321 	blkfree(vl);
322 	break;
323     }
324     return (str);
325 }
326 
327 static Char **
328 libglob(vl)
329     Char  **vl;
330 {
331     int     gflgs = GLOB_QUOTE | GLOB_NOCHECK;
332     glob_t  globv;
333     char   *ptr;
334 
335     globv.gl_offs = 0;
336     globv.gl_pathv = 0;
337     globv.gl_pathc = 0;
338     nonomatch = adrof(STRnonomatch) != 0;
339     do {
340 	ptr = short2qstr(*vl);
341 	switch (glob(ptr, gflgs, 0, &globv)) {
342 	case GLOB_ABEND:
343 	    setname(ptr);
344 	    stderror(ERR_NAME | ERR_GLOB);
345 	    /* NOTREACHED */
346 	case GLOB_NOSPACE:
347 	    stderror(ERR_NOMEM);
348 	    /* NOTREACHED */
349 	default:
350 	    break;
351 	}
352 	if (!nonomatch && (globv.gl_matchc == 0) &&
353 	    (globv.gl_flags & GLOB_MAGCHAR)) {
354 	    globfree(&globv);
355 	    return (NULL);
356 	}
357 	gflgs |= GLOB_APPEND;
358     }
359     while (*++vl);
360     vl = blk2short(globv.gl_pathv);
361     globfree(&globv);
362     return (vl);
363 }
364 
365 Char   *
366 globone(str, action)
367     Char   *str;
368     int     action;
369 {
370 
371     Char   *v[2], **vl, **vo;
372 
373     noglob = adrof(STRnoglob) != 0;
374     gflag = 0;
375     v[0] = str;
376     v[1] = 0;
377     tglob(v);
378     if (gflag == G_NONE)
379 	return (strip(Strsave(str)));
380 
381     if (gflag & G_CSH) {
382 	/*
383 	 * Expand back-quote, tilde and brace
384 	 */
385 	vo = globexpand(v);
386 	if (noglob || (gflag & G_GLOB) == 0) {
387 	    if (vo[0] == NULL) {
388 		xfree((ptr_t) vo);
389 		return (Strsave(STRNULL));
390 	    }
391 	    if (vo[1] != NULL)
392 		return (handleone(str, vo, action));
393 	    else {
394 		str = strip(vo[0]);
395 		xfree((ptr_t) vo);
396 		return (str);
397 	    }
398 	}
399     }
400     else if (noglob || (gflag & G_GLOB) == 0)
401 	return (strip(Strsave(str)));
402     else
403 	vo = v;
404 
405     vl = libglob(vo);
406     if (gflag & G_CSH)
407 	blkfree(vo);
408     if (vl == NULL) {
409 	setname(short2str(str));
410 	stderror(ERR_NAME | ERR_NOMATCH);
411     }
412     if (vl[0] == NULL) {
413 	xfree((ptr_t) vl);
414 	return (Strsave(STRNULL));
415     }
416     if (vl[1] != NULL)
417 	return (handleone(str, vl, action));
418     else {
419 	str = strip(*vl);
420 	xfree((ptr_t) vl);
421 	return (str);
422     }
423 }
424 
425 Char  **
426 globall(v)
427     Char  **v;
428 {
429     Char  **vl, **vo;
430 
431     if (!v || !v[0]) {
432 	gargv = saveblk(v);
433 	gargc = blklen(gargv);
434 	return (gargv);
435     }
436 
437     noglob = adrof(STRnoglob) != 0;
438 
439     if (gflag & G_CSH)
440 	/*
441 	 * Expand back-quote, tilde and brace
442 	 */
443 	vl = vo = globexpand(v);
444     else
445 	vl = vo = saveblk(v);
446 
447     if (!noglob && (gflag & G_GLOB)) {
448 	vl = libglob(vo);
449 	if (gflag & G_CSH)
450 	    blkfree(vo);
451     }
452 
453     gargc = vl ? blklen(vl) : 0;
454     return (gargv = vl);
455 }
456 
457 void
458 ginit()
459 {
460     gargsiz = GLOBSPACE;
461     gargv = (Char **) xmalloc((size_t) sizeof(Char *) * gargsiz);
462     gargv[0] = 0;
463     gargc = 0;
464 }
465 
466 void
467 rscan(t, f)
468     register Char **t;
469     void    (*f) ();
470 {
471     register Char *p;
472 
473     while (p = *t++)
474 	while (*p)
475 	    (*f) (*p++);
476 }
477 
478 void
479 trim(t)
480     register Char **t;
481 {
482     register Char *p;
483 
484     while (p = *t++)
485 	while (*p)
486 	    *p++ &= TRIM;
487 }
488 
489 void
490 tglob(t)
491     register Char **t;
492 {
493     register Char *p, c;
494 
495     while (p = *t++) {
496 	if (*p == '~' || *p == '=')
497 	    gflag |= G_CSH;
498 	else if (*p == '{' &&
499 		 (p[1] == '\0' || p[1] == '}' && p[2] == '\0'))
500 	    continue;
501 	while (c = *p++)
502 	    if (isglob(c))
503 		gflag |= (c == '{' || c == '`') ? G_CSH : G_GLOB;
504     }
505 }
506 
507 /*
508  * Command substitute cp.  If literal, then this is a substitution from a
509  * << redirection, and so we should not crunch blanks and tabs, separating
510  * words only at newlines.
511  */
512 Char  **
513 dobackp(cp, literal)
514     Char   *cp;
515     bool    literal;
516 {
517     register Char *lp, *rp;
518     Char   *ep, word[MAXPATHLEN];
519 
520     if (pargv) {
521 	abort();
522 	blkfree(pargv);
523     }
524     pargsiz = GLOBSPACE;
525     pargv = (Char **) xmalloc((size_t) sizeof(Char *) * pargsiz);
526     pargv[0] = NULL;
527     pargcp = pargs = word;
528     pargc = 0;
529     pnleft = MAXPATHLEN - 4;
530     for (;;) {
531 	for (lp = cp; *lp != '`'; lp++) {
532 	    if (*lp == 0) {
533 		if (pargcp != pargs)
534 		    pword();
535 		return (pargv);
536 	    }
537 	    psave(*lp);
538 	}
539 	lp++;
540 	for (rp = lp; *rp && *rp != '`'; rp++)
541 	    if (*rp == '\\') {
542 		rp++;
543 		if (!*rp)
544 		    goto oops;
545 	    }
546 	if (!*rp)
547     oops:  stderror(ERR_UNMATCHED, '`');
548 	ep = Strsave(lp);
549 	ep[rp - lp] = 0;
550 	backeval(ep, literal);
551 	cp = rp + 1;
552     }
553 }
554 
555 static void
556 backeval(cp, literal)
557     Char   *cp;
558     bool    literal;
559 {
560     register int icnt, c;
561     register Char *ip;
562     struct command faket;
563     bool    hadnl;
564     int     pvec[2], quoted;
565     Char   *fakecom[2], ibuf[BUFSIZ];
566     char    tibuf[BUFSIZ];
567 
568     hadnl = 0;
569     icnt = 0;
570     quoted = (literal || (cp[0] & QUOTE)) ? QUOTE : 0;
571     faket.t_dtyp = NODE_COMMAND;
572     faket.t_dflg = 0;
573     faket.t_dlef = 0;
574     faket.t_drit = 0;
575     faket.t_dspr = 0;
576     faket.t_dcom = fakecom;
577     fakecom[0] = STRfakecom1;
578     fakecom[1] = 0;
579 
580     /*
581      * We do the psave job to temporarily change the current job so that the
582      * following fork is considered a separate job.  This is so that when
583      * backquotes are used in a builtin function that calls glob the "current
584      * job" is not corrupted.  We only need one level of pushed jobs as long as
585      * we are sure to fork here.
586      */
587     psavejob();
588 
589     /*
590      * It would be nicer if we could integrate this redirection more with the
591      * routines in sh.sem.c by doing a fake execute on a builtin function that
592      * was piped out.
593      */
594     mypipe(pvec);
595     if (pfork(&faket, -1) == 0) {
596 	struct wordent paraml;
597 	struct command *t;
598 
599 	(void) close(pvec[0]);
600 	(void) dmove(pvec[1], 1);
601 	(void) dmove(SHDIAG, 2);
602 	initdesc();
603 	/*
604 	 * Bugfix for nested backquotes by Michael Greim <greim@sbsvax.UUCP>,
605 	 * posted to comp.bugs.4bsd 12 Sep. 1989.
606 	 */
607 	if (pargv)		/* mg, 21.dec.88 */
608 	    blkfree(pargv), pargv = 0, pargsiz = 0;
609 	/* mg, 21.dec.88 */
610 	arginp = cp;
611 	while (*cp)
612 	    *cp++ &= TRIM;
613 	(void) lex(&paraml);
614 	if (seterr)
615 	    stderror(ERR_OLD);
616 	alias(&paraml);
617 	t = syntax(paraml.next, &paraml, 0);
618 	if (seterr)
619 	    stderror(ERR_OLD);
620 	if (t)
621 	    t->t_dflg |= F_NOFORK;
622 	(void) signal(SIGTSTP, SIG_IGN);
623 	(void) signal(SIGTTIN, SIG_IGN);
624 	(void) signal(SIGTTOU, SIG_IGN);
625 	execute(t, -1, NULL, NULL);
626 	exitstat();
627     }
628     xfree((ptr_t) cp);
629     (void) close(pvec[1]);
630     c = 0;
631     ip = NULL;
632     do {
633 	int     cnt = 0;
634 
635 	for (;;) {
636 	    if (icnt == 0) {
637 		int     i;
638 
639 		ip = ibuf;
640 		do
641 		    icnt = read(pvec[0], tibuf, BUFSIZ);
642 		while (icnt == -1 && errno == EINTR);
643 		if (icnt <= 0) {
644 		    c = -1;
645 		    break;
646 		}
647 		for (i = 0; i < icnt; i++)
648 		    ip[i] = (unsigned char) tibuf[i];
649 	    }
650 	    if (hadnl)
651 		break;
652 	    --icnt;
653 	    c = (*ip++ & TRIM);
654 	    if (c == 0)
655 		break;
656 	    if (c == '\n') {
657 		/*
658 		 * Continue around the loop one more time, so that we can eat
659 		 * the last newline without terminating this word.
660 		 */
661 		hadnl = 1;
662 		continue;
663 	    }
664 	    if (!quoted && (c == ' ' || c == '\t'))
665 		break;
666 	    cnt++;
667 	    psave(c | quoted);
668 	}
669 	/*
670 	 * Unless at end-of-file, we will form a new word here if there were
671 	 * characters in the word, or in any case when we take text literally.
672 	 * If we didn't make empty words here when literal was set then we
673 	 * would lose blank lines.
674 	 */
675 	if (c != -1 && (cnt || literal))
676 	    pword();
677 	hadnl = 0;
678     } while (c >= 0);
679     (void) close(pvec[0]);
680     pwait();
681     prestjob();
682 }
683 
684 static void
685 psave(c)
686     int    c;
687 {
688     if (--pnleft <= 0)
689 	stderror(ERR_WTOOLONG);
690     *pargcp++ = c;
691 }
692 
693 static void
694 pword()
695 {
696     psave(0);
697     if (pargc == pargsiz - 1) {
698 	pargsiz += GLOBSPACE;
699 	pargv = (Char **) xrealloc((ptr_t) pargv,
700 				   (size_t) pargsiz * sizeof(Char *));
701     }
702     pargv[pargc++] = Strsave(pargs);
703     pargv[pargc] = NULL;
704     pargcp = pargs;
705     pnleft = MAXPATHLEN - 4;
706 }
707 
708 int
709 Gmatch(string, pattern)
710     register Char *string, *pattern;
711 {
712     register Char stringc, patternc;
713     int     match;
714     Char    rangec;
715 
716     for (;; ++string) {
717 	stringc = *string & TRIM;
718 	patternc = *pattern++;
719 	switch (patternc) {
720 	case 0:
721 	    return (stringc == 0);
722 	case '?':
723 	    if (stringc == 0)
724 		return (0);
725 	    break;
726 	case '*':
727 	    if (!*pattern)
728 		return (1);
729 	    while (*string)
730 		if (Gmatch(string++, pattern))
731 		    return (1);
732 	    return (0);
733 	case '[':
734 	    match = 0;
735 	    while (rangec = *pattern++) {
736 		if (rangec == ']')
737 		    if (match)
738 			break;
739 		    else
740 			return (0);
741 		if (match)
742 		    continue;
743 		if (rangec == '-' && *(pattern - 2) != '[' && *pattern != ']') {
744 		    match = (stringc <= (*pattern & TRIM) &&
745 			     (*(pattern - 2) & TRIM) <= stringc);
746 		    pattern++;
747 		}
748 		else
749 		    match = (stringc == rangec);
750 	    }
751 	    if (rangec == 0)
752 		stderror(ERR_NAME | ERR_MISSING, ']');
753 	    break;
754 	default:
755 	    if ((patternc & TRIM) != stringc)
756 		return (0);
757 	    break;
758 
759 	}
760     }
761 }
762 
763 void
764 Gcat(s1, s2)
765     Char   *s1, *s2;
766 {
767     register Char *p, *q;
768     int     n;
769 
770     for (p = s1; *p++;);
771     for (q = s2; *q++;);
772     n = (p - s1) + (q - s2) - 1;
773     if (++gargc >= gargsiz) {
774 	gargsiz += GLOBSPACE;
775 	gargv = (Char **) xrealloc((ptr_t) gargv,
776 				   (size_t) gargsiz * sizeof(Char *));
777     }
778     gargv[gargc] = 0;
779     p = gargv[gargc - 1] = (Char *) xmalloc((size_t) n * sizeof(Char));
780     for (q = s1; *p++ = *q++;);
781     for (p--, q = s2; *p++ = *q++;);
782 }
783 
784 #ifdef FILEC
785 int
786 sortscmp(a, b)
787     register Char **a, **b;
788 {
789 #if defined(NLS) && !defined(NOSTRCOLL)
790     char    buf[2048];
791 
792 #endif
793 
794     if (!a)			/* check for NULL */
795 	return (b ? 1 : 0);
796     if (!b)
797 	return (-1);
798 
799     if (!*a)			/* check for NULL */
800 	return (*b ? 1 : 0);
801     if (!*b)
802 	return (-1);
803 
804 #if defined(NLS) && !defined(NOSTRCOLL)
805     (void) strcpy(buf, short2str(*a));
806     return ((int) strcoll(buf, short2str(*b)));
807 #else
808     return ((int) Strcmp(*a, *b));
809 #endif
810 }
811 #endif /* FILEC */
812