aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2014-10-23 08:48:45 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2015-12-17 15:25:24 +0200
commitbeda81512afdcf16432ebd5b76930392f427a7fc (patch)
tree7c92a795bb3b1e43b5242dd64c67e450fcf5585b
parent2d4c689ad13b663ab80a82cf139a3b779a8d28ec (diff)
downloadgrecs-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.c210
-rw-r--r--src/wordsplit.h2
-rw-r--r--tests/wsp.c64
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;

Return to:

Send suggestions and report system problems to the System administrator.