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.
This commit is contained in:
Sergey Poznyakoff 2014-10-23 08:48:45 +03:00 committed by Sergey Poznyakoff
parent 83a979b74a
commit 2795912aa5
3 changed files with 246 additions and 30 deletions

View file

@ -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])
{
*poff = i;
return 0;
level++;
break;
}
else if (str[i] == paren[1])
{
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);
}
}

View file

@ -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;

View file

@ -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;