/* * subex.c - Substitution (execution) * * Copyright 2012 by Werner Almesberger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include #include #include #include #include #include #include #include "util.h" #include "vstring.h" #include "relop.h" #include "subst.h" #include "subex.h" #define FIELDS 10 static const char *fn = NULL, *f[FIELDS]; /* Jump target that can never be reached. */ static struct subst jump_end; static char *canonicalize(const char *s, char unit) { char *res; int res_len = 0; int seen_dot = 0; int seen_unit = 0; char mult = 0; if (!unit) return stralloc(s); res = stralloc(""); while (*s) { if (*s == unit) { assert(!seen_unit); if (!s[1]) break; if (!seen_dot) append_char(&res, &res_len, '.'); seen_unit = seen_dot = 1; s++; continue; } if (*s == '.') { assert(!seen_dot); append_char(&res, &res_len, '.'); seen_dot = 1; } else if (*s == '-' || isdigit(*s)) { append_char(&res, &res_len, *s); s++; continue; } else if (strchr(MULT_CHARS, *s)) { assert(!seen_unit); assert(!mult); mult = *s; if (!seen_dot) append_char(&res, &res_len, '.'); seen_dot = 1; } else { abort(); } s++; } if (res_len && res[res_len-1] == '.') res_len--; if (mult) append_char(&res, &res_len, mult); append_char(&res, &res_len, unit); return res; } static char *compose(const struct chunk *c, const struct var *in, const struct var *out, const char *s, const regmatch_t *match, const char *units) { char *res = stralloc(""); int res_len = 0, len; const char *val; char *tmp, *tmp2; int n; while (c) { switch (c->type) { case ct_string: append(&res, &res_len, c->u.s); break; case ct_var: val = var_lookup(in, c->u.var); if (!val) val = var_lookup(out, c->u.var); assert(val); append(&res, &res_len, val); break; case ct_sub: n = c->u.sub; assert(s); if (!n) { append(&res, &res_len, s); break; } if (match[n-1].rm_so == -1) break; len = match[n-1].rm_eo-match[n-1].rm_so; tmp = alloc_size(len); memcpy(tmp, s+match[n-1].rm_so, len); tmp[len] = 0; tmp2 = canonicalize(tmp, units ? units[n-1] : 0); append(&res, &res_len, tmp2); free(tmp); free(tmp2); break; default: abort(); } c = c->next; } return res; } struct var *make_var(const char *name, enum relop op, const char *val) { struct var *var; var = alloc_type(struct var); var->name = unique(name); var->op = op; var->value = unique(val); var->next = NULL; return var; } static void do_assign(const char *name, struct var **out, enum relop op, const char *val) { while (*out) { if ((*out)->name == name) break; out = &(*out)->next; } if (*out) { free((void *) (*out)->value); (*out)->value = val; } else { *out = make_var(name, op, val); } } static int do_match_1(const char *var, const regex_t *re, const struct var *in, const struct var *out, const char **val, regmatch_t *match) { *val = var_lookup(out, var); if (!*val) *val = var_lookup(in, var); if (!*val) return -1; return regexec(re, *val, 10, match, 0); } static int do_match(const char *var, const regex_t *re, const struct var *in, const struct var *out, const char **val, regmatch_t *match) { int i; if (var != fn) return do_match_1(var, re, in, out, val, match); for (i = 0; i != FIELDS; i++) if (!do_match_1(f[i], re, in, out, val, match)) return 0; return -1; } static const struct subst *recurse_sub(const struct subst *sub, const struct var *in, const char *s, const regmatch_t *match, const char *units, struct var **out) { const struct subst *jump; regmatch_t m_tmp[10]; const char *val; char *tmp; while (sub) { switch (sub->type) { case st_match: if (do_match(sub->u.match.src, &sub->u.match.re, in, *out, &val, m_tmp)) break; jump = recurse_sub(sub->u.match.block, in, val, m_tmp, sub->u.match.units, out); if (jump && jump != sub) return jump; break; case st_assign: tmp = compose(sub->u.assign.pat, in, *out, s, match, units); do_assign(sub->u.assign.dst, out, sub->u.assign.op, tmp); break; case st_end: return &jump_end; case st_break: sub = sub->u.jump->next; continue; case st_again: sub = sub->u.jump->u.match.block; continue; default: abort(); } sub = sub->next; } return NULL; } struct var *substitute(const struct subst *sub, const struct var *in) { struct var *out = NULL; int i; char tmp[4]; if (!fn) { fn = unique("FN"); for (i = 0; i != FIELDS; i++) { sprintf(tmp, "F%d", i); f[i] = unique(tmp); } } recurse_sub(sub, in, NULL, NULL, NULL, &out); return out; } const char *var_lookup(const struct var *vars, const char *name) { while (vars) { if (vars->name == name) return vars->value; vars = vars->next; } return NULL; } void free_vars(struct var *vars) { struct var *next; while (vars) { next = vars->next; free(vars); vars = next; } }