diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2014-10-23 08:48:45 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2015-12-17 15:25:24 +0200 |
commit | beda81512afdcf16432ebd5b76930392f427a7fc (patch) | |
tree | 7c92a795bb3b1e43b5242dd64c67e450fcf5585b | |
parent | 2d4c689ad13b663ab80a82cf139a3b779a8d28ec (diff) | |
download | grecs-beda81512afdcf16432ebd5b76930392f427a7fc.tar.gz grecs-beda81512afdcf16432ebd5b76930392f427a7fc.tar.bz2 |
wordsplit: implement command expansion
* src/wordsplit.c (wordsplit_init): Change handling of lacking
WRDSF_NOCMD.
(find_closing_cbrace): Rename to find_closing_paren, take additional
argument. All uses changed.
(node_expand_vars): Rewrite as a generalized function node_expand.
(wordsplit_varexp): Use node_expand.
(expcmd, wordsplit_cmdexp): New functions.
(scan_word): Handle $(command) notation
(wordsplit_process_list): Command expansion.
* src/wordsplit.h (wordsplit) <ws_command>: New member.
* tests/wsp.c (wsp_runcmd): New function.
(main): Set ws_command unless WRDSF_NOCMD flag is set.
-rw-r--r-- | src/wordsplit.c | 210 | ||||
-rw-r--r-- | src/wordsplit.h | 2 | ||||
-rw-r--r-- | tests/wsp.c | 64 |
3 files changed, 246 insertions, 30 deletions
diff --git a/src/wordsplit.c b/src/wordsplit.c index 853adff..f17ed98 100644 --- a/src/wordsplit.c +++ b/src/wordsplit.c @@ -131,11 +131,14 @@ wordsplit_init (struct wordsplit *wsp, const char *input, size_t len, if (!(wsp->ws_flags & WRDSF_NOCMD)) { - errno = EINVAL; - wsp->ws_errno = WRDSE_NOSUPP; - if (wsp->ws_flags & WRDSF_SHOWERR) - wordsplit_perror (wsp); - return wsp->ws_errno; + if (!wsp->ws_command) + { + errno = EINVAL; + wsp->ws_errno = WRDSE_NOSUPP; + if (wsp->ws_flags & WRDSF_SHOWERR) + wordsplit_perror (wsp); + return wsp->ws_errno; + } } if (wsp->ws_flags & WRDSF_SHOWDBG) @@ -610,7 +613,8 @@ node_split_prefix (struct wordsplit *wsp, } static int -find_closing_cbrace (const char *str, size_t i, size_t len, size_t * poff) +find_closing_paren (const char *str, size_t i, size_t len, size_t *poff, + char *paren) { enum { st_init, st_squote, st_dquote } state = st_init; size_t level = 1; @@ -622,18 +626,23 @@ find_closing_cbrace (const char *str, size_t i, size_t len, size_t * poff) case st_init: switch (str[i]) { - case '{': - level++; - break; - - case '}': - if (--level == 0) + default: + if (str[i] == paren[0]) + { + level++; + break; + } + else if (str[i] == paren[1]) { - *poff = i; - return 0; + if (--level == 0) + { + *poff = i; + return 0; + } + break; } break; - + case '"': state = st_dquote; break; @@ -729,8 +738,8 @@ expvar (struct wordsplit *wsp, const char *str, size_t len, { size_t j; - /* FIXME: default value ignored: str + i + 1; */ - if (find_closing_cbrace (str, i + 1, len, &j)) + defstr = str + i + 1; + if (find_closing_paren (str, i + 1, len, &j, "{}")) { wsp->ws_errno = WRDSE_CBRACE; return 1; @@ -918,7 +927,19 @@ expvar (struct wordsplit *wsp, const char *str, size_t len, } static int -node_expand_vars (struct wordsplit *wsp, struct wordsplit_node *node) +begin_var_p (int c) +{ + return c == '{' || ISVARBEG (c); +} + +static int +node_expand (struct wordsplit *wsp, struct wordsplit_node *node, + int (*beg_p) (int), + int (*ws_exp_fn) (struct wordsplit *wsp, + const char *str, size_t len, + struct wordsplit_node **ptail, + const char **pend, + int flg)) { const char *str = wsnode_ptr (wsp, node); size_t slen = wsnode_len (node); @@ -934,7 +955,7 @@ node_expand_vars (struct wordsplit *wsp, struct wordsplit_node *node) p++; continue; } - if (*p == '$') + if (*p == '$' && beg_p (p[1])) { size_t n = p - str; @@ -943,8 +964,8 @@ node_expand_vars (struct wordsplit *wsp, struct wordsplit_node *node) if (node_split_prefix (wsp, &tail, node, off, n, _WSNF_JOIN)) return 1; p++; - if (expvar (wsp, p, slen - n, &tail, &p, - node->flags & (_WSNF_JOIN | _WSNF_QUOTE))) + if (ws_exp_fn (wsp, p, slen - n, &tail, &p, + node->flags & (_WSNF_JOIN | _WSNF_QUOTE))) return 1; off += p - str + 1; str = p + 1; @@ -955,7 +976,7 @@ node_expand_vars (struct wordsplit *wsp, struct wordsplit_node *node) if (tail != node) tail->flags |= _WSNF_JOIN; if (node_split_prefix (wsp, &tail, node, off, p - str, - node->flags & _WSNF_JOIN)) + node->flags & (_WSNF_JOIN|_WSNF_QUOTE))) return 1; } if (tail != node) @@ -965,7 +986,7 @@ node_expand_vars (struct wordsplit *wsp, struct wordsplit_node *node) } return 0; } - + /* Remove NULL lists */ static void wsnode_nullelim (struct wordsplit *wsp) @@ -993,7 +1014,114 @@ wordsplit_varexp (struct wordsplit *wsp) { struct wordsplit_node *next = p->next; if (!(p->flags & _WSNF_NOEXPAND)) - if (node_expand_vars (wsp, p)) + if (node_expand (wsp, p, begin_var_p, expvar)) + return 1; + p = next; + } + + wsnode_nullelim (wsp); + return 0; +} + +static int +begin_cmd_p (int c) +{ + return c == '('; +} + +static int +expcmd (struct wordsplit *wsp, const char *str, size_t len, + struct wordsplit_node **ptail, const char **pend, int flg) +{ + size_t j; + char *value; + struct wordsplit_node *newnode; + + str++; + len--; + + if (find_closing_paren (str, 0, len, &j, "()")) + { + wsp->ws_errno = WRDSE_CBRACE; + return 1; + } + + *pend = str + j; + value = wsp->ws_command (str, j, wsp); + + if (value) + { + if (flg & _WSNF_QUOTE) + { + if (wsnode_new (wsp, &newnode)) + return 1; + wsnode_insert (wsp, newnode, *ptail, 0); + *ptail = newnode; + newnode->flags = _WSNF_WORD | _WSNF_NOEXPAND | flg; + newnode->v.word = value; + } + else if (*value == 0) + { + free (value); + /* Empty string is a special case */ + if (wsnode_new (wsp, &newnode)) + return 1; + wsnode_insert (wsp, newnode, *ptail, 0); + *ptail = newnode; + newnode->flags = _WSNF_NULL; + } + else + { + struct wordsplit ws; + int i, rc; + + ws.ws_delim = wsp->ws_delim; + rc = wordsplit (value, &ws, + WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_DELIM | + WRDSF_WS | WRDSF_QUOTE); + free (value); + if (rc) + { + wordsplit_free (&ws); + return 1; + } + for (i = 0; i < ws.ws_wordc; i++) + { + if (wsnode_new (wsp, &newnode)) + return 1; + wsnode_insert (wsp, newnode, *ptail, 0); + *ptail = newnode; + newnode->flags = _WSNF_WORD | + _WSNF_NOEXPAND | + (i + 1 < ws.ws_wordc ? (flg & ~_WSNF_JOIN) : flg); + newnode->v.word = strdup (ws.ws_wordv[i]); + if (!newnode->v.word) + return _wsplt_nomem (wsp); + } + wordsplit_free (&ws); + } + } + else + { + if (wsnode_new (wsp, &newnode)) + return 1; + wsnode_insert (wsp, newnode, *ptail, 0); + *ptail = newnode; + newnode->flags = _WSNF_NULL; + } + return 0; +} + +static int +wordsplit_cmdexp (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 (wsp, p, begin_cmd_p, expcmd)) return 1; p = next; } @@ -1194,11 +1322,19 @@ scan_word (struct wordsplit *wsp, size_t start) } } - if (!(wsp->ws_flags & WRDSF_NOVAR) - && command[i] == '$' && command[i+1] == '{' - && find_closing_cbrace (command, i + 2, len, &i) == 0) - continue; - else if (ISDELIM (wsp, command[i])) + if (command[i] == '$') + { + if (!(wsp->ws_flags & WRDSF_NOVAR) + && command[i+1] == '{' + && find_closing_paren (command, i + 2, len, &i, "{}") == 0) + continue; + if (!(wsp->ws_flags & WRDSF_NOCMD) + && command[i+1] == '(' + && find_closing_paren (command, i + 2, len, &i, "()") == 0) + continue; + } + + if (ISDELIM (wsp, command[i])) break; else i++; @@ -1481,7 +1617,21 @@ wordsplit_process_list (struct wordsplit *wsp, size_t start) } if (wsp->ws_flags & WRDSF_SHOWDBG) { - wsp->ws_debug ("Expanded list:"); + wsp->ws_debug ("After variable expansion:"); + wordsplit_dump_nodes (wsp); + } + } + + if (!(wsp->ws_flags & WRDSF_NOCMD)) + { + if (wordsplit_cmdexp (wsp)) + { + wordsplit_free_nodes (wsp); + return wsp->ws_errno; + } + if (wsp->ws_flags & WRDSF_SHOWDBG) + { + wsp->ws_debug ("After command expansion:"); wordsplit_dump_nodes (wsp); } } diff --git a/src/wordsplit.h b/src/wordsplit.h index 682c57e..96088f9 100644 --- a/src/wordsplit.h +++ b/src/wordsplit.h @@ -39,6 +39,8 @@ struct wordsplit char *(*ws_getvar) (const char *, size_t, void *); void *ws_closure; + char *(*ws_command) (const char *, size_t, struct wordsplit const *); + const char *ws_input; size_t ws_len; size_t ws_endp; diff --git a/tests/wsp.c b/tests/wsp.c index de90c54..6fb61da 100644 --- a/tests/wsp.c +++ b/tests/wsp.c @@ -21,6 +21,7 @@ #include <stdio.h> #include <string.h> #include <assert.h> +#include <errno.h> #include "wordsplit.h" extern char **environ; @@ -181,6 +182,66 @@ wsp_getvar (const char *vptr, size_t vlen, void *data) return NULL; } +static char * +wsp_runcmd (const char *str, size_t len, struct wordsplit const *parent) +{ + FILE *fp; + char *cmd; + int c, lastc; + char *buffer = NULL; + size_t bufsize = 0; + size_t buflen = 0; + + cmd = malloc (len + 1); + if (!cmd) + { + parent->ws_error ("memory exhausted"); + abort (); + } + memcpy (cmd, str, len); + cmd[len] = 0; + + fp = popen(cmd, "r"); + if (!fp) + { + parent->ws_error ("can't run %s: %s", cmd, strerror (errno)); + return NULL; + } + + while ((c = fgetc (fp)) != EOF) + { + lastc = c; + if (c == '\n') + c = ' '; + if (buflen == bufsize) + { + if (bufsize == 0) + bufsize = 80; + else + bufsize *= 2; + buffer = realloc (buffer, bufsize); + if (!buffer) + { + parent->ws_error ("can't run %s: %s", cmd, strerror (errno)); + return NULL; + } + } + buffer[buflen++] = c; + } + + if (buffer) + { + if (lastc == '\n') + --buflen; + buffer[buflen] = 0; + } + + close (fp); + free (cmd); + + return buffer; +} + int main (int argc, char **argv) { @@ -343,6 +404,9 @@ main (int argc, char **argv) else ws.ws_env = (const char **) environ; + if (!(wsflags & WRDSF_NOCMD)) + ws.ws_command = wsp_runcmd; + if (wsflags & WRDSF_INCREMENTAL) trimnl_option = 1; |