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