/* $Header: /src/pub/tcsh/sh.glob.c,v 3.54 2002/07/04 19:28:29 christos Exp $ */ /* * sh.glob.c: Regular expression expansion */ /*- * Copyright (c) 1980, 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "sh.h" RCSID("$Id: sh.glob.c,v 3.54 2002/07/04 19:28:29 christos Exp $") #include "tc.h" #include "tw.h" #include "glob.h" static int noglob; static int pargsiz, gargsiz; /* * Values for gflag */ #define G_NONE 0 /* No globbing needed */ #define G_GLOB 1 /* string contains *?[] characters */ #define G_CSH 2 /* string contains ~`{ characters */ #define GLOBSPACE 100 /* Alloc increment */ #define LONGBSIZE 10240 /* Backquote expansion buffer size */ #define LBRC '{' #define RBRC '}' #define LBRK '[' #define RBRK ']' #define EOS '\0' Char **gargv = NULL; int gargc = 0; Char **pargv = NULL; static int pargc = 0; /* * globbing is now done in two stages. In the first pass we expand * csh globbing idioms ~`{ and then we proceed doing the normal * globbing if needed ?*[ * * Csh type globbing is handled in globexpand() and the rest is * handled in glob() which is part of the 4.4BSD libc. * */ static Char *globtilde __P((Char **, Char *)); static Char *handleone __P((Char *, Char **, int)); static Char **libglob __P((Char **)); static Char **globexpand __P((Char **)); static int globbrace __P((Char *, Char *, Char ***)); static void expbrace __P((Char ***, Char ***, int)); static int pmatch __P((Char *, Char *, Char **)); static void pword __P((int)); static void psave __P((int)); static void backeval __P((Char *, bool)); static Char * globtilde(nv, s) Char **nv, *s; { Char gbuf[BUFSIZE], *gstart, *b, *u, *e; #ifdef apollo int slash; #endif gstart = gbuf; *gstart++ = *s++; u = s; for (b = gstart, e = &gbuf[BUFSIZE - 1]; *s && *s != '/' && *s != ':' && b < e; *b++ = *s++) continue; *b = EOS; if (gethdir(gstart)) { if (adrof(STRnonomatch)) return (--u); blkfree(nv); if (*gstart) stderror(ERR_UNKUSER, short2str(gstart)); else stderror(ERR_NOHOME); } b = &gstart[Strlen(gstart)]; #ifdef apollo slash = gstart[0] == '/' && gstart[1] == '\0'; #endif while (*s) *b++ = *s++; *b = EOS; --u; xfree((ptr_t) u); #ifdef apollo if (slash && gstart[1] == '/') gstart++; #endif return (Strsave(gstart)); } Char * globequal(new, old) Char *new, *old; { int dig; Char *b, *d; /* * kfk - 17 Jan 1984 - stack hack allows user to get at arbitrary dir names * in stack. PWP: let =foobar pass through (for X windows) */ if (old[1] == '-' && (old[2] == '\0' || old[2] == '/')) { /* =- */ dig = -1; b = &old[2]; } else if (Isdigit(old[1])) { /* = */ dig = old[1] - '0'; for (b = &old[2]; Isdigit(*b); b++) dig = dig * 10 + (*b - '0'); if (*b != '\0' && *b != '/') /* =foobar */ return old; } else /* =foobar */ return old; if (!getstakd(new, dig)) return NULL; /* Copy the rest of the string */ for (d = &new[Strlen(new)]; d < &new[BUFSIZE - 1] && (*d++ = *b++) != '\0';) continue; *d = '\0'; return new; } static int globbrace(s, p, bl) Char *s, *p, ***bl; { int i, len; Char *pm, *pe, *lm, *pl; Char **nv, **vl; Char gbuf[BUFSIZE]; int size = GLOBSPACE; nv = vl = (Char **) xmalloc((size_t) (sizeof(Char *) * size)); *vl = NULL; len = 0; /* copy part up to the brace */ for (lm = gbuf, p = s; *p != LBRC; *lm++ = *p++) continue; /* check for balanced braces */ for (i = 0, pe = ++p; *pe; pe++) #ifdef DSPMBYTE if (Ismbyte1(*pe) && *(pe + 1) != EOS) pe ++; else #endif /* DSPMBYTE */ if (*pe == LBRK) { /* Ignore everything between [] */ for (++pe; *pe != RBRK && *pe != EOS; pe++) #ifdef DSPMBYTE if (Ismbyte1(*pe) && *(pe + 1) != EOS) pe ++; else #endif /* DSPMBYTE */ continue; if (*pe == EOS) { blkfree(nv); return (-RBRK); } } else if (*pe == LBRC) i++; else if (*pe == RBRC) { if (i == 0) break; i--; } if (i != 0 || *pe == '\0') { blkfree(nv); return (-RBRC); } for (i = 0, pl = pm = p; pm <= pe; pm++) #ifdef DSPMBYTE if (Ismbyte1(*pm) && pm + 1 <= pe) pm ++; else #endif /* DSPMBYTE */ switch (*pm) { case LBRK: for (++pm; *pm != RBRK && *pm != EOS; pm++) #ifdef DSPMBYTE if (Ismbyte1(*pm) && *(pm + 1) != EOS) pm ++; else #endif /* DSPMBYTE */ continue; if (*pm == EOS) { *vl = NULL; blkfree(nv); return (-RBRK); } break; case LBRC: i++; break; case RBRC: if (i) { i--; break; } /* FALLTHROUGH */ case ',': if (i && *pm == ',') break; else { Char savec = *pm; *pm = EOS; (void) Strcpy(lm, pl); (void) Strcat(gbuf, pe + 1); *pm = savec; *vl++ = Strsave(gbuf); len++; pl = pm + 1; if (vl == &nv[size]) { size += GLOBSPACE; nv = (Char **) xrealloc((ptr_t) nv, (size_t) (size * sizeof(Char *))); vl = &nv[size - GLOBSPACE]; } } break; default: break; } *vl = NULL; *bl = nv; return (len); } static void expbrace(nvp, elp, size) Char ***nvp, ***elp; int size; { Char **vl, **el, **nv, *s; vl = nv = *nvp; if (elp != NULL) el = *elp; else for (el = vl; *el; el++) continue; for (s = *vl; s; s = *++vl) { Char *b; Char **vp, **bp; /* leave {} untouched for find */ if (s[0] == '{' && (s[1] == '\0' || (s[1] == '}' && s[2] == '\0'))) continue; if ((b = Strchr(s, '{')) != NULL) { Char **bl; int len; #if defined (DSPMBYTE) if (b != s && Ismbyte2(*b) && Ismbyte1(*(b-1))) { /* The "{" is the 2nd byte of a MB character */ continue; } #endif /* DSPMBYTE */ if ((len = globbrace(s, b, &bl)) < 0) { xfree((ptr_t) nv); stderror(ERR_MISSING, -len); } xfree((ptr_t) s); if (len == 1) { *vl-- = *bl; xfree((ptr_t) bl); continue; } if (&el[len] >= &nv[size]) { int l, e; l = (int) (&el[len] - &nv[size]); size += GLOBSPACE > l ? GLOBSPACE : l; l = (int) (vl - nv); e = (int) (el - nv); nv = (Char **) xrealloc((ptr_t) nv, (size_t) (size * sizeof(Char *))); vl = nv + l; el = nv + e; } /* nv vl el bl * | | | | * -.--..-- x-- * | len * vp */ vp = vl--; *vp = *bl; len--; for (bp = el; bp != vp; bp--) bp[len] = *bp; el += len; /* nv vl el bl * | | | | * -.-x --- -- * |len * vp */ vp++; for (bp = bl + 1; *bp; *vp++ = *bp++) continue; xfree((ptr_t) bl); } } if (elp != NULL) *elp = el; *nvp = nv; } static Char ** globexpand(v) Char **v; { Char *s; Char **nv, **vl, **el; int size = GLOBSPACE; nv = vl = (Char **) xmalloc((size_t) (sizeof(Char *) * size)); *vl = NULL; /* * Step 1: expand backquotes. */ while ((s = *v++) != '\0') { if (Strchr(s, '`')) { int i; (void) dobackp(s, 0); for (i = 0; i < pargc; i++) { *vl++ = pargv[i]; if (vl == &nv[size]) { size += GLOBSPACE; nv = (Char **) xrealloc((ptr_t) nv, (size_t) (size * sizeof(Char *))); vl = &nv[size - GLOBSPACE]; } } xfree((ptr_t) pargv); pargv = NULL; } else { *vl++ = Strsave(s); if (vl == &nv[size]) { size += GLOBSPACE; nv = (Char **) xrealloc((ptr_t) nv, (size_t) (size * sizeof(Char *))); vl = &nv[size - GLOBSPACE]; } } } *vl = NULL; if (noglob) return (nv); /* * Step 2: expand braces */ el = vl; expbrace(&nv, &el, size); /* * Step 3: expand ~ = */ vl = nv; for (s = *vl; s; s = *++vl) switch (*s) { Char gp[BUFSIZE], *ns; case '~': *vl = globtilde(nv, s); break; case '=': if ((ns = globequal(gp, s)) == NULL) { if (!adrof(STRnonomatch)) { /* Error */ blkfree(nv); stderror(ERR_DEEP); } } if (ns && ns != s) { /* Expansion succeeded */ xfree((ptr_t) s); *vl = Strsave(gp); } break; default: break; } vl = nv; /* * Step 4: expand .. if the variable symlinks==expand is set */ if (symlinks == SYM_EXPAND) { for (s = *vl; s; s = *++vl) { *vl = dnormalize(s, 1); xfree((ptr_t) s); } } vl = nv; return (vl); } static Char * handleone(str, vl, action) Char *str, **vl; int action; { Char **vlp = vl; int chars; Char **t, *p, *strp; switch (action) { case G_ERROR: setname(short2str(str)); blkfree(vl); stderror(ERR_NAME | ERR_AMBIG); break; case G_APPEND: chars = 0; for (t = vlp; (p = *t++) != '\0'; chars++) while (*p++) chars++; str = (Char *)xmalloc((size_t)(chars * sizeof(Char))); for (t = vlp, strp = str; (p = *t++) != '\0'; chars++) { while (*p) *strp++ = *p++ & TRIM; *strp++ = ' '; } *--strp = '\0'; blkfree(vl); break; case G_IGNORE: str = Strsave(strip(*vlp)); blkfree(vl); break; default: break; } return (str); } static Char ** libglob(vl) Char **vl; { int gflgs = GLOB_QUOTE | GLOB_NOMAGIC | GLOB_ALTNOT; glob_t globv; char *ptr; int nonomatch = adrof(STRnonomatch) != 0, magic = 0, match = 0; if (!vl || !vl[0]) return(vl); globv.gl_offs = 0; globv.gl_pathv = 0; globv.gl_pathc = 0; if (nonomatch) gflgs |= GLOB_NOCHECK; do { ptr = short2qstr(*vl); switch (glob(ptr, gflgs, 0, &globv)) { case GLOB_ABEND: globfree(&globv); setname(ptr); stderror(ERR_NAME | ERR_GLOB); /* NOTREACHED */ case GLOB_NOSPACE: globfree(&globv); stderror(ERR_NOMEM); /* NOTREACHED */ default: break; } if (globv.gl_flags & GLOB_MAGCHAR) { match |= (globv.gl_matchc != 0); magic = 1; } gflgs |= GLOB_APPEND; } while (*++vl); vl = (globv.gl_pathc == 0 || (magic && !match && !nonomatch)) ? NULL : blk2short(globv.gl_pathv); globfree(&globv); return (vl); } Char * globone(str, action) Char *str; int action; { Char *v[2], **vl, **vo; int gflg; noglob = adrof(STRnoglob) != 0; gflag = 0; v[0] = str; v[1] = 0; tglob(v); gflg = gflag; if (gflg == G_NONE) return (strip(Strsave(str))); if (gflg & G_CSH) { /* * Expand back-quote, tilde and brace */ vo = globexpand(v); if (noglob || (gflg & G_GLOB) == 0) { if (vo[0] == NULL) { xfree((ptr_t) vo); return (Strsave(STRNULL)); } if (vo[1] != NULL) return (handleone(str, vo, action)); else { str = strip(vo[0]); xfree((ptr_t) vo); return (str); } } } else if (noglob || (gflg & G_GLOB) == 0) return (strip(Strsave(str))); else vo = v; vl = libglob(vo); if ((gflg & G_CSH) && vl != vo) blkfree(vo); if (vl == NULL) { setname(short2str(str)); stderror(ERR_NAME | ERR_NOMATCH); } if (vl[0] == NULL) { xfree((ptr_t) vl); return (Strsave(STRNULL)); } if (vl[1]) return (handleone(str, vl, action)); else { str = strip(*vl); xfree((ptr_t) vl); return (str); } } Char ** globall(v) Char **v; { Char **vl, **vo; int gflg = gflag; if (!v || !v[0]) { gargv = saveblk(v); gargc = blklen(gargv); return (gargv); } noglob = adrof(STRnoglob) != 0; if (gflg & G_CSH) /* * Expand back-quote, tilde and brace */ vl = vo = globexpand(v); else vl = vo = saveblk(v); if (!noglob && (gflg & G_GLOB)) { vl = libglob(vo); if (vl != vo) blkfree(vo); } else trim(vl); gargc = vl ? blklen(vl) : 0; return (gargv = vl); } void ginit() { gargsiz = GLOBSPACE; gargv = (Char **) xmalloc((size_t) (sizeof(Char *) * gargsiz)); gargv[0] = 0; gargc = 0; } void rscan(t, f) register Char **t; void (*f) __P((int)); { register Char *p; while ((p = *t++) != '\0') while (*p) (*f) (*p++); } void trim(t) register Char **t; { register Char *p; while ((p = *t++) != '\0') while (*p) *p++ &= TRIM; } void tglob(t) register Char **t; { register Char *p, *c; while ((p = *t++) != '\0') { if (*p == '~' || *p == '=') gflag |= G_CSH; else if (*p == '{' && (p[1] == '\0' || (p[1] == '}' && p[2] == '\0'))) continue; /* * The following line used to be *(c = p++), but hp broke their * optimizer in 9.01, so we break the assignment into two pieces * The careful reader here will note that *most* compiler workarounds * in tcsh are either for apollo/DomainOS or hpux. Is it a coincidence? */ while ( *(c = p) != '\0') { p++; if (*c == '`') { gflag |= G_CSH; #ifdef notdef /* * We do want to expand echo `echo '*'`, so we don't\ * use this piece of code anymore. */ while (*p && *p != '`') if (*p++ == '\\') { if (*p) /* Quoted chars */ p++; else break; } if (*p) /* The matching ` */ p++; else break; #endif } else if (*c == '{') gflag |= G_CSH; else if (isglob(*c)) gflag |= G_GLOB; else if (symlinks == SYM_EXPAND && *p && ISDOTDOT(c) && (c == *(t-1) || *(c-1) == '/') ) gflag |= G_CSH; } } } /* * Command substitute cp. If literal, then this is a substitution from a * << redirection, and so we should not crunch blanks and tabs, separating * words only at newlines. */ Char ** dobackp(cp, literal) Char *cp; bool literal; { register Char *lp, *rp; Char *ep, word[LONGBSIZE]; if (pargv) { #ifdef notdef abort(); #endif blkfree(pargv); } pargsiz = GLOBSPACE; pargv = (Char **) xmalloc((size_t) (sizeof(Char *) * pargsiz)); pargv[0] = NULL; pargcp = pargs = word; pargc = 0; pnleft = LONGBSIZE - 4; for (;;) { #if defined(DSPMBYTE) for (lp = cp;; lp++) { /* } */ if (*lp == '`' && (lp-1 < cp || !Ismbyte2(*lp) || !Ismbyte1(*(lp-1)))) { break; } #else /* DSPMBYTE */ for (lp = cp; *lp != '`'; lp++) { #endif /* DSPMBYTE */ if (*lp == 0) { if (pargcp != pargs) pword(LONGBSIZE); return (pargv); } psave(*lp); } lp++; for (rp = lp; *rp && *rp != '`'; rp++) if (*rp == '\\') { rp++; if (!*rp) goto oops; } if (!*rp) oops: stderror(ERR_UNMATCHED, '`'); ep = Strsave(lp); ep[rp - lp] = 0; backeval(ep, literal); cp = rp + 1; } } static void backeval(cp, literal) Char *cp; bool literal; { register int icnt, c; register Char *ip; struct command faket; bool hadnl; int pvec[2], quoted; Char *fakecom[2], ibuf[BUFSIZE]; char tibuf[BUFSIZE]; hadnl = 0; icnt = 0; quoted = (literal || (cp[0] & QUOTE)) ? QUOTE : 0; faket.t_dtyp = NODE_COMMAND; faket.t_dflg = F_BACKQ; faket.t_dlef = 0; faket.t_drit = 0; faket.t_dspr = 0; faket.t_dcom = fakecom; fakecom[0] = STRfakecom1; fakecom[1] = 0; /* * We do the psave job to temporarily change the current job so that the * following fork is considered a separate job. This is so that when * backquotes are used in a builtin function that calls glob the "current * job" is not corrupted. We only need one level of pushed jobs as long as * we are sure to fork here. */ psavejob(); /* * It would be nicer if we could integrate this redirection more with the * routines in sh.sem.c by doing a fake execute on a builtin function that * was piped out. */ mypipe(pvec); if (pfork(&faket, -1) == 0) { struct command *t; (void) close(pvec[0]); (void) dmove(pvec[1], 1); (void) dmove(SHDIAG, 2); initdesc(); closem(); /* * Bugfix for nested backquotes by Michael Greim , * posted to comp.bugs.4bsd 12 Sep. 1989. */ if (pargv) /* mg, 21.dec.88 */ blkfree(pargv), pargv = 0, pargsiz = 0; /* mg, 21.dec.88 */ arginp = cp; for (arginp = cp; *cp; cp++) { *cp &= TRIM; if (*cp == '\n' || *cp == '\r') *cp = ' '; } /* * In the child ``forget'' everything about current aliases or * eval vectors. */ alvec = NULL; evalvec = NULL; alvecp = NULL; evalp = NULL; (void) lex(¶ml); if (seterr) stderror(ERR_OLD); alias(¶ml); t = syntax(paraml.next, ¶ml, 0); if (seterr) stderror(ERR_OLD); if (t) t->t_dflg |= F_NOFORK; #ifdef SIGTSTP (void) sigignore(SIGTSTP); #endif #ifdef SIGTTIN (void) sigignore(SIGTTIN); #endif #ifdef SIGTTOU (void) sigignore(SIGTTOU); #endif execute(t, -1, NULL, NULL, TRUE); exitstat(); } xfree((ptr_t) cp); (void) close(pvec[1]); c = 0; ip = NULL; do { int cnt = 0; for (;;) { if (icnt == 0) { int i; ip = ibuf; do icnt = read(pvec[0], tibuf, BUFSIZE); while (icnt == -1 && errno == EINTR); if (icnt <= 0) { c = -1; break; } for (i = 0; i < icnt; i++) ip[i] = (unsigned char) tibuf[i]; } if (hadnl) break; --icnt; c = (*ip++ & TRIM); if (c == 0) break; #ifdef WINNT_NATIVE if (c == '\r') c = ' '; #endif /* WINNT_NATIVE */ if (c == '\n') { /* * Continue around the loop one more time, so that we can eat * the last newline without terminating this word. */ hadnl = 1; continue; } if (!quoted && (c == ' ' || c == '\t')) break; cnt++; psave(c | quoted); } /* * Unless at end-of-file, we will form a new word here if there were * characters in the word, or in any case when we take text literally. * If we didn't make empty words here when literal was set then we * would lose blank lines. */ if (c != -1 && (cnt || literal)) pword(BUFSIZE); hadnl = 0; } while (c >= 0); (void) close(pvec[0]); pwait(); prestjob(); } static void psave(c) int c; { if (--pnleft <= 0) stderror(ERR_WTOOLONG); *pargcp++ = (Char) c; } static void pword(bufsiz) int bufsiz; { psave(0); if (pargc == pargsiz - 1) { pargsiz += GLOBSPACE; pargv = (Char **) xrealloc((ptr_t) pargv, (size_t) (pargsiz * sizeof(Char *))); } pargv[pargc++] = Strsave(pargs); pargv[pargc] = NULL; pargcp = pargs; pnleft = bufsiz - 4; } int Gmatch(string, pattern) Char *string, *pattern; { return Gnmatch(string, pattern, NULL); } int Gnmatch(string, pattern, endstr) Char *string, *pattern, **endstr; { Char **blk, **p, *tstring = string; int gpol = 1, gres = 0; if (*pattern == '^') { gpol = 0; pattern++; } blk = (Char **) xmalloc((size_t) (GLOBSPACE * sizeof(Char *))); blk[0] = Strsave(pattern); blk[1] = NULL; expbrace(&blk, NULL, GLOBSPACE); if (endstr == NULL) /* Exact matches only */ for (p = blk; *p; p++) gres |= pmatch(string, *p, &tstring) == 2 ? 1 : 0; else { /* partial matches */ int minc = 0x7fffffff; for (p = blk; *p; p++) if (pmatch(string, *p, &tstring) != 0) { int t = (int) (tstring - string); gres |= 1; if (minc == -1 || minc > t) minc = t; } *endstr = string + minc; } blkfree(blk); return(gres == gpol); } /* pmatch(): * Return 2 on exact match, * Return 1 on substring match. * Return 0 on no match. * *estr will point to the end of the longest exact or substring match. */ static int pmatch(string, pattern, estr) register Char *string, *pattern, **estr; { register Char stringc, patternc; int match, negate_range; Char rangec, *oestr, *pestr; for (;; ++string) { stringc = *string & TRIM; /* * apollo compiler bug: switch (patternc = *pattern++) dies */ patternc = *pattern++; switch (patternc) { case 0: *estr = string; return (stringc == 0 ? 2 : 1); case '?': if (stringc == 0) return (0); *estr = string; break; case '*': if (!*pattern) { while (*string) string++; *estr = string; return (2); } oestr = *estr; pestr = NULL; do { switch(pmatch(string, pattern, estr)) { case 0: break; case 1: pestr = *estr; break; case 2: return 2; default: abort(); /* Cannot happen */ } *estr = string; } while (*string++); if (pestr) { *estr = pestr; return 1; } else { *estr = oestr; return 0; } case '[': match = 0; if ((negate_range = (*pattern == '^')) != 0) pattern++; while ((rangec = *pattern++) != '\0') { if (rangec == ']') break; if (match) continue; if (rangec == '-' && *(pattern-2) != '[' && *pattern != ']') { match = (globcharcoll(stringc, *pattern & TRIM) <= 0 && globcharcoll(*(pattern-2) & TRIM, stringc) <= 0); pattern++; } else match = (stringc == (rangec & TRIM)); } if (rangec == 0) stderror(ERR_NAME | ERR_MISSING, ']'); if (match == negate_range) return (0); *estr = string; break; default: if ((patternc & TRIM) != stringc) return (0); *estr = string; break; } } } void Gcat(s1, s2) Char *s1, *s2; { register Char *p, *q; int n; for (p = s1; *p++;) continue; for (q = s2; *q++;) continue; n = (int) ((p - s1) + (q - s2) - 1); if (++gargc >= gargsiz) { gargsiz += GLOBSPACE; gargv = (Char **) xrealloc((ptr_t) gargv, (size_t) (gargsiz * sizeof(Char *))); } gargv[gargc] = 0; p = gargv[gargc - 1] = (Char *) xmalloc((size_t) (n * sizeof(Char))); for (q = s1; (*p++ = *q++) != '\0';) continue; for (p--, q = s2; (*p++ = *q++) != '\0';) continue; } #if defined(FILEC) && defined(TIOCSTI) int sortscmp(a, b) register Char **a, **b; { if (!a) /* check for NULL */ return (b ? 1 : 0); if (!b) return (-1); if (!*a) /* check for NULL */ return (*b ? 1 : 0); if (!*b) return (-1); return (int) collate(*a, *b); } #endif