expand.c revision 3f54fd611f536639ec30dd53c48e5ec1897cc7d9
/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1984-2012 AT&T Intellectual Property *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* Glenn Fowler
* AT&T Research
*
* make variable expansion routines
*/
#include "make.h"
#include "expand.h"
#include <magic.h>
#include <regex.h>
#define BREAKARGS 100
#define EDITCONTEXT 20
#define SORT_posix 0x0000
#define SORT_collate 0x0002
#define SORT_numeric 0x0004
#define SORT_prefix 0x0006
#define SORT_version 0x0008
#define SORT_invert 0x0001
#define SORT_MASK 0x000f
typedef int (*Cmp_f)(const char*, const char*);
static const regflags_t submap[] =
{
'g', REG_SUB_ALL,
'l', REG_SUB_LOWER,
'u', REG_SUB_UPPER,
'G', REG_SUB_ALL,
'L', REG_SUB_LOWER,
'U', REG_SUB_UPPER,
0, 0
};
/*
* inverted strcmp(3)
*/
static int
istrcmp(const char* a, const char* b)
{
return strcmp(b, a);
}
/*
* inverted strcoll(3)
*/
static int
istrcoll(const char* a, const char* b)
{
return strcoll(b, a);
}
/*
* numeric strcmp(3)
*/
static int
numcmp(const char* a, const char* b)
{
char* ea;
char* eb;
return -1;
return 1;
return 0;
}
/*
* inverted numcmp(3)
*/
static int
inumcmp(const char* a, const char* b)
{
return numcmp(b, a);
}
/*
* inverted strpcmp(3)
*/
static int
istrpcmp(const char* a, const char* b)
{
return strpcmp(b, a);
}
/*
* inverted strvcmp(3)
*/
static int
istrvcmp(const char* a, const char* b)
{
return strvcmp(b, a);
}
{
/* strcoll is an ast macro */ 0,
};
/*
* return sort comparison function for SORT_* flags
*/
static Cmp_f
{
Cmp_f f;
flags &= SORT_invert;
f = strcoll;
return f;
}
/*
* `$(...)' expansion
*
* each <var> is expanded before the value is determined
* each <op> is applied to blank separated tokens in the variable value
* $$(...) expands to $(...) -- one $ gobbled each time
*
* general syntax:
*
* $(<var>[|<var>][:[<pfx>]<op>[<sep><val>]])
*
* top level alternate syntax:
*
* $(<var>[|<var>]`[<del>[<pfx>]<op>[<sep><val>]]<del>)
*
* variable name syntax:
*
* $(<var>) value of <var>
* $(<var1>|<var2>) value of <var1> if non-null else value of <var2>
* $("<string>") <string> is used as the variable value
*
* edit operator syntax:
*
* :<op><sep><value>:
* :/<old>/<new>/<glb>:
* :C<del><old><del><new><del><glb>:
* :?<if-non-null>?<if-null>?:
* :Y<del><if-non-null><del><if-null><del>:
*
*
* edit operator forms:
*
* $(s) simple expansion (multiple character name)
* $(s:@<op>...) don't tokenize list elements for <op>
* $(s:A=a[|b]) list of rules with attribute a [or b ...]
* $(s:F=<fmt>) format components according to <fmt>
* $(s:G=<pat>) select components that build (generate) <pat> files
* $(s:H[>]) sort [hi to lo]
* $(s:I=<lst>) directory intersection of s with list <lst>
* $(s:K=<pfx>) break into `<pfx> s ...' ~ BREAK(ARGS|LINE) line chunks
* $(s:L[=<pat>) list all (viewed) files matching <pat>
* $(s:M[!]=<pat>) select components [not] matching RE pattern <pat>
* $(s:N[!]=<pat>) select components [not] matching shell pattern <pat>
* $(s:O<sep><n>) select components <|<=|==|!=|>=|> <n> starting at 1
* $(s:P=<op>) path name operations (see below)
* $(s:Q) quote shell chars in value
* $(s:R) read value as makefile
* $(s:T=<op>[r]) select components matching rule token <op> (see below)
* $(s:V) do not expand value (prefix)
* $(s:W?[=<arg>]) ops on entire value
* $(s:X=<lst>) directory cross product of components of s and <lst>
* $(s:Z) expand in parent reference frame (prefix cumulative)
* $(s:f) include or modify file name components (see below)
*
* token op components:
*
* A archive (returns archive symbol table update command)
* D definition of state variable: -Dx[=y]
* E alternate definition of state variable: x=[y]
* F file
* G built (generated) file
* I[-] value is [non]expanded contents of bound file
* Q select defined atoms
* QV select defined variables
* P physically a file (not a symbolic link ...)
* R relative time since epoch
* Sc return staterule name given non-state rule
* U return variable or non-state name given state rule
* W component prefix to not wait for bind
* X component prefix to skip binding
* * any
*
* token op forms:
*
* $(s:T=t?ret) ret if true else null
*
* format forms:
*
* L convert to lower case
* U convert to upper case
* %n.nc printf(3) format
*
* path name operations:
*
* A absolute (rooted) path name
* B physical binding
* C canonicalize path name
* D generate directory where s was bound
* I=n files identical to n
* L[=n] return s if bound in view level 0 [n]
* P=l probe info file for language l processor (in token)
* R[=d] relative dir path to d [pwd]
* S atoms bound in subdirectory of view
* U unbound name
* V generate view directory path where s was bound
* X return s if it is an existing file
*
* file name components (any or all may be null):
*
* D directory up to and including the last '/'
* B base after D up to but not including the last '.'
* S suffix after B
*
* a component (DBS) is deleted if the corresponding char is omitted
* a component (DBS) is retained if the corresponding char is specified
* a component (DBS) is changed if the corresponding char is followed
* by '=' and a replacement value
*
* if '=' is specified then a ':' separates the value from the next
* file name component specification
*/
/*
* return edit map for *p delimited by space or del
* 0 for no match otherwise *p points to del or op arg
*/
static Edit_map_t*
{
register int v;
register unsigned char* s;
register unsigned char* t;
{
s = (unsigned char*)*p;
for (;;)
{
{
while (isspace(*s)) s++;
*p = (char*)s;
return mid;
}
if ((v = *s++ - *t++) > 0)
{
break;
}
else if (v < 0)
{
break;
}
}
}
return 0;
}
/*
* expand one instance of v not in w into xp
* (sep & NOT) for file equality
*/
static void
{
register char* s;
char* tok;
{
{
}
sep = 0;
{
{
if (sep)
else
sep = 1;
}
}
}
else
{
sep = 0;
{
if (sep)
else
sep = 1;
}
}
}
/*
* mark r and recursively all prerequisites with m
* prereqs already marked with m are also marked with c
* if c!=0 then c marked prereqs listed in xp
* otherwise m marked prereqs listed in xp
* xp==0 removes marks
*/
static int
{
register List_t* p;
if (xp)
{
if (r->mark & c)
{
return 1;
}
if (!(r->mark & m))
{
r->mark |= m|c;
{
return 1;
}
if (c)
r->mark &= ~c;
else
}
}
else if (r->mark & m)
{
r->mark &= ~(m|c);
}
return 0;
}
/*
* expand closure of v into xp
*/
static void
{
register char* s;
char* tok;
long pos;
int cycle;
}
/*
* expand the directory cross product of v and w into xp
*
* memorize your multiplication table:
*
* . unit multiplication operand
* A absolute path rooted at /
* R path relative to .
*
* lhs rhs cross-product
* ---- ----- -------------
* . . . *note (1)*
* . A A *note (2)*
* . R R
* A . A
* A R A/R
* A A A *note (2)*
* R . R
* R A A *note (2)*
* R R R/R
*
* (1) the first . lhs operand produces a . in the product
*
* (2) the first A rhs operand is placed in the product
*/
static void
{
register char* s;
register char* t;
char* x;
int dot;
int sep;
long pos;
char* tok0;
char* tok1;
sep = 0;
{
if (*t == '/')
{
else sep = 1;
}
else
{
{
else sep = 1;
x = t;
if (dot)
x = s;
else if (*s != '.' || *(s + 1))
}
}
}
}
/*
* expand the pathname intersection of v with w into xp
* sep!=EQ for literal intersection
*/
static void
{
register List_t* p;
register Rule_t* r;
register char* s;
register int n;
List_t* q;
List_t* x;
char* tok;
p = q = 0;
{
canon(s);
r = makerule(s);
s = r->name;
{
{
{
r = makerule(s);
}
}
}
}
for (x = q; x; x = x->next)
{
canon(s);
if (r = getrule(s))
{
{
if (!(r->mark & M_metarule) && r->name[0] == '.' && r->name[1] == '.' && (!r->name[2] || r->name[2] == '/'))
{
r->mark |= M_metarule;
}
{
if (!(r->mark & M_metarule) && r->name[0] == '.' && r->name[1] == '.' && (!r->name[2] || r->name[2] == '/'))
{
r->mark |= M_metarule;
}
}
}
}
}
n = 0;
for (p = q; p; p = p->next)
{
else n = 1;
}
for (p = q; p; p = p->next)
freelist(q);
}
/*
* expand the rules in v that have any of the prereqs in w
*/
static void
{
register List_t* p;
register List_t* q;
register Rule_t* r;
register char* s;
int sep;
List_t* x;
char* tok;
x = 0;
sep = 0;
{
if (r = getrule(s))
for (q = x; q; q = q->next)
{
else sep = 1;
goto next;
}
next:;
}
freelist(x);
}
/*
* break into ~ BREAK(ARGS|LINE) `<pfx> s ...' line chunks
*/
static void
{
register int a;
char* tok;
long rew;
long pre;
long pos;
long brk;
{
{
a = BREAKARGS;
}
}
}
/*
* generate list of hash table names matching pat
*/
static void
{
register char** v;
int n;
{
return;
}
{
{
{
if (n != REG_NOMATCH)
{
break;
}
continue;
}
}
}
/*
* sort and fill the output buffer
*/
{
while (*++v)
}
if (*pat)
}
/*
* generate list of file base names matching pat from all dirs in s
*/
static void
{
register Rule_t* r;
register char** v;
char** w;
File_t* f;
char* tok;
int n;
int ignorecase;
{
return;
}
ignorecase = 0;
/*
* generate and bind (scan) the ordered dir list
*/
{
r = makerule(s);
{
if (flags & SORT_force)
dirscan(r);
}
}
{
*v++ = r->name;
}
/*
* scan the file hash for pattern and dir matches
*/
{
{
{
if (*pat)
{
if (f->dir->ignorecase)
{
if (!ignorecase)
{
{
break;
}
ignorecase = 1;
}
}
else
{
if (n != REG_NOMATCH)
{
break;
}
continue;
}
}
for (; f; f = f->next)
{
goto next;
}
}
next: ;
}
}
/*
* sort and fill the output buffer
*/
{
{
if (flags & SORT_version)
{
{
if (flags & SORT_qualified)
else
if (flags & SORT_first)
goto first;
}
}
else
{
{
if (flags & SORT_qualified)
else
if (flags & SORT_first)
goto first;
}
}
first: ;
}
else
{
while (*++v)
}
}
if (*pat)
{
if (ignorecase)
}
}
/*
* sort list s into xp
*
* NOTE: s modified in place and not restored
*/
static void
{
register char** p;
register char** r;
char* tok;
long n;
{
if (flags & SORT_reverse)
{
r = p + n - 1;
while (--r >= p)
}
else
{
if (!(flags & SORT_first))
{
while (s = *++p)
}
}
}
}
/*
* construct relative path from dir s to t in xp
*/
static void
{
register char* u;
register char* v;
long pos;
while (*s && *s == *t)
if (*s++ == '/')
{
u = s;
v = ++t;
}
else t++;
while (*u)
if (*u++ == '/')
}
/*
* apply binary separator operator to a and b
*/
static int
{
switch (sep)
{
case LT:
return a < b;
case LE:
return a <= b;
case EQ:
return a == b;
case NE:
case NOT:
return a != b;
case GE:
return a >= b;
case GT:
return a > b;
default:
#if DEBUG
#endif
return 0;
}
}
/*
* apply binary separator operator to times a and b
*/
static int
{
switch (sep)
{
case LT:
return a < b;
case LE:
return a <= b;
case EQ:
return a == b;
case NE:
case NOT:
return a != b;
case GE:
return a >= b;
case GT:
return a > b;
default:
#if DEBUG
#endif
return 0;
}
}
/*
* convert path to native representation with '..' quotes if needed
*/
static void
{
size_t m;
size_t n;
if (*s)
{
n = PATH_MAX;
do
{
m = n;
} while (n > m);
}
}
#define ORDER_all 0x01
#define ORDER_directory 0x02
#define ORDER_force 0x04
#define ORDER_implicit 0x08
#define ORDER_paths 0x10
#define ORDER_prereqs 0x20
#define M_INIT M_metarule
#define M_SKIP M_generate
/*
* order_recurse() partial order traversal support
*/
static unsigned long
{
register List_t* p;
register Rule_t* a;
unsigned long here;
unsigned long need;
need = 0;
if (r->prereqs)
{
{
{
a = p->rule;
else if (a == r)
continue;
if (!need)
{
need = 1;
}
}
if (need)
{
need = 0;
}
}
{
a = p->rule;
}
r->prereqs = 0;
}
else if (flags & ORDER_prereqs)
{
return mark;
}
if (!(flags & ORDER_prereqs))
{
return mark;
{
mark++;
}
}
return mark;
}
static void order_find(Sfio_t*, Sfio_t*, Sfio_t*, Hash_table_t*, char*, char*, char*, char*, unsigned int);
static void
order_all(Sfio_t* xp, Sfio_t* tmp, Sfio_t* vec, Hash_table_t* tab, Rule_t* d, char* makefiles, char* skip, unsigned int flags)
{
register char* t;
register char** v;
int n;
while (t = *v++)
{
t[strlen(t) - 1] = 0;
}
}
/*
* scan makefile r for ORDER_RECURSE assertion operators and add to vec
*/
static void
order_scan(Sfio_t* xp, Sfio_t* tmp, Sfio_t* vec, Hash_table_t* tab, Rule_t* d, Rule_t* r, char* makefiles, char* skip, unsigned int flags)
{
register char* s;
else
{
s++;
else
s = d->name;
else if (flags & ORDER_prereqs)
else
{
}
}
{
if (flags & ORDER_implicit)
else
while (*s)
if (*s++ == ':')
{
if (strneq(s, ORDER_RECURSE, sizeof(ORDER_RECURSE) - 1) && *(s += sizeof(ORDER_RECURSE) - 1) == ':')
{
while (*++s && (*s == ' ' || *s == '\t'));
if (*s)
else
}
break;
}
}
}
/*
*/
static void
order_find(Sfio_t* xp, Sfio_t* tmp, Sfio_t* vec, Hash_table_t* tab, char* dir, char* files, char* makefiles, char* skip, unsigned int flags)
{
Rule_t* r;
Rule_t* d;
char* s;
char* t;
char* e;
char* tok;
dir = 0;
{
if (skip)
{
if (t = strrchr(s, '/'))
t++;
else
t = s;
continue;
}
if (dir)
{
}
else if (!(state.questionable & 0x20000000) && !strchr(s, '/') && (t = strrchr(internal.pwd, '/')) && streq(s, t + 1))
continue;
else
t = s;
if ((flags & ORDER_directory) || (flags & (ORDER_force|ORDER_paths)) == ORDER_force && (d = bindfile(NiL, t, 0)) && !stat(d->name, &st) && S_ISDIR(st.st_mode))
{
d = makerule(t);
t = makefiles;
while (t)
{
if (dir)
if (e = strchr(t, ':'))
{
t = e + 1;
}
else
{
t = 0;
}
{
break;
}
}
}
{
if (s = strrchr(t, '/'))
{
*s = 0;
d = makerule(t);
*s = '/';
}
else
}
}
}
/*
* order strsort comparison function
*/
static int
order_cmp(const void* a, const void* b)
{
}
/*
* generate an ordered list of directories in xp based on
* (recursive) makefile prereqs; if targets!=0 then only
* those targets and prerequisites are considered
* directories and targets are ' ' separated
* ORDER_paths for old :W=O: where directories are makefile paths
*/
static void
order_recurse(Sfio_t* xp, char* directories, char* makefiles, char* skip, char* targets, unsigned int flags)
{
char* s;
char* t;
char* u;
char* b;
char* a;
char* z;
char* tok;
char* lib;
Rule_t* r;
Rule_t* d;
Rule_t** v;
Rule_t** e;
List_t* q;
unsigned long mark;
int i;
int j;
int k;
int m;
int p;
int var;
flags |= ORDER_implicit;
while (r = *v++)
{
d = *v++;
{
z = 0;
{
j = p = 0;
b = s;
while (*s)
{
var = 0;
lib = 0;
for (k = 1; (i = *s) == ' ' || i == '\t' || i == '\r' || i == '"' || i == '\''; s++);
m = *s != '-' && *s != '+' || *(s + 1) != 'l';
for (t = s; (i = *s) && i != ' ' && i != '\t' && i != '\r' && i != '"' && i != '\'' && i != '\\' && i != ':'; s++)
if (i == '/' && m)
t = s + 1;
else if (i == '.' && *(s + 1) != 'c' && *(s + 1) != 'C' && *(s + 1) != 'h' && *(s + 1) != 'H' && t[0] == 'l' && t[1] == 'i' && t[2] == 'b')
*s = 0;
else if (i == '$')
var = 1;
if (*s)
*s++ = 0;
if (var)
continue;
if (!t[0])
k = 0;
else if ((t[0] == '-' || t[0] == '+') && t[1] == 'l')
{
a = 0;
*u = 0;
if (!*t)
continue;
if (a)
{
if (!z)
if (*z == '/' && ++m == 2)
{
z++;
break;
}
if (z > d->name)
else
}
else
}
else if (p)
{
if (t[0] == '+' && !t[1])
p = 2;
else if (p == 1)
{
{
if (!order)
}
{
}
if (i == ':')
while (*s && !isspace(*s))
s++;
}
}
else if (i == ':')
{
if (j != ':' || !isupper(*t))
k = 0;
{
for (; *b == ' ' || *b == '\t' || *b == '\r'; b++);
if (!*b || *b == ':' || *b == ' ' || *b == '\t' || *b == '\r')
{
*b = 0;
if (!i)
{
}
}
}
else if (streq(t, ORDER_PACKAGE))
{
p = 1;
k = 0;
}
else if (streq(t, ORDER_RECURSE))
{
p = -1;
k = 0;
}
else
for (u = t; *u; u++)
if (isupper(*u))
*u = tolower(*u);
else if (!isalnum(*u))
{
k = 0;
break;
}
}
else if (t[0] != 'l' || t[1] != 'i' || t[2] != 'b')
k = 0;
else
for (u = t + 3; *u; u++)
if (!isalnum(*u))
{
k = 0;
break;
}
if (k && ((r = (Rule_t*)hashget(tab, t)) && (r->mark & M_MUST) && r != d || *t++ == 'l' && *t++ == 'i' && *t++ == 'b' && *t && (r = (Rule_t*)hashget(tab, t)) && (r->mark & M_MUST) && r != d))
{
t++;
else
t = d->name;
if (t[0] == 'l' && t[1] == 'i' && t[2] == 'b')
t += 3;
u++;
else
u = r->name;
addprereq(d, r, PREREQ_APPEND);
}
addprereq(d, r, PREREQ_APPEND);
j = i;
}
}
{
{
/*
* foolib : foo : libfoo
*/
*(s - 3) = 0;
if (r != d)
addprereq(d, r, PREREQ_APPEND);
t++;
else
t = d->name;
if (r != d)
addprereq(d, r, PREREQ_APPEND);
*(s - 3) = 'l';
}
else if (((s - d->name) != 3 || *(s - 1) != 'b' || *(s - 2) != 'i' || *(s - 3) != 'l') && (*(s + 1) != 'l' || *(s + 2) != 'i' || *(s + 3) != 'b'))
{
/*
*/
s++;
t = s + strlen(s);
while (--t > s)
{
addprereq(d, r, PREREQ_APPEND);
}
}
}
}
}
mark = 0;
if (targets)
{
}
else
{
/*
* favor external.order prereqs if they are in the mix
*/
if (order)
{
if (!(flags & ORDER_prereqs))
}
}
while (*v++)
{
r = *v++;
}
e = v - 1;
if (flags & ORDER_prereqs)
{
while (*v++)
{
r = *v++;
}
}
k = 1;
while (--e >= v)
{
r = *e;
else if (!(state.questionable & 0x40000000) && !(flags & ORDER_prereqs) && (r->mark & (M_INIT|M_LHS|M_MUST|M_RHS|M_SKIP)) == M_RHS)
{
if (k)
{
k = 0;
}
}
r->complink = 0;
}
}
/*
* path name operations from (rule) s into xp using op
*
* A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
* A B C D E F G H I L N P R S U V W X Z
*/
static void
{
register char* t;
register int n;
register Rule_t* r;
register char** p;
char* e;
Rule_t* x;
int c;
int i;
int chop;
int root;
long pos;
if (r = getrule(s))
{
switch (n)
{
case 'B':
case 'D':
case 'P':
case 'Z':
break;
default:
break;
}
s = r->name;
}
switch (n)
{
case 'A':
/*
* construct absolute pathname for s
*/
if (*s)
{
if (*s == '/')
{
op = "A";
goto view;
}
else
}
return;
case 'B':
/*
* return physical binding for name
*/
{
}
return;
case 'C':
/*
* canonicalize path name
*/
if (*s)
{
}
return;
case 'D':
/*
* generate directory where s was bound
*/
sep = 0;
break;
if (((state.questionable & 0x10000000) || !(s = r->uname) || !(t = strrchr(r->name, '/')) || !streq(t+1, s)) && ((t = getbound(r->name)) || (s = r->uname) && (t = getbound(s))))
{
if ((x = getrule(t)) && (x->dynamic & (D_entries|D_scanned)) == (D_entries|D_scanned) || *t == '/' && !*(t + 1))
s = 0;
else if (s = strrchr(t, '/'))
*s = 0;
else
t = ".";
x = makerule(t);
if (s)
*s = '/';
{
{
*e = 0;
*e = '/';
}
}
if (!s)
return;
sep = 1;
}
if (s)
{
{
{
*t = 0;
*t = '/';
if (sep)
return;
}
if (!(t = strchr(s, '/')))
{
#if DEBUG
#endif
break;
}
c -= ++t - s;
s = t;
}
#if DEBUG
else
#endif
}
if (*r->name != '/')
{
if (sep)
}
return;
case 'E':
/*
* construct a PATH independent executable pathname for s
*/
if (*s)
{
s = unbound(r);
if (*s != '/' && (*s != '.' || *(s + 1) != '/' && (*(s + 1) != '.' || *(s + 2) != '/')))
{
}
}
return;
case 'F':
sep = 0;
if (!r)
r = makerule(s);
r = bind(r);
{
if (chop)
else
{
}
}
return;
case 'G':
sep = 0;
{
if (sep)
else
sep = 1;
}
return;
case 'H':
/*
* generate 14 char hash of s with op suffix
*/
if (*++op == '=')
op++;
n = 5;
while (c = *s++)
{
if (n++ >= 5)
break;
}
n = 0;
while (n++ < 5 && (c = *op++))
return;
case 'I':
/*
* return bound name if identical to op
* or return inode number
*/
if (*s)
{
if (*++op == '=')
op++;
if (!*op)
else if ((!stat(s, &st) && !stat(op, &st1) && st.st_dev == st1.st_dev && st.st_ino == st1.st_ino) == (sep == EQ))
}
return;
case 'L':
/* ignore */;
{
view:
/*
* if bound then return top and covered view names
*/
if (*op == '=')
op++;
sep = 0;
{
r = 0;
goto absolute;
}
{
for (n = *op == 'A';;)
{
break;
if (sep)
else
sep = 1;
sfputr(xp, t + ((!n++ && !strncmp(t, internal.pwd, internal.pwdlen) && t[internal.pwdlen] == '/') ? (internal.pwdlen + 1) : 0), -1);
if (*op != '*')
break;
if (n > 64)
{
break;
}
}
}
else
{
root = 0;
s = r->name;
if (n = r->view)
{
{
s++;
root = 1;
}
}
else if (*s == '/')
{
for (i = 0;; i++)
{
break;
{
if (!*(s += n) || !*++s)
n = i;
root = 1;
break;
}
}
}
if (*s == '/')
{
r = 0;
goto absolute;
}
{
if (root)
else
{
if (*s)
}
pathcanon(t, 0, 0);
{
if (sep)
else
sep = 1;
if (*op != '*')
break;
}
}
}
{
r = 0;
goto absolute;
}
}
else
{
/*
* return bound name if bound in view level 0 [n]
*/
if (sepcmp(sep, (unsigned long)((r->dynamic & D_global) ? state.maxview : r->view), (unsigned long)n))
}
return;
case 'N':
return;
case 'P':
if (*++op == '=')
op++;
*t++ = 0;
{
}
if (t)
*--t = 0;
return;
case 'R':
if (*++op == '=')
op++;
return;
case 'S':
/*
* return bound name if bound in subdirectory in view
*/
{
c = 0;
if (*++op == '=')
op++;
{
if (*op)
{
for (t = state.view[r->view].path + n; t > state.view[r->view].path && (*t != '/' || --n > 0); t--);
}
c = 1;
}
{
if (*op)
{
pathcanon(s, 0, 0);
}
if (*s++ != '.' || *s++ != '.' || *s && *s != '/')
c = 1;
}
}
return;
case 'U':
/*
* return unbound name
*/
return;
case 'V':
return;
if (*++op)
{
/*
* return the top view logical name of s
*/
s = r->name;
if (*op == '=')
op++;
{
return;
}
{
s++;
}
}
else
{
/*
* return view directory path of s
*/
}
return;
case 'W':
/*
* return license info
*/
if (*++op == '=')
op++;
n = 8 * 1024;
return;
case 'X':
/*
* return s if it is an existing file
* op[1] == 'P' does physical test
*/
return;
case 'Z':
/*
* return the longer of the bound name and the alias name
*/
if (r)
sfputr(xp, !(r->dynamic & D_alias) || !r->uname || strlen(r->name) >= strlen(r->uname) ? r->name : r->uname, -1);
return;
default:
return;
}
}
/*
* edit a single (expanded) file name s into xp
*
* each file component (described above) is modified as follows:
*
* KEEP component is kept unchanged
* DELETE component is deleted
* <string> component is changed to <string>
*/
void
{
register char* p;
register char* q;
long pos;
if (!*s)
return;
/*
* directory
*/
q = dir;
q = DELETE;
if (p = strrchr(s, '/'))
{
if (q == KEEP)
while (s <= p)
else
s = ++p;
}
{
}
/*
* base
*/
q = bas;
if (!(p = strrchr(s, '.')) || p == s)
p = s + strlen(s);
else
while (p > s && *(p - 1) == '.')
p--;
if (q == KEEP)
while (s < p)
else
s = p;
/*
* suffix
*/
q = suf;
if (*p && q == KEEP)
/*
* cleanup
*/
while (q > p && *(q - 1) == '/')
q--;
}
/*
* substitute a single (expanded) name s into xp
*/
static void
{
int n;
if (*s)
{
if (!(n = regexec(re, s, elementsof(match), match, 0)) && !(n = regsubexec(re, s, elementsof(match), match)))
else if (n != REG_NOMATCH)
}
}
static void
{
char* mime;
static Magicdisc_t disc;
if (!magic)
{
}
mime = "error";
else
{
}
}
/*
* apply token op p on (possibly bound) s with result in xp
*
* NOTE: this and :D:B:S: were the first edit operators
*
* A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
* A B D E F G I M N O P Q R S T U V W X Y Z
*/
static void
{
register Rule_t* r;
register Rule_t* x;
register Var_t* v;
register int op;
char* ops;
int dobind;
int dounbind;
int dowait;
int force;
int matched;
int tst;
int f;
List_t* q;
List_t* z;
dobind = 1;
dounbind = 0;
dowait = 1;
while (op = *p)
{
p++;
switch (op)
{
case 'B':
dounbind = 1;
continue;
case 'W':
dowait = 0;
continue;
case 'X':
dobind = 0;
continue;
}
break;
}
ops = p;
while (*p && *p++ != '?');
switch (op)
{
case 'N':
case 'V':
/*NOP*/;
else if (*p)
else
return;
case 'Q':
switch (*ops)
{
case 0:
break;
case 'O':
break;
case 'R':
tst = (!dobind || getrule(s)) && !(nametype(s, NiL) & (NAME_altstate|NAME_staterule|NAME_statevar));
break;
case 'S':
switch (*(ops + 1))
{
case 0:
break;
case 'A':
tst = NAME_altstate;
break;
case 'R':
break;
case 'V':
tst = NAME_statevar;
break;
default:
tst = 0;
break;
}
break;
case 'V':
switch (*(ops + 1))
{
case 0:
break;
case 'I':
break;
case 'V':
break;
default:
tst = 0;
break;
}
break;
default:
tst = 0;
break;
}
return;
}
r = makerule(s);
if (dounbind)
if (dobind)
{
r = bind(r);
{
/*
* don't wait for targets in the active frames
*/
{
break;
}
}
}
if (*ops == '=')
ops++;
if (!*r->name)
{
switch (op)
{
case 'Z':
if (*ops != 'W')
break;
if (*++ops == '=')
ops++;
/*FALLTHROUGH*/
case 'R':
break;
}
return;
}
tst = (notfile(r) || !r->time && ((state.questionable & 0x04000000) || !(r->dynamic & D_triggered)) || r->status == IGNORE || state.exec && r->status != NOTYET && (x = staterule(RULE, r, NiL, 0)) && !x->time || (r->dynamic & (D_member|D_membertoo)) == D_member) ? 0 : 'F';
switch (op)
{
case 0:
case '*':
matched = 1;
break;
case 'A':
{
{
}
}
return;
case 'D':
case 'E':
{
if (*(p = v->value))
{
}
else if (*p)
{
if (op == 'D')
{
else
op = 0;
if (*p != '1' || *(p + 1))
{
if (!op)
else
{
}
}
}
else
{
if (*p)
{
if (*p == '"' || *p == '\'')
else
}
}
}
if (tmp)
}
return;
case 'F':
{
break;
}
{
matched = 1;
break;
}
{
matched = 0;
break;
}
switch (op)
{
case 'B':
break;
case 'C':
break;
case 'D':
break;
case 'F':
case 'R':
case '-':
break;
case 'L':
break;
case 'P':
break;
case 'X':
matched = 1;
break;
default:
break;
}
break;
case 'G':
{
matched = 0;
break;
}
matched = 1;
break;
/*
* the remaining checks are necessary because of
* state.accept | state.ignorestate
* the tests are conservative, allowing some built
* files to go undetected
*/
{
if (r->action || (r->property & (P_archive|P_command)) || !(state.questionable & 0x00004000) && (r->dynamic & D_dynamic))
break;
if (z)
break;
}
matched = 0;
f = x ? (x->property & P_implicit) : 0;
{
if (metamatch(stem, unbound(r), z->rule->name) && (!(r->property & P_terminal) || (z->rule->property & P_terminal)) && !(z->rule->property & f) && (x = metainfo('I', z->rule->name, NiL, 0)))
if ((x = metarule(q->rule->name, z->rule->name, 0)) && (!(r->property & P_terminal) || (x->property & P_terminal)) && !(x->property & f))
{
{
matched = 1;
break;
}
}
}
break;
case 'I':
else
{
switch (*ops)
{
case '-':
case 'X':
case 'x':
break;
default:
break;
}
while (p > s && *(p - 1) == '\n')
p--;
{
}
}
return;
case 'M':
if (!*ops)
return;
case 'O':
p = "w";
sep = '\n';
tst = 1;
for (;; ops++)
{
switch (*ops)
{
case '+':
case 'A':
case 'a':
p = "a";
continue;
case '-':
case 'N':
case 'n':
sep = -1;
continue;
case 'X':
case 'x':
tst = 0;
continue;
}
break;
}
if (*ops == '=')
ops++;
else
{
if (tst)
else
{
}
}
return;
case 'P':
break;
case 'R':
return;
case 'S':
if (*ops == '=')
ops++;
{
if (*ops == '=')
ops++;
}
if (!op)
{
break;
}
x = 0;
switch (op)
{
case 'A':
if (r->property & P_staterule)
break;
case 'M':
break;
case 'P':
break;
case 'R':
break;
case 'V':
break;
default:
break;
}
if (x)
return;
case 'T':
if (!*ops)
else
{
if (*s == '.')
if (*s)
{
{
if (dobind)
x = bind(x);
}
else
tm = 0;
}
}
return;
case 'U':
if (r->property & P_staterule)
{
}
else if (r->property & P_statevar)
{
}
else
return;
case 'Y':
return;
case 'Z':
switch (*ops++)
{
case 'C':
break;
case 'E':
break;
case 'R':
break;
case 0:
ops--;
/*FALLTHROUGH*/
case 'S':
break;
case 'W':
break;
default:
tm = 0;
break;
}
if (*ops == '=')
ops++;
return;
default:
break;
}
{
if (*p)
else
}
}
/*
* return 1 if fp is an active frame
*/
static int
{
return 1;
return 0;
}
/*
* construct the parentage of r in xp, starting with r
* sep placed between names
*/
void
{
if (r->active && active(r, r->active) && r->active->parent && !(r->active->parent->target->mark & M_mark) && r->active->parent->parent != r->active->parent)
{
}
}
/*
* copy s into xp if rule s has any attribute in att or
* if att is 0 then copy the named attributes of rule s into xp
* attribute pattern propagation is taken into account
*/
static void
{
register char* t;
register Rule_t* r;
register Rule_t* a;
register List_t* p;
long n;
int c;
int i;
Rule_t* x;
Rule_t* y;
Rule_t* z;
i = 0;
r = getrule(s);
do
{
if (!r) z = 0;
{
s = r->name;
z = 0;
}
{
n = r->attribute;
if (x) n |= x->attribute;
if (att)
{
for (;;)
{
{
{
if (t) *t = c;
else i = 1;
goto next;
}
}
{
{
{
if (t) *t = c;
else i = 1;
goto next;
}
}
else if (hasattribute(r, a, x))
{
if (t) *t = c;
{
else i = 1;
}
goto next;
}
}
if (!t) break;
*t++ = c;
att = t;
}
{
else i = 1;
}
}
else
{
{
else i = 1;
}
{
else i = 1;
break;
}
}
}
{
else i = 1;
}
next: ;
} while (r = z);
}
/*
* check if name generates a target matching the metarule pattern pat
* (sep<) lists the primary and secondary targets for name
*/
static void
{
register Rule_t* x;
register List_t* p;
register List_t* q;
char* b;
{
{
return;
}
{
{
return;
}
if (metamatch(tmpname, name, p->rule->name) && (streq(tmpname, pat) || (b = strrchr(pat, '/')) && streq(tmpname, b + 1)))
{
return;
}
}
else
{
long n;
{
{
{
/*UNDENT...*/
Rule_t* y;
Rule_t* z;
List_t* u;
long b;
{
if (!z->uname)
{
{
{
break;
}
sfstrclose(tp);
return;
}
}
{
if (metamatch(NiL, u->rule->name, z->uname) && (y = metainfo('S', q->rule->name, u->rule->name, 0)))
{
{
}
sfstrclose(tp);
return;
}
}
}
metaexpand(xp, stem, z && z->uname && (y = metarule(z->uname, p->rule->name, 0)) && y->action && !*y->action ? z->uname : p->rule->name);
/*...INDENT*/
}
sfstrclose(tp);
return;
}
{
char* t;
{
{
{
}
}
}
{
sfstrclose(tp);
return;
}
}
}
sfstrclose(tp);
}
}
{
{
return;
}
}
}
/*
* quote s into xp according to sh syntax
*/
void
{
register char* t;
register char* b;
register int c;
register int q;
{
return;
}
q = 0;
b = 0;
for (t = s;;)
{
switch (c = *t++)
{
case 0:
break;
case '\n':
case ';':
case '&':
case '|':
case '<':
case '>':
case '(':
case ')':
case '[':
case ']':
case '{':
case '}':
case '*':
case '?':
case ' ':
case '\t':
case '\\':
q |= 4;
#if _HUH_2000_06_01
if (!b)
b = t - 1;
#endif
continue;
case '\'':
q |= 1;
if (q & 2)
break;
continue;
case '"':
case '$':
q |= 2;
if (q & 1)
break;
continue;
case '=':
if (!q && !b && *(b = t) == '=')
b++;
continue;
default:
continue;
}
break;
}
if (!q)
else if (!(q & 1))
{
if (b)
else
}
else if (!(q & 2))
{
if (b)
else
}
else
for (t = s;;)
switch (c = *t++)
{
case 0:
return;
case '\n':
break;
case ';':
case '&':
case '|':
case '<':
case '>':
case '(':
case ')':
case '[':
case ']':
case '{':
case '}':
case '$':
case '*':
case '?':
case ' ':
case '\t':
case '\\':
case '\'':
case '"':
/*FALLTHROUGH*/
default:
break;
}
}
/*
* generate edit context in xp at cur from beg
*/
static char*
{
register int n;
{
n = EDITCONTEXT - n;
}
}
/*
* expand rules selected by $(...) into xp
*/
static void
{
register int sep;
register Rule_t* r;
sep = 0;
{
{
{
else sep = 1;
}
}
}
}
/*
* apply edit operators ed on value v into xp
*
* :C:, :/:, :Y: and :?: have a different syntax from the other ops
* :D:, :B: and :S:, when contiguous, are collected and applied as a group
* a single `:' must separate each op, the trailing `:' is optional
* =, !, !=, <>, <, <=, > and >= may separate an op from its value
*
* A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
* A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
*/
static void
{
register char* s;
register int op;
char* dir;
char* bas;
char* suf;
char* val;
char* oldp;
char* newp;
char* eb;
int zer;
int old;
int qual;
int cnt;
int cntlim;
int ctx;
int expall;
int n;
int m;
int sep;
int tokenize;
long beg;
long ctx_beg;
long cur;
long arg;
unsigned long all;
char* ctx_end;
char* tok;
char* x;
Rule_t* r;
int out;
char flags[8];
long top[2];
/*
* apply the operators from left to right
*/
out = 0;
buf[1] = 0;
top[1] = 0;
if (exp < 0)
{
{
return;
}
}
else
{
all = 0;
if (exp)
else
{
}
}
qual = 0;
{
continue;
if (op == '!')
{
ed++;
continue;
}
{
ed--;
#if DEBUG
#endif
{
{
case ED_COPY:
goto copy;
case ED_EDIT:
{
*--ed = '=';
}
goto copy;
case ED_OP:
{
const Edit_opt_t* opt;
/*UNDENT...*/
{
{
{
}
}
}
while (*ed == '-')
{
{
break;
{
if (n == '-')
break;
}
m = *ed;
*ed = 0;
{
{
break;
}
{
else
{
{
}
}
break;
}
}
*ed = m;
}
else
{
while (n && n != del)
{
if (isspace(n))
break;
{
{
break;
}
{
else
{
{
}
}
break;
}
}
n = *++ed;
}
}
ed++;
}
/*..INDENT*/
}
break;
case ED_QUAL:
continue;
}
if (op != 'T')
{
*--ed = 'F';
}
*--ed = 'X';
*--ed = 'W';
if (ed != s)
{
{
*--ed = '=';
*--ed = '>';
*--ed = '<';
*--ed = '!';
}
else
*--ed = '=';
}
copy:
}
else
{
}
*--ed = '@';
#if DEBUG
#endif
}
/*
* check for tokenization
*/
if (op == '@')
{
tokenize = 0;
}
else
tokenize = 1;
sep = 0;
/*
* collect the operands
*/
{
/*
* substitute: <delim><old><delim><new><delim>[flags]
*/
switch (op)
{
case 'C':
break;
case '/':
op = 'C';
ed--;
break;
}
s = ed;
{
}
if (n)
{
continue;
}
if (*ed)
{
}
if (*++s == ' ')
tokenize = 0;
}
{
/*
* conditional: <delim><non-null><delim><null><delim>
*/
n = op;
switch (op)
{
case 'Y':
if (n = *ed)
ed++;
break;
case '?':
op = 'Y';
break;
}
s = ed;
if (*ed == n)
ed++;
*s = 0;
s = ed;
if (*ed)
ed++;
*s = 0;
switch (*ed++)
{
case 'O':
case 'o':
old = 1;
break;
case 'Z':
case 'z':
case '+':
case '-':
zer = 1;
break;
default:
break;
}
if (*ed)
ed++;
}
{
ed++;
}
else if (*ed)
{
/*
* value: [~!<>][=][<val>]
*/
{
for (;; ed++)
{
switch (*ed)
{
case '=':
ed++;
break;
case '-':
case '+':
if (!sep)
{
ed++;
}
break;
case '~':
break;
continue;
case '^':
break;
continue;
case '!':
break;
continue;
case '<':
break;
continue;
case '>':
break;
continue;
}
break;
}
if (!sep)
{
break;
}
}
{
if (cnt)
{
n++;
cnt = 0;
}
else if (*ed == '(')
{
cnt = '(';
cntlim = ')';
n++;
}
else if (*ed == '[')
{
cnt = '[';
cntlim = ']';
n++;
}
else if (n <= 0)
{
break;
{
s = ed;
while (isspace(*++s));
if (*s == del)
break;
}
}
}
if (*ed)
*ed++ = 0;
if (!*val)
}
else
switch (op)
{
case 'B':
{
/*
* B, D and S are grouped before application
*/
switch (*ed)
{
case 'B':
continue;
break;
case 'D':
continue;
break;
case 'S':
continue;
break;
}
}
break;
case 'D':
goto parts;
case 'S':
goto parts;
case 'E':
case 'H':
case 'I':
case 'J':
case 'K':
case 'L':
case 'R':
case 'U':
case 'W':
case 'X':
case 'Z':
case '-':
case '+':
case '~':
tokenize = 0;
break;
}
qual = 0;
/*
* validate the operator and apply non-tokenizing operators
*/
{
all = 0;
}
if (!all)
{
if (xp)
{
}
else
x = v;
}
switch (op)
{
case 'A':
case 'Q':
val = 0;
break;
case 'B':
case 'D':
case 'S':
if (!dir)
ctx = 0;
break;
case 'C':
case 'Y':
ctx = 0;
break;
case 'E':
continue;
case 'F':
case 'X':
ctx = 0;
/*FALLTHROUGH*/
case 'P':
case 'T':
switch (op)
{
case 'X':
continue;
}
break;
case 'G':
case 'I':
case 'J':
case 'K':
case 'L':
case 'W':
case 'Z':
ctx = 0;
/*FALLTHROUGH*/
case 'M':
case 'N':
case 'O':
case 'U':
if (!sep)
switch (op)
{
case 'I':
continue;
case 'J':
continue;
case 'K':
continue;
case 'L':
n = SORT_first;
n |= SORT_sort|SORT_invert;
n |= SORT_sort;
n &= ~SORT_first;
n |= SORT_qualified;
n |= SORT_sort|SORT_version;
n |= SORT_force;
switch (x[1])
{
case 'F':
case 'f':
break;
case 'R':
case 'r':
break;
case 'V':
case 'v':
break;
default:
return;
}
else
continue;
case 'M':
{
continue;
}
break;
case 'N':
if (n = regcomp(&re, val, REG_SHELL|REG_AUGMENTED|REG_LEFT|REG_RIGHT|REG_LENIENT|REG_NOSUB|REG_NULL))
{
continue;
}
break;
case 'O':
break;
case 'U':
continue;
case 'W':
val = 0;
switch (op)
{
case 'O':
break;
case 'P':
order_recurse(xp, x, getval(external.files, VAL_PRIMARY|VAL_AUXILIARY), getval(external.skip, VAL_PRIMARY|VAL_AUXILIARY), val, ORDER_force|ORDER_prereqs);
break;
case 'R':
order_recurse(xp, x, getval(external.files, VAL_PRIMARY|VAL_AUXILIARY), getval(external.skip, VAL_PRIMARY|VAL_AUXILIARY), val, ORDER_force);
break;
default:
break;
}
continue;
case 'Z':
continue;
}
break;
case 'H':
if (x == v)
{
}
n = SORT_sort;
{
n |= SORT_invert;
n |= SORT_numeric;
n |= SORT_uniq;
n |= SORT_version;
}
else
for (;;)
{
switch (*val++)
{
case 0:
break;
case 'C':
case 'c':
n |= SORT_collate;
continue;
case 'F':
case 'f':
n |= SORT_first;
continue;
case 'I':
case 'i':
case 'R':
case 'r':
n |= SORT_invert;
continue;
case 'N':
case 'n':
n |= SORT_numeric;
continue;
case 'O':
case 'o':
n |= SORT_reverse;
continue;
case 'P':
case 'p':
n |= SORT_prefix;
continue;
case 'U':
case 'u':
n |= SORT_uniq;
continue;
case 'V':
case 'v':
n |= SORT_version;
continue;
}
break;
}
continue;
case 'R':
continue;
case 'V':
continue;
case '-':
case '+':
case '~':
else if (op != '~')
continue;
default:
continue;
}
/*
* the operator is applied to each token if tokenize!=0
*/
if (!all)
goto breakloop;
{
if (!tokenize)
{
if (cnt > 1)
break;
s = x;
}
else if (all)
{
for (;;)
{
goto breakloop;
{
break;
}
}
s = r->name;
}
else
{
{
if (cnt > 1)
break;
s = null;
}
{
}
}
{
s++;
*(ctx_end - 1) = 0;
}
switch (op)
{
case 'A':
break;
case 'B':
case 'D':
case 'S':
if (*s)
{
}
break;
case 'C':
break;
case 'F':
#if !_drop_this_in_3_2
switch (*val)
{
case 'L':
val = "%(lower)s";
break;
case 'U':
val = "%(upper)s";
break;
case 'V':
val = "%(variable)s";
break;
}
#endif
break;
case 'G':
if (*val)
{
char* t;
char* v;
while (v = tokread(t))
tokclose(t);
}
else
break;
case 'M':
case 'N':
break;
case 'O':
if (cntlim < 0)
else
switch (sep)
{
case EQ:
if (!cntlim)
{
if (s == null)
goto breakloop;
goto nextloop;
}
goto nextloop;
goto breakloop;
break;
case NOT:
if (!cntlim)
{
goto nextloop;
}
/*FALLTHROUGH*/
case NE:
goto nextloop;
break;
case LT:
goto breakloop;
break;
case LE:
goto breakloop;
break;
case GE:
goto nextloop;
break;
case GT:
goto nextloop;
break;
}
break;
case 'P':
break;
case 'Q':
break;
case 'T':
break;
case 'Y':
break;
#if DEBUG
default:
#endif
}
if (ctx_end)
{
*ctx_end = MARK_CONTEXT;
if (!n)
*(s - 1) = ' ';
else
{
x = s + n + 1;
m = 0;
while (s < x)
if (*s++ == ' ')
for (m++; s < x && *s == ' '; s++);
if (m)
{
s = x + m * 2;
for (;;)
{
if ((*--s = *--x) == ' ')
{
*s = MARK_CONTEXT;
for (x++; *(x - 1) == ' '; *--s = *--x);
*--s = MARK_CONTEXT;
if (--m <= 0)
break;
}
}
}
}
}
{
}
}
if (ctx_end)
{
*ctx_end = MARK_CONTEXT;
}
if (all)
{
if (pos)
{
{
/*
* a poorly understood interaction
* some unwanted rules slip through
* this loop catches them
*/
{
}
}
}
n = all;
lla = n;
exp++;
}
else
{
{
}
}
/*
* operator cleanup
*/
switch (op)
{
case 'B':
case 'D':
case 'S':
break;
case 'C':
case 'M':
case 'N':
break;
}
}
if (all)
{
}
else
{
if (out)
if (buf[1])
}
}
/*
* expand first non-null of nvars variables in s with edit ops ed into xp
*/
static void
{
register char* v;
char* t;
int exp;
int op;
int aux;
int ign;
long pos;
#if DEBUG
long beg;
#endif
static int level;
if (level++ > 64)
{
level = 0;
}
#if DEBUG
{
}
#endif
/*
* some operators must appear first (and before expansion)
*/
exp = 1;
aux = 0;
if (ed)
{
{
ed++;
{
t = ed;
{
case ED_AUXILLIARY:
exp = 0;
aux |= VAL_AUXILIARY;
continue;
case ED_LITERAL:
exp = 0;
continue;
case ED_PRIMARY:
exp = 0;
aux |= VAL_PRIMARY;
continue;
}
ed = t;
break;
}
else if (op != 'V')
break;
else
{
for (;;)
{
switch (*++ed)
{
case 0:
break;
case 'A':
aux |= VAL_AUXILIARY;
continue;
case 'B':
continue;
case 'F':
continue;
case 'I':
ign = 1;
continue;
case 'P':
aux |= VAL_PRIMARY;
continue;
case 'U':
aux |= VAL_UNBOUND;
continue;
case 'X':
exp = 1;
continue;
default:
{
ed++;
break;
}
if (!ign)
continue;
}
break;
}
}
}
}
for (;;)
{
#if DEBUG
if (msg)
{
}
#endif
{
if (!cvt)
*t = 0;
*t = *s;
}
else
{
if (strchr(s, '$'))
{
if (!cvt)
}
else v = s;
{
exp = -1;
if (!ed)
}
else
}
{
if (!cvt)
exp = 0;
for (;;)
{
{
exp++;
}
else
if (--nvars <= 0)
break;
while (*s++);
}
while (exp--)
break;
}
if (*v || --nvars <= 0)
{
if (ed)
{
if (!cvt)
{
if (!val)
}
else
{
tmp = 0;
v = 0;
}
if (tmp)
}
else if (*v)
break;
} while (*s++);
}
#if DEBUG
if (msg)
{
}
#endif
if (cvt)
if (val)
level--;
}
/*
* expand `$(...)' from a into xp
*/
void
{
register int c;
register char* s;
int alt;
int del;
int p;
int q;
int nvars;
long ed;
long var;
if (!*a) return;
if (!(s = strchr(a, '$')))
{
return;
}
{
}
a = s;
while (*a)
{
if (*a != '$')
else if (*++a == '(')
{
if (isspace(*++a))
{
}
else
{
ed = 0;
nvars = 1;
alt = '|';
del = ':';
q = 0;
p = 1;
while (c = *a++)
{
if (c == '\\' && *a)
{
c = *a++;
}
else if (c == '"')
q = !q;
else if (q)
/* quoted */;
else if (c == '(')
p++;
else if (c == ')')
{
if (!--p)
break;
}
else if (!ed && p == 1)
{
if (c == alt)
{
c = 0;
nvars++;
}
else if (c == del)
{
alt = c = 0;
}
else if (c == '`')
{
alt = c = 0;
if (!(del = *a++))
{
ed--;
break;
}
}
else if (isspace(c))
{
if (*a == del || *a == '`')
continue;
}
}
}
if (q || !c)
{
a--;
}
}
}
else if (*a == '$')
{
for (s = a; *s == '$'; s++);
if (*s != '(')
while (a < s)
}
else
}
if (val)
{
}
}