diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2010-06-21 08:50:18 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2010-06-21 08:50:18 +0300 |
commit | b21233c538ebb235a780e068641263801730339d (patch) | |
tree | 1c12e1361b1009ecc73d448e9095e400f4c8c1b7 | |
parent | f74f474c5648f38f044aa8d717496016c3ea6606 (diff) | |
download | smap-b21233c538ebb235a780e068641263801730339d.tar.gz smap-b21233c538ebb235a780e068641263801730339d.tar.bz2 |
Implement variable expansion.
* include/smap/wordsplit.h (WRDSE_CBRACE): New error code.
* lib/wordsplit.c (wordsplit_init): Dont bail out on absense
of WRDSF_NOVAR.
(wsnode_insert): Bugfix.
(wordsplit_finish): Unquote is 1 by default.
(node_split_prefix, find_closing_cbrace)
(wordsplit_find_env, expvar, node_expand_vars)
(wordsplit_varexp): New functions.
(wordsplit_len): Implement variable expansion.
(_wordsplit_errstr, wordsplit_perror): Handle new error code.
-rw-r--r-- | include/smap/wordsplit.h | 1 | ||||
-rw-r--r-- | lib/wordsplit.c | 267 |
2 files changed, 263 insertions, 5 deletions
diff --git a/include/smap/wordsplit.h b/include/smap/wordsplit.h index 9889a57..a836d83 100644 --- a/include/smap/wordsplit.h +++ b/include/smap/wordsplit.h @@ -97,6 +97,7 @@ struct wordsplit #define WRDSE_NOSPACE 2 #define WRDSE_NOSUPP 3 #define WRDSE_USAGE 4 +#define WRDSE_CBRACE 5 int wordsplit(const char *s, struct wordsplit *p, int flags); void wordsplit_free(struct wordsplit *p); diff --git a/lib/wordsplit.c b/lib/wordsplit.c index 6b25375..6672f0d 100644 --- a/lib/wordsplit.c +++ b/lib/wordsplit.c @@ -105,8 +105,7 @@ wordsplit_init(struct wordsplit *wsp, const char *input, size_t len, return wsp->ws_errno; } - if ((wsp->ws_flags & (WRDSF_NOVAR|WRDSF_NOCMD)) - != (WRDSF_NOVAR|WRDSF_NOCMD)) { + if (!(wsp->ws_flags & WRDSF_NOCMD)) { errno = EINVAL; wsp->ws_errno = WRDSE_NOSUPP; if (wsp->ws_flags & WRDSF_SHOWERR) @@ -313,6 +312,7 @@ wsnode_insert(struct wordsplit *wsp, struct wordsplit_node *node, wsp->ws_tail = node; node->next = p; node->prev = anchor; + anchor->next = node; } } @@ -442,7 +442,7 @@ wordsplit_finish(struct wordsplit *wsp) for (p = wsp->ws_head; p; p = p->next) { const char *str = wsnode_ptr(wsp, p); size_t slen = wsnode_len(p); - int unquote = 0; + int unquote = 1; char *newstr; if ((wsp->ws_flags & WRDSF_QUOTE) @@ -469,7 +469,246 @@ wordsplit_finish(struct wordsplit *wsp) wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc] = NULL; return 0; } + +/* Variable expansion */ +static int +node_split_prefix(struct wordsplit *wsp, + struct wordsplit_node **ptail, + struct wordsplit_node *node, + size_t beg, size_t len, + int flg) +{ + struct wordsplit_node *newnode; + + if (len == 0) + return 0; + if (wsnode_new(wsp, &newnode)) + return 1; + wsnode_insert(wsp, newnode, *ptail, 0); + if (node->flags & _WSNF_WORD) { + const char *str = wsnode_ptr(wsp, node); + char *newstr = malloc(len + 1); + if (!newstr) + return _wsplt_nomem(wsp); + memcpy(newstr, str + beg, len); + newstr[len] = 0; + newnode->flags = _WSNF_WORD; + newnode->v.word = newstr; + } else { + newnode->v.segm.beg = node->v.segm.beg + beg; + newnode->v.segm.end = newnode->v.segm.beg + len; + } + newnode->flags |= flg; + *ptail = newnode; + return 0; +} + +static int +find_closing_cbrace(const char *str, size_t i, size_t len, size_t *poff) +{ + enum { st_init, st_squote, st_dquote } state = st_init; + size_t level = 1; + + for ( ; i < len; i++) { + switch (state) { + case st_init: + switch (str[i]) { + case '{': + level++; + break; + + case '}': + if (--level == 0) { + *poff = i; + return 0; + } + break; + + case '"': + state = st_dquote; + break; + + case '\'': + state = st_squote; + break; + } + break; + + case st_squote: + if (str[i] == '\'') + state = st_init; + break; + + case st_dquote: + if (str[i] == '\\') + i++; + else if (str[i] == '"') + state = st_init; + break; + } + } + return 1; +} + +static const char * +wordsplit_find_env(struct wordsplit *wsp, const char *name, size_t len) +{ + size_t i; + + if (!(wsp->ws_flags & WRDSF_ENV)) + return NULL; + for (i = 0; wsp->ws_env[i]; i++) { + size_t j; + const char *var = wsp->ws_env[i]; + + for (j = 0; j < len; j++) + if (name[j] != var[j]) + break; + if (j == len && var[j] == '=') + return var + j + 1; + } + return NULL; +} + +static int +expvar(struct wordsplit *wsp, const char *str, size_t len, + struct wordsplit_node **ptail, const char **pend) +{ + size_t i = 0; + const char *defstr = NULL; + char *value; + const char *vptr; + + if (ISALPHA(str[0]) || str[0] == '_') { + for (i = 1; i < len; i++) + if (!(ISALNUM(str[i]) || str[i] == '_')) + break; + *pend = str + i - 1; + } else if (str[0] == '{') { + str++; + len--; + for (i = 1; i < len; i++) + if (str[i] == '}' || str[i] == ':') + break; + if (str[i] == ':') { + size_t j; + + defstr = str + i + 1; + if (find_closing_cbrace(str, i + 1, len, &j)) { + wsp->ws_errno = WRDSE_CBRACE; + return 1; + } + *pend = str + j; + } else if (str[i] == '}') { + defstr = NULL; + *pend = str + i; + } else { + wsp->ws_errno = WRDSE_CBRACE; + return 1; + } + } else { + struct wordsplit_node *newnode; + + if (wsnode_new(wsp, &newnode)) + return 1; + wsnode_insert(wsp, newnode, *ptail, 0); + *ptail = newnode; + newnode->flags = _WSNF_WORD; + newnode->v.word = malloc(3); + if (!newnode->v.word) + return _wsplt_nomem(wsp); + newnode->v.word[0] = '$'; + newnode->v.word[1] = str[0]; + newnode->v.word[2] = 0; + *pend = str; + return 0; + } + + /* Actually expand the variable */ + /* str - start of the variable name + i - its length + defstr - default replacement str */ + + vptr = wordsplit_find_env(wsp, str, i); + if (vptr) { + value = strdup(vptr); + if (!value) + return _wsplt_nomem(wsp); + } else if (wsp->ws_flags & WRDSF_GETVAR) + value = wsp->ws_getvar(str, i); + else + value = NULL; + /* FIXME: handle defstr */ + if (value) { + struct wordsplit_node *newnode; + + if (wsnode_new(wsp, &newnode)) + return 1; + wsnode_insert(wsp, newnode, *ptail, 0); + *ptail = newnode; + newnode->flags = _WSNF_WORD; + newnode->v.word = value; + } + return 0; +} + +static int +node_expand_vars(struct wordsplit *wsp, struct wordsplit_node *node) +{ + const char *str = wsnode_ptr(wsp, node); + size_t slen = wsnode_len(node); + const char *end = str + slen; + const char *p; + size_t off = 0; + struct wordsplit_node *tail = node; + for (p = str; p < end; p++) { + if (*p == '\\') { + p++; + continue; + } + if (*p == '$') { + size_t n = p - str; + + if (tail != node) + tail->flags |= _WSNF_JOIN; + if (node_split_prefix(wsp, &tail, node, off, n, + _WSNF_JOIN)) + return 1; + p++; + if (expvar(wsp, p, slen - n, &tail, &p)) + return 1; + off += p - str + 1; + str = p + 1; + } + } + if (p > str) { + if (tail != node) + tail->flags |= _WSNF_JOIN; + if (node_split_prefix(wsp, &tail, node, off, p - str, 0)) + return 1; + } + if (tail != node) { + wsnode_remove(wsp, node); + wsnode_free(node); + } + return 0; +} + +static int +wordsplit_varexp(struct wordsplit *wsp) +{ + struct wordsplit_node *p; + + for (p = wsp->ws_head; p; ) { + struct wordsplit_node *next = p->next; + if (!(p->flags & _WSNF_NOEXPAND)) + if (node_expand_vars(wsp, p)) + return 1; + p = next; + } + return 0; +} static int skip_sed_expr(const char *command, size_t i, size_t len) @@ -834,14 +1073,27 @@ wordsplit_len(const char *command, size_t len, struct wordsplit *wsp, while ((rc = scan_word(wsp, start)) == _WRDS_OK) start = skip_delim(wsp); - if (wsp->ws_flags & WRDSF_DEBUG) + if (wsp->ws_flags & WRDSF_DEBUG) { + wsp->ws_error("Initial list:"); wordsplit_dump_nodes(wsp); + } if (rc) { wordsplit_free_nodes(wsp); return wsp->ws_errno; } /* FIXME: Expand variables & commands here */ + if (!(wsp->ws_flags & WRDSF_NOVAR)) { + if (wordsplit_varexp(wsp)) { + wordsplit_free_nodes(wsp); + return wsp->ws_errno; + } + if (wsp->ws_flags & WRDSF_DEBUG) { + wsp->ws_error("Expanded list:"); + wordsplit_dump_nodes(wsp); + } + } + if (wsnode_coalesce(wsp) == 0) { if (wsp->ws_flags & WRDSF_DEBUG) { wsp->ws_error("Coalesced list:"); @@ -891,6 +1143,10 @@ wordsplit_perror(struct wordsplit *wsp) case WRDSE_USAGE: wsp->ws_error(_("invalid wordsplit usage")); break; + + case WRDSE_CBRACE: + wsp->ws_error(_("unbalanced curly brace")); + break; default: wsp->ws_error(_("unknown error")); @@ -903,7 +1159,8 @@ const char *_wordsplit_errstr[] = { N_("memory exhausted"), N_("variable expansion and command substitution " "are not yet supported"), - N_("invalid wordsplit usage") + N_("invalid wordsplit usage"), + N_("unbalanced curly brace") }; int _wordsplit_nerrs = sizeof(_wordsplit_errstr)/sizeof(_wordsplit_errstr[0]); |