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