mirror of
git://git.gnu.org.ua/wordsplit.git
synced 2025-04-26 00:29:54 +03:00
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:
parent
83a979b74a
commit
2795912aa5
3 changed files with 246 additions and 30 deletions
210
src/wordsplit.c
210
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])
|
||||
{
|
||||
*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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
64
tests/wsp.c
64
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;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue