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