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