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