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
45static int noglob;
46static 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
63Char  **gargv = NULL;
64long    gargc = 0;
65Char  **pargv = NULL;
66long    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 */
77static Char	*globtilde(Char **, Char *);
78static Char	**libglob(Char **);
79static Char	**globexpand(Char **);
80static int	globbrace(Char *, Char *, Char ***);
81static void	expbrace(Char ***, Char ***, int);
82static int	pmatch(Char *, Char *);
83static void	pword(void);
84static void	psave(int);
85static void	backeval(Char *, bool);
86
87
88static Char *
89globtilde(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
117static int
118globbrace(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
207static void
208expbrace(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
270static Char **
271globexpand(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
331static Char *
332handleone(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
366static Char **
367libglob(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
411Char   *
412globone(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
470Char  **
471globall(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
504void
505ginit(void)
506{
507    gargsiz = GLOBSPACE;
508    gargv = xreallocarray(NULL, gargsiz, sizeof(Char *));
509    gargv[0] = 0;
510    gargc = 0;
511}
512
513void
514rscan(Char **t, void (*f)(int))
515{
516    Char *p;
517
518    while ((p = *t++) != NULL)
519	while (*p)
520	    (*f) (*p++);
521}
522
523void
524trim(Char **t)
525{
526    Char *p;
527
528    while ((p = *t++) != NULL)
529	while (*p)
530	    *p++ &= TRIM;
531}
532
533void
534tglob(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 */
575Char  **
576dobackp(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
613static void
614backeval(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
749static void
750psave(int c)
751{
752    if (--pnleft <= 0)
753	stderror(ERR_WTOOLONG);
754    *pargcp++ = c;
755}
756
757static void
758pword(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
771int
772Gmatch(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
795static int
796pmatch(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
850void
851Gcat(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
873int
874sortscmp(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