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