#include "sh.h"
RCSID("$Id: sh.glob.c,v 3.62 2004/12/25 21:15:07 christos Exp $")
#include "tc.h"
#include "tw.h"
#include "glob.h"
static int noglob;
static int pargsiz, gargsiz;
#define G_NONE 0
#define G_GLOB 1
#define G_CSH 2
#define GLOBSPACE 100
#define LONGBSIZE 10240
#define LBRC '{'
#define RBRC '}'
#define LBRK '['
#define RBRK ']'
#define EOS '\0'
Char **gargv = NULL;
int gargc = 0;
Char **pargv = NULL;
static int pargc = 0;
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 void pword __P((int));
static void psave __P((Char));
static void backeval __P((Char *, int));
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;
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 != '/')
return old;
}
else
return old;
if (!getstakd(new, dig))
return NULL;
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;
for (lm = gbuf, p = s; *p != LBRC; *lm++ = *p++)
continue;
for (i = 0, pe = ++p; *pe; pe++)
if (*pe == LBRK) {
for (++pe; *pe != RBRK && *pe != EOS; pe++)
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++)
switch (*pm) {
case LBRK:
for (++pm; *pm != RBRK && *pm != EOS; pm++)
continue;
if (*pm == EOS) {
*vl = NULL;
blkfree(nv);
return (-RBRK);
}
break;
case LBRC:
i++;
break;
case RBRC:
if (i) {
i--;
break;
}
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;
if (s[0] == '{' && (s[1] == '\0' || (s[1] == '}' && s[2] == '\0')))
continue;
if ((b = Strchr(s, '{')) != NULL) {
Char **bl;
int len;
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;
}
vp = vl--;
*vp = *bl;
len--;
for (bp = el; bp != vp; bp--)
bp[len] = *bp;
el += len;
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;
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);
el = vl;
expbrace(&nv, &el, size);
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)) {
blkfree(nv);
stderror(ERR_DEEP);
}
}
if (ns && ns != s) {
xfree((ptr_t) s);
*vl = Strsave(gp);
}
break;
default:
break;
}
vl = nv;
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);
case GLOB_NOSPACE:
globfree(&globv);
stderror(ERR_NOMEM);
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) {
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)
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)
Char **t;
void (*f) __P((Char));
{
Char *p;
while ((p = *t++) != '\0')
while (*p)
(*f) (*p++);
}
void
trim(t)
Char **t;
{
Char *p;
while ((p = *t++) != '\0')
while (*p)
*p++ &= TRIM;
}
void
tglob(t)
Char **t;
{
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;
while ( *(c = p) != '\0') {
p++;
if (*c == '`') {
gflag |= G_CSH;
#ifdef notdef
while (*p && *p != '`')
if (*p++ == '\\') {
if (*p)
p++;
else
break;
}
if (*p)
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;
}
}
}
Char **
dobackp(cp, literal)
Char *cp;
int literal;
{
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 (;;) {
for (lp = cp; *lp != '`'; lp++) {
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;
int literal;
{
int icnt;
Char c, *ip;
struct command faket;
int 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;
psavejob();
mypipe(pvec);
if (pfork(&faket, -1) == 0) {
jmp_buf_t osetexit;
struct command *volatile t;
(void) close(pvec[0]);
(void) dmove(pvec[1], 1);
(void) dmove(SHDIAG, 2);
initdesc();
closem();
if (pargv)
blkfree(pargv), pargv = 0, pargsiz = 0;
arginp = cp;
for (arginp = cp; *cp; cp++) {
*cp &= TRIM;
if (is_set(STRcsubstnonl) && (*cp == '\n' || *cp == '\r'))
*cp = ' ';
}
alvec = NULL;
evalvec = NULL;
alvecp = NULL;
evalp = NULL;
t = NULL;
getexit(osetexit);
for (;;) {
if (paraml.next && paraml.next != ¶ml)
freelex(¶ml);
paraml.next = paraml.prev = ¶ml;
paraml.word = STRNULL;
(void) setexit();
justpr = 0;
freelex(¶ml);
if (t)
freesyn(t), t = NULL;
if (haderr) {
doneinp = 0;
resexit(osetexit);
reset();
}
if (seterr) {
xfree((ptr_t) seterr);
seterr = NULL;
}
(void) lex(¶ml);
if (seterr)
stderror(ERR_OLD);
alias(¶ml);
t = syntax(paraml.next, ¶ml, 0);
if (seterr)
stderror(ERR_OLD);
#ifdef SIGTSTP
(void) sigignore(SIGTSTP);
#endif
#ifdef SIGTTIN
(void) sigignore(SIGTTIN);
#endif
#ifdef SIGTTOU
(void) sigignore(SIGTTOU);
#endif
execute(t, -1, NULL, NULL, TRUE);
freelex(¶ml);
freesyn(t), t = NULL;
}
}
xfree((ptr_t) cp);
(void) close(pvec[1]);
c = 0;
ip = NULL;
do {
int cnt = 0;
char *tmp;
tmp = tibuf;
for (;;) {
while (icnt == 0) {
int i, eof;
ip = ibuf;
do
icnt = read(pvec[0], tmp, tibuf + BUFSIZE - tmp);
while (icnt == -1 && errno == EINTR);
eof = 0;
if (icnt <= 0) {
if (tmp == tibuf)
goto eof;
icnt = 0;
eof = 1;
}
icnt += tmp - tibuf;
i = 0;
tmp = tibuf;
while (tmp < tibuf + icnt) {
int len;
len = normal_mbtowc(&ip[i], tmp, tibuf + icnt - tmp);
if (len == -1) {
reset_mbtowc();
if (!eof && (size_t)(tibuf + icnt - tmp) < MB_CUR_MAX) {
break;
}
ip[i] = (unsigned char) *tmp | INVALID_BYTE;
}
if (len <= 0)
len = 1;
i++;
tmp += len;
}
if (tmp != tibuf)
memmove (tibuf, tmp, tibuf + icnt - tmp);
tmp = tibuf + (tibuf + icnt - tmp);
icnt = i;
}
if (hadnl)
break;
--icnt;
c = (*ip++ & TRIM);
if (c == 0)
break;
#ifdef WINNT_NATIVE
if (c == '\r')
c = ' ';
#endif
if (c == '\n') {
hadnl = 1;
continue;
}
if (!quoted && (c == ' ' || c == '\t'))
break;
cnt++;
psave(c | quoted);
}
if (c != 0 && (cnt || literal))
pword(BUFSIZE);
hadnl = 0;
} while (c > 0);
eof:
(void) close(pvec[0]);
pwait();
prestjob();
}
static void
psave(c)
Char 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 *)));
}
NLSQuote(pargs);
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)
for (p = blk; *p; p++)
gres |= t_pmatch(string, *p, &tstring, 1) == 2 ? 1 : 0;
else {
int minc = 0x7fffffff;
for (p = blk; *p; p++)
if (t_pmatch(string, *p, &tstring, 1) != 0) {
int t = (int) (tstring - string);
gres |= 1;
if (minc == -1 || minc > t)
minc = t;
}
*endstr = string + minc;
}
blkfree(blk);
return(gres == gpol);
}
int
t_pmatch(string, pattern, estr, cs)
Char *string, *pattern, **estr;
int cs;
{
NLSChar stringc, patternc, rangec;
int match, negate_range;
Char *oestr, *pestr, *nstring;
for (nstring = string;; string = nstring) {
stringc = *nstring++;
TRIM_AND_EXTEND(nstring, stringc);
patternc = *pattern++;
TRIM_AND_EXTEND(pattern, patternc);
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;
for (;;) {
switch(t_pmatch(string, pattern, estr, cs)) {
case 0:
break;
case 1:
pestr = *estr;
break;
case 2:
return 2;
default:
abort();
}
*estr = string;
stringc = *string++;
if (!stringc)
break;
TRIM_AND_EXTEND(string, stringc);
}
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;
TRIM_AND_EXTEND(pattern, rangec);
if (*pattern == '-' && pattern[1] != ']') {
NLSChar rangec2;
pattern++;
rangec2 = *pattern++;
TRIM_AND_EXTEND(pattern, rangec2);
match = (globcharcoll(stringc, rangec2, 0) <= 0 &&
globcharcoll(rangec, stringc, 0) <= 0);
}
else
match = (stringc == rangec);
}
if (rangec == '\0')
stderror(ERR_NAME | ERR_MISSING, ']');
if ((!match) && (stringc == '\0'))
return (0);
if (match == negate_range)
return (0);
*estr = string;
break;
default:
TRIM_AND_EXTEND(pattern, patternc);
if (cs ? patternc != stringc
#if defined (NLS) && defined (SHORT_STRINGS)
: towlower(patternc) != towlower(stringc))
#else
: Tolower(patternc) != Tolower(stringc))
#endif
return (0);
*estr = string;
break;
}
}
}
void
Gcat(s1, s2)
Char *s1, *s2;
{
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)
Char **a, **b;
{
if (!a)
return (b ? 1 : 0);
if (!b)
return (-1);
if (!*a)
return (*b ? 1 : 0);
if (!*b)
return (-1);
return (int) collate(*a, *b);
}
#endif