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
35static 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
53static int noglob;
54static 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
71Char **gargv = NULL;
72Char **pargv = NULL;
73long gargc = 0;
74long 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 */
85static Char *globtilde(Char **, Char *);
86static Char *handleone(Char *, Char **, int);
87static Char **libglob(Char **);
88static Char **globexpand(Char **);
89static int globbrace(Char *, Char *, Char ***);
90static void expbrace(Char ***, Char ***, int);
91static int pmatch(Char *, Char *);
92static void pword(void);
93static void psave(int);
94static void backeval(Char *, int);
95
96static Char *
97globtilde(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
125static int
126globbrace(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
213static void
214expbrace(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
276static Char **
277globexpand(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
338static Char *
339handleone(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
372static Char **
373libglob(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
420Char *
421globone(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
479Char  **
480globall(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
514void
515ginit(void)
516{
517    gargsiz = GLOBSPACE;
518    gargv = (Char **)xmalloc((size_t)sizeof(Char *) * gargsiz);
519    gargv[0] = 0;
520    gargc = 0;
521}
522
523void
524rscan(Char **t, void (*f)(int))
525{
526    Char *p;
527
528    while ((p = *t++) != NULL)
529	while (*p)
530	    (*f) (*p++);
531}
532
533void
534trim(Char **t)
535{
536    Char *p;
537
538    while ((p = *t++) != NULL)
539	while (*p)
540	    *p++ &= TRIM;
541}
542
543void
544tglob(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 */
585Char **
586dobackp(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
629static void
630backeval(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
768static void
769psave(int c)
770{
771    if (--pnleft <= 0)
772	stderror(ERR_WTOOLONG);
773    *pargcp++ = c;
774}
775
776static void
777pword(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
791int
792Gmatch(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
818static int
819pmatch(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
872void
873Gcat(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
897int
898sortscmp(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