version 1.21, 2007/09/16 12:01:11 |
version 1.22, 2007/09/16 12:02:38 |
|
|
#define VAR_BANG_EQUAL 0x100 |
#define VAR_BANG_EQUAL 0x100 |
|
|
typedef struct { |
typedef struct { |
char *lbuffer; /* left string to free */ |
char *lbuffer; /* left string to free */ |
char *lhs; /* String to match */ |
char *lhs; /* String to match */ |
size_t leftLen; /* Length of string */ |
size_t leftLen; /* Length of string */ |
char *rhs; /* Replacement string (w/ &'s removed) */ |
char *rhs; /* Replacement string (w/ &'s removed) */ |
size_t rightLen; /* Length of replacement */ |
size_t rightLen; /* Length of replacement */ |
int flags; |
int flags; |
} VarPattern; |
} VarPattern; |
|
|
struct LoopStuff { |
struct LoopStuff { |
struct LoopVar *var; |
struct LoopVar *var; |
char *expand; |
char *expand; |
bool err; |
bool err; |
}; |
}; |
|
|
static bool VarHead(struct Name *, bool, Buffer, void *); |
static bool VarHead(struct Name *, bool, Buffer, void *); |
|
|
static char *do_regex(const char *, const struct Name *, void *); |
static char *do_regex(const char *, const struct Name *, void *); |
|
|
typedef struct { |
typedef struct { |
regex_t re; |
regex_t re; |
int nsub; |
int nsub; |
regmatch_t *matches; |
regmatch_t *matches; |
char *replace; |
char *replace; |
int flags; |
int flags; |
} VarREPattern; |
} VarREPattern; |
#endif |
#endif |
|
|
|
|
static struct Name *dummy_arg = &dummy; |
static struct Name *dummy_arg = &dummy; |
|
|
static struct modifier { |
static struct modifier { |
bool atstart; |
bool atstart; |
void * (*getarg)(const char **, SymTable *, bool, int); |
void * (*getarg)(const char **, SymTable *, bool, int); |
char * (*apply)(const char *, const struct Name *, void *); |
char * (*apply)(const char *, const struct Name *, void *); |
bool (*word_apply)(struct Name *, bool, Buffer, void *); |
bool (*word_apply)(struct Name *, bool, Buffer, void *); |
void (*freearg)(void *); |
void (*freearg)(void *); |
} *choose_mod[256], |
} *choose_mod[256], |
match_mod = {false, get_stringarg, NULL, VarMatch, free_stringarg}, |
match_mod = {false, get_stringarg, NULL, VarMatch, free_stringarg}, |
nomatch_mod = {false, get_stringarg, NULL, VarNoMatch, free_stringarg}, |
nomatch_mod = {false, get_stringarg, NULL, VarNoMatch, free_stringarg}, |
subst_mod = {false, get_spatternarg, NULL, VarSubstitute, free_patternarg}, |
subst_mod = {false, get_spatternarg, NULL, VarSubstitute, free_patternarg}, |
#ifndef MAKE_BOOTSTRAP |
#ifndef MAKE_BOOTSTRAP |
resubst_mod = {false, get_patternarg, do_regex, NULL, free_patternarg}, |
resubst_mod = {false, get_patternarg, do_regex, NULL, free_patternarg}, |
#endif |
#endif |
quote_mod = {false, check_empty, VarQuote, NULL , NULL}, |
quote_mod = {false, check_empty, VarQuote, NULL , NULL}, |
tail_mod = {false, check_empty, NULL, VarTail, NULL}, |
tail_mod = {false, check_empty, NULL, VarTail, NULL}, |
head_mod = {false, check_empty, NULL, VarHead, NULL}, |
head_mod = {false, check_empty, NULL, VarHead, NULL}, |
suffix_mod = {false, check_empty, NULL, VarSuffix, NULL}, |
suffix_mod = {false, check_empty, NULL, VarSuffix, NULL}, |
root_mod = {false, check_empty, NULL, VarRoot, NULL}, |
root_mod = {false, check_empty, NULL, VarRoot, NULL}, |
upper_mod = {false, check_empty, do_upper, NULL, NULL}, |
upper_mod = {false, check_empty, do_upper, NULL, NULL}, |
lower_mod = {false, check_empty, do_lower, NULL, NULL}, |
lower_mod = {false, check_empty, do_lower, NULL, NULL}, |
shcmd_mod = {false, check_shcmd, do_shcmd, NULL, NULL}, |
shcmd_mod = {false, check_shcmd, do_shcmd, NULL, NULL}, |
sysv_mod = {false, get_sysvpattern, NULL, VarSYSVMatch, free_patternarg}, |
sysv_mod = {false, get_sysvpattern, NULL, VarSYSVMatch, free_patternarg}, |
uniq_mod = {false, check_empty, NULL, VarUniq, NULL}, |
uniq_mod = {false, check_empty, NULL, VarUniq, NULL}, |
sort_mod = {false, check_empty, do_sort, NULL, NULL}, |
sort_mod = {false, check_empty, do_sort, NULL, NULL}, |
loop_mod = {false, get_loop, finish_loop, VarLoop, free_looparg}, |
loop_mod = {false, get_loop, finish_loop, VarLoop, free_looparg}, |
undef_mod = {true, get_value, do_undef, NULL, NULL}, |
undef_mod = {true, get_value, do_undef, NULL, NULL}, |
def_mod = {true, get_value, do_def, NULL, NULL}, |
def_mod = {true, get_value, do_def, NULL, NULL}, |
label_mod = {true, check_empty, do_label, NULL, NULL}, |
label_mod = {true, check_empty, do_label, NULL, NULL}, |
path_mod = {true, check_empty, do_path, NULL, NULL}, |
path_mod = {true, check_empty, do_path, NULL, NULL}, |
assign_mod = {true, assign_get_value, do_assign, NULL, free_patternarg}, |
assign_mod = {true, assign_get_value, do_assign, NULL, free_patternarg}, |
exec_mod = {true, get_cmd, do_exec, NULL, free_patternarg} |
exec_mod = {true, get_cmd, do_exec, NULL, free_patternarg} |
; |
; |
|
|
void |
void |
VarModifiers_Init() |
VarModifiers_Init() |
{ |
{ |
choose_mod['M'] = &match_mod; |
choose_mod['M'] = &match_mod; |
choose_mod['N'] = &nomatch_mod; |
choose_mod['N'] = &nomatch_mod; |
choose_mod['S'] = &subst_mod; |
choose_mod['S'] = &subst_mod; |
#ifndef MAKE_BOOTSTRAP |
#ifndef MAKE_BOOTSTRAP |
choose_mod['C'] = &resubst_mod; |
choose_mod['C'] = &resubst_mod; |
#endif |
#endif |
choose_mod['Q'] = "e_mod; |
choose_mod['Q'] = "e_mod; |
choose_mod['T'] = &tail_mod; |
choose_mod['T'] = &tail_mod; |
choose_mod['H'] = &head_mod; |
choose_mod['H'] = &head_mod; |
choose_mod['E'] = &suffix_mod; |
choose_mod['E'] = &suffix_mod; |
choose_mod['R'] = &root_mod; |
choose_mod['R'] = &root_mod; |
if (FEATURES(FEATURE_UPPERLOWER)) { |
if (FEATURES(FEATURE_UPPERLOWER)) { |
choose_mod['U'] = &upper_mod; |
choose_mod['U'] = &upper_mod; |
choose_mod['L'] = &lower_mod; |
choose_mod['L'] = &lower_mod; |
} |
} |
if (FEATURES(FEATURE_SUNSHCMD)) |
if (FEATURES(FEATURE_SUNSHCMD)) |
choose_mod['s'] = &shcmd_mod; |
choose_mod['s'] = &shcmd_mod; |
if (FEATURES(FEATURE_UNIQ)) |
if (FEATURES(FEATURE_UNIQ)) |
choose_mod['u'] = &uniq_mod; |
choose_mod['u'] = &uniq_mod; |
if (FEATURES(FEATURE_SORT)) |
if (FEATURES(FEATURE_SORT)) |
choose_mod['O'] = &sort_mod; |
choose_mod['O'] = &sort_mod; |
if (FEATURES(FEATURE_ODE)) { |
if (FEATURES(FEATURE_ODE)) { |
choose_mod['@'] = &loop_mod; |
choose_mod['@'] = &loop_mod; |
choose_mod['D'] = &def_mod; |
choose_mod['D'] = &def_mod; |
choose_mod['U'] = &undef_mod; |
choose_mod['U'] = &undef_mod; |
choose_mod['L'] = &label_mod; |
choose_mod['L'] = &label_mod; |
choose_mod['P'] = &path_mod; |
choose_mod['P'] = &path_mod; |
} |
} |
if (FEATURES(FEATURE_ASSIGN)) |
if (FEATURES(FEATURE_ASSIGN)) |
choose_mod[':'] = &assign_mod; |
choose_mod[':'] = &assign_mod; |
if (FEATURES(FEATURE_EXECMOD)) |
if (FEATURES(FEATURE_EXECMOD)) |
choose_mod['!'] = &exec_mod; |
choose_mod['!'] = &exec_mod; |
} |
} |
|
|
/* All modifiers handle addSpace (need to add a space before placing the |
/* All modifiers handle addSpace (need to add a space before placing the |
|
|
static bool |
static bool |
VarHead(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED) |
VarHead(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED) |
{ |
{ |
const char *slash; |
const char *slash; |
|
|
slash = Str_rchri(word->s, word->e, '/'); |
slash = Str_rchri(word->s, word->e, '/'); |
if (slash != NULL) { |
if (slash != NULL) { |
if (addSpace) |
if (addSpace) |
Buf_AddSpace(buf); |
Buf_AddSpace(buf); |
Buf_Addi(buf, word->s, slash); |
Buf_Addi(buf, word->s, slash); |
} else { |
} else { |
/* If no directory part, give . (q.v. the POSIX standard). */ |
/* If no directory part, give . (q.v. the POSIX standard). */ |
if (addSpace) |
if (addSpace) |
Buf_AddString(buf, " ."); |
Buf_AddString(buf, " ."); |
else |
else |
Buf_AddChar(buf, '.'); |
Buf_AddChar(buf, '.'); |
} |
} |
return true; |
return true; |
} |
} |
|
|
/*- |
/*- |
|
|
static bool |
static bool |
VarTail(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED) |
VarTail(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED) |
{ |
{ |
const char *slash; |
const char *slash; |
|
|
if (addSpace) |
if (addSpace) |
Buf_AddSpace(buf); |
Buf_AddSpace(buf); |
slash = Str_rchri(word->s, word->e, '/'); |
slash = Str_rchri(word->s, word->e, '/'); |
if (slash != NULL) |
if (slash != NULL) |
Buf_Addi(buf, slash+1, word->e); |
Buf_Addi(buf, slash+1, word->e); |
else |
else |
Buf_Addi(buf, word->s, word->e); |
Buf_Addi(buf, word->s, word->e); |
return true; |
return true; |
} |
} |
|
|
/*- |
/*- |
|
|
static bool |
static bool |
VarSuffix(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED) |
VarSuffix(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED) |
{ |
{ |
const char *dot; |
const char *dot; |
|
|
dot = Str_rchri(word->s, word->e, '.'); |
dot = Str_rchri(word->s, word->e, '.'); |
if (dot != NULL) { |
if (dot != NULL) { |
if (addSpace) |
if (addSpace) |
Buf_AddSpace(buf); |
Buf_AddSpace(buf); |
Buf_Addi(buf, dot+1, word->e); |
Buf_Addi(buf, dot+1, word->e); |
addSpace = true; |
addSpace = true; |
} |
} |
return addSpace; |
return addSpace; |
} |
} |
|
|
/*- |
/*- |
|
|
static bool |
static bool |
VarRoot(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED) |
VarRoot(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED) |
{ |
{ |
const char *dot; |
const char *dot; |
|
|
if (addSpace) |
if (addSpace) |
Buf_AddSpace(buf); |
Buf_AddSpace(buf); |
dot = Str_rchri(word->s, word->e, '.'); |
dot = Str_rchri(word->s, word->e, '.'); |
if (dot != NULL) |
if (dot != NULL) |
Buf_Addi(buf, word->s, dot); |
Buf_Addi(buf, word->s, dot); |
else |
else |
Buf_Addi(buf, word->s, word->e); |
Buf_Addi(buf, word->s, word->e); |
return true; |
return true; |
} |
} |
|
|
/*- |
/*- |
|
|
VarMatch(struct Name *word, bool addSpace, Buffer buf, |
VarMatch(struct Name *word, bool addSpace, Buffer buf, |
void *pattern) /* Pattern the word must match */ |
void *pattern) /* Pattern the word must match */ |
{ |
{ |
const char *pat = (const char *)pattern; |
const char *pat = (const char *)pattern; |
|
|
if (Str_Matchi(word->s, word->e, pat, strchr(pat, '\0'))) { |
if (Str_Matchi(word->s, word->e, pat, strchr(pat, '\0'))) { |
if (addSpace) |
if (addSpace) |
Buf_AddSpace(buf); |
Buf_AddSpace(buf); |
Buf_Addi(buf, word->s, word->e); |
Buf_Addi(buf, word->s, word->e); |
return true; |
return true; |
} else |
} else |
return addSpace; |
return addSpace; |
} |
} |
|
|
/*- |
/*- |
|
|
VarNoMatch(struct Name *word, bool addSpace, Buffer buf, |
VarNoMatch(struct Name *word, bool addSpace, Buffer buf, |
void *pattern) /* Pattern the word must not match */ |
void *pattern) /* Pattern the word must not match */ |
{ |
{ |
const char *pat = (const char *)pattern; |
const char *pat = (const char *)pattern; |
|
|
if (!Str_Matchi(word->s, word->e, pat, strchr(pat, '\0'))) { |
if (!Str_Matchi(word->s, word->e, pat, strchr(pat, '\0'))) { |
if (addSpace) |
if (addSpace) |
Buf_AddSpace(buf); |
Buf_AddSpace(buf); |
Buf_Addi(buf, word->s, word->e); |
Buf_Addi(buf, word->s, word->e); |
return true; |
return true; |
} else |
} else |
return addSpace; |
return addSpace; |
} |
} |
|
|
static bool |
static bool |
VarUniq(struct Name *word, bool addSpace, Buffer buf, void *lastp) |
VarUniq(struct Name *word, bool addSpace, Buffer buf, void *lastp) |
{ |
{ |
struct Name *last = (struct Name *)lastp; |
struct Name *last = (struct Name *)lastp; |
|
|
/* does not match */ |
/* does not match */ |
if (last->s == NULL || last->e - last->s != word->e - word->s || |
if (last->s == NULL || last->e - last->s != word->e - word->s || |
strncmp(word->s, last->s, word->e - word->s) != 0) { |
strncmp(word->s, last->s, word->e - word->s) != 0) { |
if (addSpace) |
if (addSpace) |
Buf_AddSpace(buf); |
Buf_AddSpace(buf); |
Buf_Addi(buf, word->s, word->e); |
Buf_Addi(buf, word->s, word->e); |
addSpace = true; |
addSpace = true; |
} |
} |
last->s = word->s; |
last->s = word->s; |
last->e = word->e; |
last->e = word->e; |
return addSpace; |
return addSpace; |
} |
} |
|
|
static bool |
static bool |
VarLoop(struct Name *word, bool addSpace, Buffer buf, void *vp) |
VarLoop(struct Name *word, bool addSpace, Buffer buf, void *vp) |
{ |
{ |
struct LoopStuff *v = (struct LoopStuff *)vp; |
struct LoopStuff *v = (struct LoopStuff *)vp; |
|
|
if (addSpace) |
if (addSpace) |
Buf_AddSpace(buf); |
Buf_AddSpace(buf); |
Var_SubstVar(buf, v->expand, v->var, word->s); |
Var_SubstVar(buf, v->expand, v->var, word->s); |
return true; |
return true; |
} |
} |
|
|
static char * |
static char * |
|
|
static char * |
static char * |
do_sort(const char *s, const struct Name *dummy UNUSED, void *arg UNUSED) |
do_sort(const char *s, const struct Name *dummy UNUSED, void *arg UNUSED) |
{ |
{ |
struct Name *t; |
struct Name *t; |
unsigned long n, i, j; |
unsigned long n, i, j; |
const char *start, *end; |
const char *start, *end; |
|
|
n = 1024; /* start at 1024 words */ |
n = 1024; /* start at 1024 words */ |
t = (struct Name *)emalloc(sizeof(struct Name) * n); |
t = (struct Name *)emalloc(sizeof(struct Name) * n); |
start = s; |
start = s; |
end = start; |
end = start; |
|
|
for (i = 0;; i++) { |
for (i = 0;; i++) { |
if (i == n) { |
if (i == n) { |
n *= 2; |
n *= 2; |
t = (struct Name *)erealloc(t, sizeof(struct Name) * n); |
t = (struct Name *)erealloc(t, sizeof(struct Name) * n); |
|
} |
|
start = iterate_words(&end); |
|
if (start == NULL) |
|
break; |
|
t[i].s = start; |
|
t[i].e = end; |
} |
} |
start = iterate_words(&end); |
if (i > 0) { |
if (start == NULL) |
BUFFER buf; |
break; |
|
t[i].s = start; |
|
t[i].e = end; |
|
} |
|
if (i > 0) { |
|
BUFFER buf; |
|
|
|
Buf_Init(&buf, end - s); |
Buf_Init(&buf, end - s); |
qsort(t, i, sizeof(struct Name), NameCompare); |
qsort(t, i, sizeof(struct Name), NameCompare); |
Buf_Addi(&buf, t[0].s, t[0].e); |
Buf_Addi(&buf, t[0].s, t[0].e); |
for (j = 1; j < i; j++) { |
for (j = 1; j < i; j++) { |
Buf_AddSpace(&buf); |
Buf_AddSpace(&buf); |
Buf_Addi(&buf, t[j].s, t[j].e); |
Buf_Addi(&buf, t[j].s, t[j].e); |
|
} |
|
free(t); |
|
return Buf_Retrieve(&buf); |
|
} else { |
|
free(t); |
|
return ""; |
} |
} |
free(t); |
|
return Buf_Retrieve(&buf); |
|
} else { |
|
free(t); |
|
return ""; |
|
} |
|
} |
} |
|
|
static char * |
static char * |
do_label(const char *s UNUSED, const struct Name *n, void *arg UNUSED) |
do_label(const char *s UNUSED, const struct Name *n, void *arg UNUSED) |
{ |
{ |
return Str_dupi(n->s, n->e); |
return Str_dupi(n->s, n->e); |
} |
} |
|
|
static char * |
static char * |
do_path(const char *s UNUSED, const struct Name *n, void *arg UNUSED) |
do_path(const char *s UNUSED, const struct Name *n, void *arg UNUSED) |
{ |
{ |
GNode *gn; |
GNode *gn; |
|
|
gn = Targ_FindNodei(n->s, n->e, TARG_NOCREATE); |
gn = Targ_FindNodei(n->s, n->e, TARG_NOCREATE); |
if (gn == NULL) |
if (gn == NULL) |
return Str_dupi(n->s, n->e); |
return Str_dupi(n->s, n->e); |
else |
else |
return strdup(gn->path); |
return strdup(gn->path); |
} |
} |
|
|
static char * |
static char * |
do_def(const char *s, const struct Name *n UNUSED, void *arg) |
do_def(const char *s, const struct Name *n UNUSED, void *arg) |
{ |
{ |
VarPattern *v = (VarPattern *)arg; |
VarPattern *v = (VarPattern *)arg; |
if (s == NULL) { |
if (s == NULL) { |
free_patternarg(v); |
free_patternarg(v); |
return NULL; |
return NULL; |
} else |
} else |
return v->lbuffer; |
return v->lbuffer; |
} |
} |
|
|
static char * |
static char * |
do_undef(const char *s, const struct Name *n UNUSED, void *arg) |
do_undef(const char *s, const struct Name *n UNUSED, void *arg) |
{ |
{ |
VarPattern *v = (VarPattern *)arg; |
VarPattern *v = (VarPattern *)arg; |
if (s != NULL) { |
if (s != NULL) { |
free_patternarg(v); |
free_patternarg(v); |
return NULL; |
return NULL; |
} else |
} else |
return v->lbuffer; |
return v->lbuffer; |
} |
} |
|
|
static char * |
static char * |
do_assign(const char *s, const struct Name *n, void *arg) |
do_assign(const char *s, const struct Name *n, void *arg) |
{ |
{ |
VarPattern *v = (VarPattern *)arg; |
VarPattern *v = (VarPattern *)arg; |
char *msg; |
char *msg; |
char *result; |
char *result; |
|
|
switch (v->flags) { |
switch (v->flags) { |
case VAR_EQUAL: |
case VAR_EQUAL: |
Var_Seti(n->s, n->e, v->lbuffer); |
Var_Seti(n->s, n->e, v->lbuffer); |
break; |
break; |
case VAR_MAY_EQUAL: |
case VAR_MAY_EQUAL: |
if (s == NULL) |
if (s == NULL) |
Var_Seti(n->s, n->e, v->lbuffer); |
Var_Seti(n->s, n->e, v->lbuffer); |
break; |
break; |
case VAR_ADD_EQUAL: |
case VAR_ADD_EQUAL: |
if (s == NULL) |
if (s == NULL) |
Var_Seti(n->s, n->e, v->lbuffer); |
Var_Seti(n->s, n->e, v->lbuffer); |
else |
else |
Var_Appendi(n->s, n->e, v->lbuffer); |
Var_Appendi(n->s, n->e, v->lbuffer); |
break; |
break; |
case VAR_BANG_EQUAL: |
case VAR_BANG_EQUAL: |
result = Cmd_Exec(v->lbuffer, &msg); |
result = Cmd_Exec(v->lbuffer, &msg); |
if (result != NULL) { |
if (result != NULL) { |
Var_Seti(n->s, n->e, result); |
Var_Seti(n->s, n->e, result); |
free(result); |
free(result); |
} else |
} else |
Error(msg, v->lbuffer); |
Error(msg, v->lbuffer); |
break; |
break; |
|
|
} |
} |
return NULL; |
return NULL; |
} |
} |
|
|
static char * |
static char * |
do_exec(const char *s UNUSED, const struct Name *n UNUSED, void *arg) |
do_exec(const char *s UNUSED, const struct Name *n UNUSED, void *arg) |
{ |
{ |
VarPattern *v = (VarPattern *)arg; |
VarPattern *v = (VarPattern *)arg; |
char *msg; |
char *msg; |
char *result; |
char *result; |
|
|
result = Cmd_Exec(v->lbuffer, &msg); |
result = Cmd_Exec(v->lbuffer, &msg); |
if (result == NULL) |
if (result == NULL) |
Error(msg, v->lbuffer); |
Error(msg, v->lbuffer); |
return result; |
return result; |
} |
} |
|
|
/*- |
/*- |
|
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static bool |
static bool |
VarSYSVMatch(struct Name *word, bool addSpace, Buffer buf, |
VarSYSVMatch(struct Name *word, bool addSpace, Buffer buf, void *patp) |
void *patp) /* Pattern the word must match */ |
|
{ |
{ |
size_t len; |
size_t len; |
const char *ptr; |
const char *ptr; |
VarPattern *pat = (VarPattern *)patp; |
VarPattern *pat = (VarPattern *)patp; |
|
|
if (*word->s != '\0') { |
if (*word->s != '\0') { |
if (addSpace) |
if (addSpace) |
Buf_AddSpace(buf); |
Buf_AddSpace(buf); |
if ((ptr = Str_SYSVMatch(word->s, pat->lhs, &len)) != NULL) |
if ((ptr = Str_SYSVMatch(word->s, pat->lhs, &len)) != NULL) |
Str_SYSVSubst(buf, pat->rhs, ptr, len); |
Str_SYSVSubst(buf, pat->rhs, ptr, len); |
else |
else |
Buf_Addi(buf, word->s, word->e); |
Buf_Addi(buf, word->s, word->e); |
return true; |
return true; |
} else |
} else |
return addSpace; |
return addSpace; |
} |
} |
|
|
void * |
void * |
get_sysvpattern(const char **p, SymTable *ctxt UNUSED, bool err UNUSED, |
get_sysvpattern(const char **p, SymTable *ctxt UNUSED, bool err UNUSED, |
int endc) |
int endc) |
{ |
{ |
VarPattern *pattern; |
VarPattern *pattern; |
const char *cp, *cp2; |
const char *cp, *cp2; |
int cnt = 0; |
int cnt = 0; |
char startc = endc == ')' ? '(' : '{'; |
char startc = endc == ')' ? '(' : '{'; |
|
|
for (cp = *p;; cp++) { |
for (cp = *p;; cp++) { |
if (*cp == '=' && cnt == 0) |
if (*cp == '=' && cnt == 0) |
break; |
break; |
if (*cp == '\0') |
if (*cp == '\0') |
return NULL; |
return NULL; |
if (*cp == startc) |
if (*cp == startc) |
cnt++; |
cnt++; |
else if (*cp == endc) { |
else if (*cp == endc) { |
cnt--; |
cnt--; |
if (cnt < 0) |
if (cnt < 0) |
return NULL; |
return NULL; |
|
} |
} |
} |
} |
for (cp2 = cp+1;; cp2++) { |
for (cp2 = cp+1;; cp2++) { |
if ((*cp2 == ':' || *cp2 == endc) && cnt == 0) |
if ((*cp2 == ':' || *cp2 == endc) && cnt == 0) |
break; |
break; |
if (*cp2 == '\0') |
if (*cp2 == '\0') |
return NULL; |
return NULL; |
if (*cp2 == startc) |
if (*cp2 == startc) |
cnt++; |
cnt++; |
else if (*cp2 == endc) { |
else if (*cp2 == endc) { |
cnt--; |
cnt--; |
if (cnt < 0) |
if (cnt < 0) |
return NULL; |
return NULL; |
} |
} |
} |
} |
|
|
pattern = (VarPattern *)emalloc(sizeof(VarPattern)); |
pattern = (VarPattern *)emalloc(sizeof(VarPattern)); |
pattern->lbuffer = pattern->lhs = Str_dupi(*p, cp); |
pattern->lbuffer = pattern->lhs = Str_dupi(*p, cp); |
pattern->leftLen = cp - *p; |
pattern->leftLen = cp - *p; |
pattern->rhs = Str_dupi(cp+1, cp2); |
pattern->rhs = Str_dupi(cp+1, cp2); |
pattern->rightLen = cp2 - (cp+1); |
pattern->rightLen = cp2 - (cp+1); |
pattern->flags = 0; |
pattern->flags = 0; |
*p = cp2; |
*p = cp2; |
return pattern; |
return pattern; |
|
} |
} |
|
|
|
|
|
|
static void |
static void |
VarREError(int err, regex_t *pat, const char *str) |
VarREError(int err, regex_t *pat, const char *str) |
{ |
{ |
char *errbuf; |
char *errbuf; |
int errlen; |
int errlen; |
|
|
errlen = regerror(err, pat, 0, 0); |
errlen = regerror(err, pat, 0, 0); |
errbuf = emalloc(errlen); |
errbuf = emalloc(errlen); |
regerror(err, pat, errbuf, errlen); |
regerror(err, pat, errbuf, errlen); |
Error("%s: %s", str, errbuf); |
Error("%s: %s", str, errbuf); |
free(errbuf); |
free(errbuf); |
} |
} |
|
|
/*- |
/*- |
|
|
static bool |
static bool |
VarRESubstitute(struct Name *word, bool addSpace, Buffer buf, void *patternp) |
VarRESubstitute(struct Name *word, bool addSpace, Buffer buf, void *patternp) |
{ |
{ |
VarREPattern *pat; |
VarREPattern *pat; |
int xrv; |
int xrv; |
const char *wp; |
const char *wp; |
char *rp; |
char *rp; |
int added; |
int added; |
|
|
#define MAYBE_ADD_SPACE() \ |
#define MAYBE_ADD_SPACE() \ |
if (addSpace && !added) \ |
if (addSpace && !added) \ |
Buf_AddSpace(buf); \ |
Buf_AddSpace(buf); \ |
added = 1 |
added = 1 |
|
|
added = 0; |
added = 0; |
wp = word->s; |
wp = word->s; |
pat = patternp; |
pat = patternp; |
|
|
if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) == |
if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) == |
(VAR_SUB_ONE|VAR_SUB_MATCHED)) |
(VAR_SUB_ONE|VAR_SUB_MATCHED)) |
xrv = REG_NOMATCH; |
xrv = REG_NOMATCH; |
else { |
else { |
tryagain: |
tryagain: |
xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, 0); |
xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, 0); |
} |
|
|
|
switch (xrv) { |
|
case 0: |
|
pat->flags |= VAR_SUB_MATCHED; |
|
if (pat->matches[0].rm_so > 0) { |
|
MAYBE_ADD_SPACE(); |
|
Buf_AddChars(buf, pat->matches[0].rm_so, wp); |
|
} |
} |
|
|
for (rp = pat->replace; *rp; rp++) { |
switch (xrv) { |
if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) { |
case 0: |
MAYBE_ADD_SPACE(); |
pat->flags |= VAR_SUB_MATCHED; |
Buf_AddChar(buf,rp[1]); |
if (pat->matches[0].rm_so > 0) { |
rp++; |
MAYBE_ADD_SPACE(); |
} |
Buf_AddChars(buf, pat->matches[0].rm_so, wp); |
else if (*rp == '&' || (*rp == '\\' && isdigit(rp[1]))) { |
|
int n; |
|
const char *subbuf; |
|
int sublen; |
|
char errstr[3]; |
|
|
|
if (*rp == '&') { |
|
n = 0; |
|
errstr[0] = '&'; |
|
errstr[1] = '\0'; |
|
} else { |
|
n = rp[1] - '0'; |
|
errstr[0] = '\\'; |
|
errstr[1] = rp[1]; |
|
errstr[2] = '\0'; |
|
rp++; |
|
} |
} |
|
|
if (n > pat->nsub) { |
for (rp = pat->replace; *rp; rp++) { |
Error("No subexpression %s", &errstr[0]); |
if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) { |
subbuf = ""; |
MAYBE_ADD_SPACE(); |
sublen = 0; |
Buf_AddChar(buf,rp[1]); |
} else if (pat->matches[n].rm_so == -1 && |
rp++; |
pat->matches[n].rm_eo == -1) { |
} |
Error("No match for subexpression %s", &errstr[0]); |
else if (*rp == '&' || |
subbuf = ""; |
(*rp == '\\' && isdigit(rp[1]))) { |
sublen = 0; |
int n; |
} else { |
const char *subbuf; |
subbuf = wp + pat->matches[n].rm_so; |
int sublen; |
sublen = pat->matches[n].rm_eo - pat->matches[n].rm_so; |
char errstr[3]; |
} |
|
|
|
if (sublen > 0) { |
if (*rp == '&') { |
MAYBE_ADD_SPACE(); |
n = 0; |
Buf_AddChars(buf, sublen, subbuf); |
errstr[0] = '&'; |
|
errstr[1] = '\0'; |
|
} else { |
|
n = rp[1] - '0'; |
|
errstr[0] = '\\'; |
|
errstr[1] = rp[1]; |
|
errstr[2] = '\0'; |
|
rp++; |
|
} |
|
|
|
if (n > pat->nsub) { |
|
Error("No subexpression %s", |
|
&errstr[0]); |
|
subbuf = ""; |
|
sublen = 0; |
|
} else if (pat->matches[n].rm_so == -1 && |
|
pat->matches[n].rm_eo == -1) { |
|
Error("No match for subexpression %s", |
|
&errstr[0]); |
|
subbuf = ""; |
|
sublen = 0; |
|
} else { |
|
subbuf = wp + pat->matches[n].rm_so; |
|
sublen = pat->matches[n].rm_eo - |
|
pat->matches[n].rm_so; |
|
} |
|
|
|
if (sublen > 0) { |
|
MAYBE_ADD_SPACE(); |
|
Buf_AddChars(buf, sublen, subbuf); |
|
} |
|
} else { |
|
MAYBE_ADD_SPACE(); |
|
Buf_AddChar(buf, *rp); |
|
} |
} |
} |
} else { |
wp += pat->matches[0].rm_eo; |
MAYBE_ADD_SPACE(); |
if (pat->flags & VAR_SUB_GLOBAL) |
Buf_AddChar(buf, *rp); |
goto tryagain; |
} |
if (*wp) { |
|
MAYBE_ADD_SPACE(); |
|
Buf_AddString(buf, wp); |
|
} |
|
break; |
|
default: |
|
VarREError(xrv, &pat->re, "Unexpected regex error"); |
|
/* fall through */ |
|
case REG_NOMATCH: |
|
if (*wp) { |
|
MAYBE_ADD_SPACE(); |
|
Buf_AddString(buf, wp); |
|
} |
|
break; |
} |
} |
wp += pat->matches[0].rm_eo; |
return addSpace||added; |
if (pat->flags & VAR_SUB_GLOBAL) |
|
goto tryagain; |
|
if (*wp) { |
|
MAYBE_ADD_SPACE(); |
|
Buf_AddString(buf, wp); |
|
} |
|
break; |
|
default: |
|
VarREError(xrv, &pat->re, "Unexpected regex error"); |
|
/* fall through */ |
|
case REG_NOMATCH: |
|
if (*wp) { |
|
MAYBE_ADD_SPACE(); |
|
Buf_AddString(buf, wp); |
|
} |
|
break; |
|
} |
|
return addSpace||added; |
|
} |
} |
#endif |
#endif |
|
|
|
|
bool (*modProc)(struct Name *, bool, Buffer, void *), |
bool (*modProc)(struct Name *, bool, Buffer, void *), |
void *datum) /* Datum to pass it */ |
void *datum) /* Datum to pass it */ |
{ |
{ |
BUFFER buf; /* Buffer for the new string */ |
BUFFER buf; /* Buffer for the new string */ |
bool addSpace; /* true if need to add a space to the |
bool addSpace; /* true if need to add a space to the |
* buffer before adding the trimmed |
* buffer before adding the trimmed |
* word */ |
* word */ |
struct Name word; |
struct Name word; |
|
|
Buf_Init(&buf, 0); |
Buf_Init(&buf, 0); |
addSpace = false; |
addSpace = false; |
|
|
word.e = str; |
word.e = str; |
|
|
while ((word.s = iterate_words(&word.e)) != NULL) { |
while ((word.s = iterate_words(&word.e)) != NULL) { |
char termc; |
char termc; |
|
|
termc = *word.e; |
termc = *word.e; |
*((char *)(word.e)) = '\0'; |
*((char *)(word.e)) = '\0'; |
addSpace = (*modProc)(&word, addSpace, &buf, datum); |
addSpace = (*modProc)(&word, addSpace, &buf, datum); |
*((char *)(word.e)) = termc; |
*((char *)(word.e)) = termc; |
} |
} |
return Buf_Retrieve(&buf); |
return Buf_Retrieve(&buf); |
} |
} |
|
|
/*- |
/*- |
|
|
VarGetPattern(SymTable *ctxt, int err, const char **tstr, int delim1, |
VarGetPattern(SymTable *ctxt, int err, const char **tstr, int delim1, |
int delim2, size_t *length, VarPattern *pattern) |
int delim2, size_t *length, VarPattern *pattern) |
{ |
{ |
const char *cp; |
const char *cp; |
char *result; |
char *result; |
BUFFER buf; |
BUFFER buf; |
size_t junk; |
size_t junk; |
|
|
Buf_Init(&buf, 0); |
Buf_Init(&buf, 0); |
if (length == NULL) |
if (length == NULL) |
length = &junk; |
length = &junk; |
|
|
#define IS_A_MATCH(cp, delim1, delim2) \ |
#define IS_A_MATCH(cp, delim1, delim2) \ |
(cp[0] == '\\' && (cp[1] == delim1 || cp[1] == delim2 || \ |
(cp[0] == '\\' && (cp[1] == delim1 || cp[1] == delim2 || \ |
cp[1] == '\\' || cp[1] == '$' || (pattern && cp[1] == '&'))) |
cp[1] == '\\' || cp[1] == '$' || (pattern && cp[1] == '&'))) |
|
|
/* |
/* |
* Skim through until the matching delimiter is found; |
* Skim through until the matching delimiter is found; |
* pick up variable substitutions on the way. Also allow |
* pick up variable substitutions on the way. Also allow |
* backslashes to quote the delimiter, $, and \, but don't |
* backslashes to quote the delimiter, $, and \, but don't |
* touch other backslashes. |
* touch other backslashes. |
*/ |
*/ |
for (cp = *tstr; *cp != '\0' && *cp != delim1 && *cp != delim2; cp++) { |
for (cp = *tstr; *cp != '\0' && *cp != delim1 && *cp != delim2; cp++) { |
if (IS_A_MATCH(cp, delim1, delim2)) { |
if (IS_A_MATCH(cp, delim1, delim2)) { |
Buf_AddChar(&buf, cp[1]); |
Buf_AddChar(&buf, cp[1]); |
cp++; |
cp++; |
} else if (*cp == '$') { |
} else if (*cp == '$') { |
/* Allowed at end of pattern */ |
/* Allowed at end of pattern */ |
if (cp[1] == delim1 || cp[1] == delim2) |
if (cp[1] == delim1 || cp[1] == delim2) |
Buf_AddChar(&buf, *cp); |
Buf_AddChar(&buf, *cp); |
else { |
else { |
size_t len; |
size_t len; |
|
|
/* If unescaped dollar sign not before the delimiter, |
/* If unescaped dollar sign not before the |
* assume it's a variable substitution and recurse. */ |
* delimiter, assume it's a variable |
(void)Var_ParseBuffer(&buf, cp, ctxt, err, &len); |
* substitution and recurse. */ |
cp += len - 1; |
(void)Var_ParseBuffer(&buf, cp, ctxt, err, |
} |
&len); |
} else if (pattern && *cp == '&') |
cp += len - 1; |
Buf_AddChars(&buf, pattern->leftLen, pattern->lhs); |
} |
else |
} else if (pattern && *cp == '&') |
Buf_AddChar(&buf, *cp); |
Buf_AddChars(&buf, pattern->leftLen, pattern->lhs); |
} |
else |
|
Buf_AddChar(&buf, *cp); |
|
} |
|
|
*length = Buf_Size(&buf); |
*length = Buf_Size(&buf); |
result = Buf_Retrieve(&buf); |
result = Buf_Retrieve(&buf); |
|
|
if (*cp != delim1 && *cp != delim2) { |
if (*cp != delim1 && *cp != delim2) { |
*tstr = cp; |
*tstr = cp; |
*length = 0; |
*length = 0; |
free(result); |
free(result); |
return NULL; |
return NULL; |
} |
} |
else { |
else { |
*tstr = ++cp; |
*tstr = ++cp; |
return result; |
return result; |
} |
} |
} |
} |
|
|
/*- |
/*- |
|
|
VarQuote(const char *str, const struct Name *n UNUSED, void *dummy UNUSED) |
VarQuote(const char *str, const struct Name *n UNUSED, void *dummy UNUSED) |
{ |
{ |
|
|
BUFFER buf; |
BUFFER buf; |
/* This should cover most shells :-( */ |
/* This should cover most shells :-( */ |
static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~"; |
static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~"; |
|
|
Buf_Init(&buf, MAKE_BSIZE); |
Buf_Init(&buf, MAKE_BSIZE); |
for (; *str; str++) { |
for (; *str; str++) { |
if (strchr(meta, *str) != NULL) |
if (strchr(meta, *str) != NULL) |
Buf_AddChar(&buf, '\\'); |
Buf_AddChar(&buf, '\\'); |
Buf_AddChar(&buf, *str); |
Buf_AddChar(&buf, *str); |
} |
} |
return Buf_Retrieve(&buf); |
return Buf_Retrieve(&buf); |
} |
} |
|
|
static void * |
static void * |
check_empty(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) |
check_empty(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) |
{ |
{ |
dummy_arg->s = NULL; |
dummy_arg->s = NULL; |
if ((*p)[1] == endc || (*p)[1] == ':') { |
if ((*p)[1] == endc || (*p)[1] == ':') { |
(*p)++; |
(*p)++; |
return dummy_arg; |
return dummy_arg; |
} else |
} else |
return NULL; |
return NULL; |
} |
} |
|
|
static void * |
static void * |
check_shcmd(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) |
check_shcmd(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) |
{ |
{ |
if ((*p)[1] == 'h' && ((*p)[2] == endc || (*p)[2] == ':')) { |
if ((*p)[1] == 'h' && ((*p)[2] == endc || (*p)[2] == ':')) { |
(*p)+=2; |
(*p)+=2; |
return dummy_arg; |
return dummy_arg; |
} else |
} else |
return NULL; |
return NULL; |
} |
} |
|
|
|
|
static char * |
static char * |
do_shcmd(const char *s, const struct Name *n UNUSED, void *arg UNUSED) |
do_shcmd(const char *s, const struct Name *n UNUSED, void *arg UNUSED) |
{ |
{ |
char *err; |
char *err; |
char *t; |
char *t; |
|
|
t = Cmd_Exec(s, &err); |
t = Cmd_Exec(s, &err); |
if (err) |
if (err) |
Error(err, s); |
Error(err, s); |
return t; |
return t; |
} |
} |
|
|
static void * |
static void * |
get_stringarg(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) |
get_stringarg(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) |
{ |
{ |
const char *cp; |
const char *cp; |
char *s; |
char *s; |
|
|
for (cp = *p + 1; *cp != ':' && *cp != endc; cp++) { |
for (cp = *p + 1; *cp != ':' && *cp != endc; cp++) { |
if (*cp == '\\') { |
if (*cp == '\\') { |
if (cp[1] == ':' || cp[1] == endc || cp[1] == '\\') |
if (cp[1] == ':' || cp[1] == endc || cp[1] == '\\') |
cp++; |
cp++; |
} else if (*cp == '\0') |
} else if (*cp == '\0') |
return NULL; |
return NULL; |
} |
} |
s = escape_dupi(*p+1, cp, ":)}"); |
s = escape_dupi(*p+1, cp, ":)}"); |
*p = cp; |
*p = cp; |
return s; |
return s; |
} |
} |
|
|
static void |
static void |
free_stringarg(void *arg) |
free_stringarg(void *arg) |
{ |
{ |
free(arg); |
free(arg); |
} |
} |
|
|
static char * |
static char * |
do_upper(const char *s, const struct Name *n UNUSED, void *arg UNUSED) |
do_upper(const char *s, const struct Name *n UNUSED, void *arg UNUSED) |
{ |
{ |
size_t len, i; |
size_t len, i; |
char *t; |
char *t; |
|
|
len = strlen(s); |
len = strlen(s); |
t = emalloc(len+1); |
t = emalloc(len+1); |
for (i = 0; i < len; i++) |
for (i = 0; i < len; i++) |
t[i] = toupper(s[i]); |
t[i] = toupper(s[i]); |
t[len] = '\0'; |
t[len] = '\0'; |
return t; |
return t; |
} |
} |
|
|
static char * |
static char * |
do_lower(const char *s, const struct Name *n UNUSED, void *arg UNUSED) |
do_lower(const char *s, const struct Name *n UNUSED, void *arg UNUSED) |
{ |
{ |
size_t len, i; |
size_t len, i; |
char *t; |
char *t; |
|
|
len = strlen(s); |
len = strlen(s); |
t = emalloc(len+1); |
t = emalloc(len+1); |
for (i = 0; i < len; i++) |
for (i = 0; i < len; i++) |
t[i] = tolower(s[i]); |
t[i] = tolower(s[i]); |
t[len] = '\0'; |
t[len] = '\0'; |
return t; |
return t; |
} |
} |
|
|
static void * |
static void * |
get_patternarg(const char **p, SymTable *ctxt, bool err, int endc) |
get_patternarg(const char **p, SymTable *ctxt, bool err, int endc) |
{ |
{ |
return common_get_patternarg(p, ctxt, err, endc, false); |
return common_get_patternarg(p, ctxt, err, endc, false); |
} |
} |
|
|
/* Extract anchors */ |
/* Extract anchors */ |
static void * |
static void * |
get_spatternarg(const char **p, SymTable *ctxt, bool err, int endc) |
get_spatternarg(const char **p, SymTable *ctxt, bool err, int endc) |
{ |
{ |
VarPattern *pattern; |
VarPattern *pattern; |
|
|
pattern = common_get_patternarg(p, ctxt, err, endc, true); |
pattern = common_get_patternarg(p, ctxt, err, endc, true); |
if (pattern != NULL && pattern->leftLen > 0) { |
if (pattern != NULL && pattern->leftLen > 0) { |
if (pattern->lhs[pattern->leftLen-1] == '$') { |
if (pattern->lhs[pattern->leftLen-1] == '$') { |
pattern->leftLen--; |
pattern->leftLen--; |
pattern->flags |= VAR_MATCH_END; |
pattern->flags |= VAR_MATCH_END; |
} |
|
if (pattern->lhs[0] == '^') { |
|
pattern->lhs++; |
|
pattern->leftLen--; |
|
pattern->flags |= VAR_MATCH_START; |
|
} |
} |
} |
if (pattern->lhs[0] == '^') { |
return pattern; |
pattern->lhs++; |
|
pattern->leftLen--; |
|
pattern->flags |= VAR_MATCH_START; |
|
} |
|
} |
|
return pattern; |
} |
} |
|
|
static void |
static void |
free_looparg(void *arg) |
free_looparg(void *arg) |
{ |
{ |
struct LoopStuff *l = (struct LoopStuff *)arg; |
struct LoopStuff *l = (struct LoopStuff *)arg; |
|
|
Var_DeleteLoopVar(l->var); |
Var_DeleteLoopVar(l->var); |
free(l->expand); |
free(l->expand); |
} |
} |
|
|
static char * |
static char * |
LoopGrab(const char **s) |
LoopGrab(const char **s) |
{ |
{ |
const char *p, *start; |
const char *p, *start; |
|
|
start = *s; |
start = *s; |
for (p = start; *p != '@'; p++) { |
for (p = start; *p != '@'; p++) { |
if (*p == '\\') |
if (*p == '\\') |
p++; |
p++; |
if (*p == 0) |
if (*p == 0) |
return NULL; |
return NULL; |
} |
} |
*s = p+1; |
*s = p+1; |
return escape_dupi(start, p, "@\\"); |
return escape_dupi(start, p, "@\\"); |
} |
} |
|
|
static void * |
static void * |
get_loop(const char **p, SymTable *ctxt UNUSED, bool err, int endc) |
get_loop(const char **p, SymTable *ctxt UNUSED, bool err, int endc) |
{ |
{ |
static struct LoopStuff loop; |
static struct LoopStuff loop; |
const char *s; |
const char *s; |
const char *var; |
const char *var; |
|
|
s = *p +1; |
s = *p +1; |
|
|
loop.var = NULL; |
loop.var = NULL; |
loop.expand = NULL; |
loop.expand = NULL; |
loop.err = err; |
loop.err = err; |
var = LoopGrab(&s); |
var = LoopGrab(&s); |
if (var != NULL) { |
if (var != NULL) { |
loop.expand = LoopGrab(&s); |
loop.expand = LoopGrab(&s); |
if (*s == endc || *s == ':') { |
if (*s == endc || *s == ':') { |
*p = s; |
*p = s; |
loop.var = Var_NewLoopVar(var, NULL); |
loop.var = Var_NewLoopVar(var, NULL); |
return &loop; |
return &loop; |
|
} |
} |
} |
} |
free_looparg(&loop); |
free_looparg(&loop); |
return NULL; |
return NULL; |
|
} |
} |
|
|
static void * |
static void * |
common_get_patternarg(const char **p, SymTable *ctxt, bool err, int endc, |
common_get_patternarg(const char **p, SymTable *ctxt, bool err, int endc, |
bool dosubst) |
bool dosubst) |
{ |
{ |
VarPattern *pattern; |
VarPattern *pattern; |
char delim; |
char delim; |
const char *s; |
const char *s; |
|
|
pattern = (VarPattern *)emalloc(sizeof(VarPattern)); |
pattern = (VarPattern *)emalloc(sizeof(VarPattern)); |
pattern->flags = 0; |
pattern->flags = 0; |
s = *p; |
s = *p; |
|
|
delim = s[1]; |
delim = s[1]; |
if (delim == '\0') |
if (delim == '\0') |
return NULL; |
return NULL; |
s += 2; |
s += 2; |
|
|
pattern->rhs = NULL; |
pattern->rhs = NULL; |
pattern->lhs = VarGetPattern(ctxt, err, &s, delim, delim, |
pattern->lhs = VarGetPattern(ctxt, err, &s, delim, delim, |
&pattern->leftLen, NULL); |
&pattern->leftLen, NULL); |
pattern->lbuffer = pattern->lhs; |
pattern->lbuffer = pattern->lhs; |
if (pattern->lhs != NULL) { |
if (pattern->lhs != NULL) { |
pattern->rhs = VarGetPattern(ctxt, err, &s, delim, delim, |
pattern->rhs = VarGetPattern(ctxt, err, &s, delim, delim, |
&pattern->rightLen, dosubst ? pattern: NULL); |
&pattern->rightLen, dosubst ? pattern: NULL); |
if (pattern->rhs != NULL) { |
if (pattern->rhs != NULL) { |
/* Check for global substitution. If 'g' after the final |
/* Check for global substitution. If 'g' after the |
* delimiter, substitution is global and is marked that |
* final delimiter, substitution is global and is |
* way. */ |
* marked that way. */ |
for (;; s++) { |
for (;; s++) { |
switch (*s) { |
switch (*s) { |
case 'g': |
case 'g': |
pattern->flags |= VAR_SUB_GLOBAL; |
pattern->flags |= VAR_SUB_GLOBAL; |
continue; |
continue; |
case '1': |
case '1': |
pattern->flags |= VAR_SUB_ONE; |
pattern->flags |= VAR_SUB_ONE; |
continue; |
continue; |
|
} |
|
break; |
|
} |
|
if (*s == endc || *s == ':') { |
|
*p = s; |
|
return pattern; |
|
} |
} |
} |
break; |
|
} |
|
if (*s == endc || *s == ':') { |
|
*p = s; |
|
return pattern; |
|
} |
|
} |
} |
} |
free_patternarg(pattern); |
free_patternarg(pattern); |
return NULL; |
return NULL; |
|
} |
} |
|
|
static void * |
static void * |
assign_get_value(const char **p, SymTable *ctxt, bool err, int endc) |
assign_get_value(const char **p, SymTable *ctxt, bool err, int endc) |
{ |
{ |
const char *s; |
const char *s; |
int flags; |
int flags; |
VarPattern *arg; |
VarPattern *arg; |
|
|
s = *p + 1; |
s = *p + 1; |
if (s[0] == '=') |
if (s[0] == '=') |
flags = VAR_EQUAL; |
flags = VAR_EQUAL; |
else if (s[0] == '?' && s[1] == '=') |
else if (s[0] == '?' && s[1] == '=') |
flags = VAR_MAY_EQUAL; |
flags = VAR_MAY_EQUAL; |
else if (s[0] == '+' && s[1] == '=') |
else if (s[0] == '+' && s[1] == '=') |
flags = VAR_ADD_EQUAL; |
flags = VAR_ADD_EQUAL; |
else if (s[0] == '!' && s[1] == '=') |
else if (s[0] == '!' && s[1] == '=') |
flags = VAR_BANG_EQUAL; |
flags = VAR_BANG_EQUAL; |
else |
else |
return NULL; |
return NULL; |
|
|
arg = get_value(&s, ctxt, err, endc); |
arg = get_value(&s, ctxt, err, endc); |
if (arg != NULL) { |
if (arg != NULL) { |
*p = s; |
*p = s; |
arg->flags = flags; |
arg->flags = flags; |
} |
} |
return arg; |
return arg; |
} |
} |
|
|
static void * |
static void * |
get_value(const char **p, SymTable *ctxt, bool err, int endc) |
get_value(const char **p, SymTable *ctxt, bool err, int endc) |
{ |
{ |
VarPattern *pattern; |
VarPattern *pattern; |
const char *s; |
const char *s; |
|
|
pattern = (VarPattern *)emalloc(sizeof(VarPattern)); |
pattern = (VarPattern *)emalloc(sizeof(VarPattern)); |
s = *p + 1; |
s = *p + 1; |
pattern->rhs = NULL; |
pattern->rhs = NULL; |
pattern->lbuffer = VarGetPattern(ctxt, err, &s, ':', endc, |
pattern->lbuffer = VarGetPattern(ctxt, err, &s, ':', endc, |
&pattern->leftLen, NULL); |
&pattern->leftLen, NULL); |
if (s[-1] == endc || s[-1] == ':') { |
if (s[-1] == endc || s[-1] == ':') { |
*p = s-1; |
*p = s-1; |
return pattern; |
return pattern; |
} |
} |
free_patternarg(pattern); |
free_patternarg(pattern); |
return NULL; |
return NULL; |
} |
} |
|
|
static void * |
static void * |
get_cmd(const char **p, SymTable *ctxt, bool err, int endc UNUSED) |
get_cmd(const char **p, SymTable *ctxt, bool err, int endc UNUSED) |
{ |
{ |
VarPattern *pattern; |
VarPattern *pattern; |
const char *s; |
const char *s; |
|
|
pattern = (VarPattern *)emalloc(sizeof(VarPattern)); |
pattern = (VarPattern *)emalloc(sizeof(VarPattern)); |
s = *p + 1; |
s = *p + 1; |
pattern->rhs = NULL; |
pattern->rhs = NULL; |
pattern->lbuffer = VarGetPattern(ctxt, err, &s, '!', '!', |
pattern->lbuffer = VarGetPattern(ctxt, err, &s, '!', '!', |
&pattern->leftLen, NULL); |
&pattern->leftLen, NULL); |
if (s[-1] == '!') { |
if (s[-1] == '!') { |
*p = s-1; |
*p = s-1; |
return pattern; |
return pattern; |
} |
} |
free_patternarg(pattern); |
free_patternarg(pattern); |
return NULL; |
return NULL; |
} |
} |
|
|
static void |
static void |
free_patternarg(void *p) |
free_patternarg(void *p) |
{ |
{ |
VarPattern *vp = (VarPattern *)p; |
VarPattern *vp = (VarPattern *)p; |
|
|
free(vp->lbuffer); |
free(vp->lbuffer); |
free(vp->rhs); |
free(vp->rhs); |
free(vp); |
free(vp); |
} |
} |
|
|
#ifndef MAKE_BOOTSTRAP |
#ifndef MAKE_BOOTSTRAP |
static char * |
static char * |
do_regex(const char *s, const struct Name *n UNUSED, void *arg) |
do_regex(const char *s, const struct Name *n UNUSED, void *arg) |
{ |
{ |
VarREPattern p2; |
VarREPattern p2; |
VarPattern *p = (VarPattern *)arg; |
VarPattern *p = (VarPattern *)arg; |
int error; |
int error; |
char *result; |
char *result; |
|
|
error = regcomp(&p2.re, p->lhs, REG_EXTENDED); |
error = regcomp(&p2.re, p->lhs, REG_EXTENDED); |
if (error) { |
if (error) { |
VarREError(error, &p2.re, "RE substitution error"); |
VarREError(error, &p2.re, "RE substitution error"); |
return var_Error; |
return var_Error; |
} |
} |
p2.nsub = p2.re.re_nsub + 1; |
p2.nsub = p2.re.re_nsub + 1; |
p2.replace = p->rhs; |
p2.replace = p->rhs; |
p2.flags = p->flags; |
p2.flags = p->flags; |
if (p2.nsub < 1) |
if (p2.nsub < 1) |
p2.nsub = 1; |
p2.nsub = 1; |
if (p2.nsub > 10) |
if (p2.nsub > 10) |
p2.nsub = 10; |
p2.nsub = 10; |
p2.matches = emalloc(p2.nsub * sizeof(regmatch_t)); |
p2.matches = emalloc(p2.nsub * sizeof(regmatch_t)); |
result = VarModify((char *)s, VarRESubstitute, &p2); |
result = VarModify((char *)s, VarRESubstitute, &p2); |
regfree(&p2.re); |
regfree(&p2.re); |
free(p2.matches); |
free(p2.matches); |
return result; |
return result; |
} |
} |
#endif |
#endif |
|
|
|
|
VarModifiers_Apply(char *str, const struct Name *name, SymTable *ctxt, |
VarModifiers_Apply(char *str, const struct Name *name, SymTable *ctxt, |
bool err, bool *freePtr, const char **pscan, int paren) |
bool err, bool *freePtr, const char **pscan, int paren) |
{ |
{ |
const char *tstr; |
const char *tstr; |
bool atstart; /* Some ODE modifiers only make sense at start */ |
bool atstart; /* Some ODE modifiers only make sense at start */ |
char endc = paren == '(' ? ')' : '}'; |
char endc = paren == '(' ? ')' : '}'; |
const char *start = *pscan; |
const char *start = *pscan; |
|
|
tstr = start; |
tstr = start; |
/* |
/* |
* Now we need to apply any modifiers the user wants applied. |
* Now we need to apply any modifiers the user wants applied. |
* These are: |
* These are: |
* :M<pattern> words which match the given <pattern>. |
* :M<pattern> words which match the given <pattern>. |
* <pattern> is of the standard file |
* <pattern> is of the standard file |
* wildcarding form. |
* wildcarding form. |
* :S<d><pat1><d><pat2><d>[g] |
* :S<d><pat1><d><pat2><d>[g] |
* Substitute <pat2> for <pat1> in the value |
* Substitute <pat2> for <pat1> in the |
* :C<d><pat1><d><pat2><d>[g] |
* value |
* Substitute <pat2> for regex <pat1> in the value |
* :C<d><pat1><d><pat2><d>[g] |
* :H Substitute the head of each word |
* Substitute <pat2> for regex <pat1> in |
* :T Substitute the tail of each word |
* the value |
* :E Substitute the extension (minus '.') of |
* :H Substitute the head of each word |
* each word |
* :T Substitute the tail of each word |
* :R Substitute the root of each word |
* :E Substitute the extension (minus '.') of |
* (pathname minus the suffix). |
* each word |
* :lhs=rhs Like :S, but the rhs goes to the end of |
* :R Substitute the root of each word |
* the invocation. |
* (pathname minus the suffix). |
*/ |
* :lhs=rhs Like :S, but the rhs goes to the end of |
|
* the invocation. |
atstart = true; |
*/ |
while (*tstr != endc && *tstr != '\0') { |
|
struct modifier *mod; |
atstart = true; |
void *arg; |
while (*tstr != endc && *tstr != '\0') { |
char *newStr; |
struct modifier *mod; |
|
void *arg; |
|
char *newStr; |
|
|
tstr++; |
tstr++; |
if (DEBUG(VAR)) |
if (DEBUG(VAR)) |
printf("Applying :%c to \"%s\"\n", *tstr, str); |
printf("Applying :%c to \"%s\"\n", *tstr, str); |
|
|
mod = choose_mod[*tstr]; |
mod = choose_mod[*tstr]; |
arg = NULL; |
arg = NULL; |
|
|
if (mod != NULL && (!mod->atstart || atstart)) |
if (mod != NULL && (!mod->atstart || atstart)) |
arg = mod->getarg(&tstr, ctxt, err, endc); |
arg = mod->getarg(&tstr, ctxt, err, endc); |
if (FEATURES(FEATURE_SYSVVARSUB) && arg == NULL) { |
if (FEATURES(FEATURE_SYSVVARSUB) && arg == NULL) { |
mod = &sysv_mod; |
mod = &sysv_mod; |
arg = mod->getarg(&tstr, ctxt, err, endc); |
arg = mod->getarg(&tstr, ctxt, err, endc); |
} |
} |
atstart = false; |
atstart = false; |
if (arg != NULL) { |
if (arg != NULL) { |
if (str != NULL || (mod->atstart && name != NULL)) { |
if (str != NULL || (mod->atstart && name != NULL)) { |
if (mod->word_apply != NULL) { |
if (mod->word_apply != NULL) { |
newStr = VarModify(str, mod->word_apply, arg); |
newStr = VarModify(str, |
if (mod->apply != NULL) { |
mod->word_apply, arg); |
char *newStr2; |
if (mod->apply != NULL) { |
|
char *newStr2; |
|
|
newStr2 = mod->apply(newStr, name, arg); |
newStr2 = mod->apply(newStr, |
free(newStr); |
name, arg); |
newStr = newStr2; |
free(newStr); |
} |
newStr = newStr2; |
} else |
} |
newStr = mod->apply(str, name, arg); |
} else |
if (*freePtr) |
newStr = mod->apply(str, name, arg); |
free(str); |
if (*freePtr) |
str = newStr; |
free(str); |
if (str != var_Error) |
str = newStr; |
*freePtr = true; |
if (str != var_Error) |
else |
*freePtr = true; |
*freePtr = false; |
else |
} |
*freePtr = false; |
if (mod->freearg != NULL) |
} |
mod->freearg(arg); |
if (mod->freearg != NULL) |
} else { |
mod->freearg(arg); |
Error("Bad modifier: %s\n", tstr); |
} else { |
/* Try skipping to end of var... */ |
Error("Bad modifier: %s\n", tstr); |
for (tstr++; *tstr != endc && *tstr != '\0';) |
/* Try skipping to end of var... */ |
tstr++; |
for (tstr++; *tstr != endc && *tstr != '\0';) |
if (str != NULL && *freePtr) |
tstr++; |
free(str); |
if (str != NULL && *freePtr) |
str = var_Error; |
free(str); |
*freePtr = false; |
str = var_Error; |
break; |
*freePtr = false; |
|
break; |
|
} |
|
if (DEBUG(VAR)) |
|
printf("Result is \"%s\"\n", str); |
} |
} |
if (DEBUG(VAR)) |
if (*tstr == '\0') |
printf("Result is \"%s\"\n", str); |
Error("Unclosed variable specification"); |
} |
else |
if (*tstr == '\0') |
tstr++; |
Error("Unclosed variable specification"); |
|
else |
|
tstr++; |
|
|
|
*pscan = tstr; |
*pscan = tstr; |
return str; |
return str; |
} |
} |
|
|
char * |
char * |
Var_GetHead(char *s) |
Var_GetHead(char *s) |
{ |
{ |
return VarModify(s, VarHead, NULL); |
return VarModify(s, VarHead, NULL); |
} |
} |
|
|
char * |
char * |
Var_GetTail(char *s) |
Var_GetTail(char *s) |
{ |
{ |
return VarModify(s, VarTail, NULL); |
return VarModify(s, VarTail, NULL); |
} |
} |