aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2019-05-14 16:00:53 +0300
committerSergey Poznyakoff <gray@gnu.org>2019-05-14 16:02:06 +0300
commit5a813adff5390060fdaa7ee68c433531aac3850c (patch)
treee1d94610dbe8ca7b9663dfab595c4658b5bcbc32
parent89bfd929ae3c83fbc8338562fe3682cd6ca71bac (diff)
downloadrush-5a813adff5390060fdaa7ee68c433531aac3850c.tar.gz
rush-5a813adff5390060fdaa7ee68c433531aac3850c.tar.bz2
Import wordsplit from grecs 8652a500
This fixes handling of variable and command references in the request command line.
-rw-r--r--lib/wordsplit.c216
-rw-r--r--lib/wordsplit.h31
-rw-r--r--src/map.c66
-rw-r--r--src/rush.c8
4 files changed, 249 insertions, 72 deletions
diff --git a/lib/wordsplit.c b/lib/wordsplit.c
index 9ac9d44..6a33636 100644
--- a/lib/wordsplit.c
+++ b/lib/wordsplit.c
@@ -27,6 +27,7 @@
#include <stdarg.h>
#include <pwd.h>
#include <glob.h>
+#include <limits.h>
#if ENABLE_NLS
# include <gettext.h>
@@ -53,11 +54,12 @@
#define ISVARBEG(c) (ISALPHA(c) || c == '_')
#define ISVARCHR(c) (ISALNUM(c) || c == '_')
-#define ISPOSBEG(s) (ISDIGIT((s)[0]) || ((s)[0] == '-' && ISDIGIT((s)[1])))
-
#define WSP_RETURN_DELIMS(wsp) \
((wsp)->ws_flags & WRDSF_RETURN_DELIMS || ((wsp)->ws_options & WRDSO_MAXWORDS))
+#define to_num(c) \
+ (ISDIGIT(c) ? c - '0' : (ISXDIGIT(c) ? toupper(c) - 'A' + 10 : 255 ))
+
#define ALLOC_INIT 128
#define ALLOC_INCR 128
@@ -286,6 +288,14 @@ wordsplit_init (struct wordsplit *wsp, const char *input, size_t len,
wsp->ws_options |= WRDSO_BSKEEP_QUOTE;
}
}
+
+ if (!(wsp->ws_options & WRDSO_PARAMV))
+ {
+ wsp->ws_paramv = NULL;
+ wsp->ws_paramc = 0;
+ }
+ wsp->ws_paramidx = wsp->ws_paramsiz = 0;
+ wsp->ws_parambuf = NULL;
wsp->ws_endp = 0;
wsp->ws_wordi = 0;
@@ -830,9 +840,12 @@ wordsplit_finish (struct wordsplit *wsp)
goto again;
}
- if (wordsplit_add_segm (wsp, 0, 0, _WSNF_EMPTYOK))
- return wsp->ws_errno;
- n = 1;
+ if (wsp->ws_flags & WRDSF_NOSPLIT)
+ {
+ if (wordsplit_add_segm (wsp, 0, 0, _WSNF_EMPTYOK))
+ return wsp->ws_errno;
+ n = 1;
+ }
}
if (alloc_space (wsp, n + 1))
@@ -1035,7 +1048,7 @@ wordsplit_find_env (struct wordsplit *wsp, const char *name, size_t len,
static int
wsplt_assign_var (struct wordsplit *wsp, const char *name, size_t namelen,
- char *value)
+ char const *value)
{
int n = (wsp->ws_flags & WRDSF_ENV_KV) ? 2 : 1;
char *v;
@@ -1070,7 +1083,7 @@ wsplt_assign_var (struct wordsplit *wsp, const char *name, size_t namelen,
{
for (; j > 1; j--)
free (newenv[j-1]);
- free (newenv[j-1]);
+ free (newenv);
return _wsplt_nomem (wsp);
}
}
@@ -1141,6 +1154,70 @@ wsplt_assign_var (struct wordsplit *wsp, const char *name, size_t namelen,
return WRDSE_OK;
}
+int
+wsplt_assign_param (struct wordsplit *wsp, int param_idx, char *value)
+{
+ char *v;
+
+ if (param_idx < 0)
+ return WRDSE_BADPARAM;
+ if (param_idx == wsp->ws_paramc)
+ {
+ char **parambuf;
+ if (!wsp->ws_parambuf)
+ {
+ size_t i;
+
+ parambuf = calloc ((size_t)param_idx + 1, sizeof (parambuf[0]));
+ if (!parambuf)
+ return _wsplt_nomem (wsp);
+
+ for (i = 0; i < wsp->ws_paramc; i++)
+ {
+ parambuf[i] = strdup (wsp->ws_paramv[i]);
+ if (!parambuf[i])
+ {
+ for (; i > 1; i--)
+ free (parambuf[i-1]);
+ free (parambuf);
+ return _wsplt_nomem (wsp);
+ }
+ }
+
+ wsp->ws_parambuf = parambuf;
+ wsp->ws_paramidx = param_idx;
+ wsp->ws_paramsiz = param_idx + 1;
+ }
+ else
+ {
+ size_t n = wsp->ws_paramsiz;
+
+ if ((size_t) -1 / 3 * 2 / sizeof (wsp->ws_parambuf[0]) <= n)
+ return _wsplt_nomem (wsp);
+ n += (n + 1) / 2;
+ parambuf = realloc (wsp->ws_parambuf, n * sizeof (wsp->ws_parambuf[0]));
+ if (!parambuf)
+ return _wsplt_nomem (wsp);
+ wsp->ws_parambuf = parambuf;
+ wsp->ws_paramsiz = n;
+ wsp->ws_parambuf[param_idx] = NULL;
+ }
+
+ wsp->ws_paramv = (const char**) wsp->ws_parambuf;
+ wsp->ws_paramc = param_idx + 1;
+ }
+ else if (param_idx > wsp->ws_paramc)
+ return WRDSE_BADPARAM;
+
+ v = strdup (value);
+ if (!v)
+ return _wsplt_nomem (wsp);
+
+ free (wsp->ws_parambuf[param_idx]);
+ wsp->ws_parambuf[param_idx] = v;
+ return WRDSE_OK;
+}
+
/* Recover from what looked like a variable reference, but turned out
not to be one. STR points to first character after '$'. */
static int
@@ -1176,6 +1253,8 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
const char *start = str - 1;
int rc;
struct wordsplit ws;
+ int is_param = 0;
+ long param_idx = 0;
if (ISVARBEG (str[0]))
{
@@ -1184,16 +1263,39 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
break;
*pend = str + i - 1;
}
- else if (ISPOSBEG (&str[0]))
+ else if ((wsp->ws_options & WRDSO_PARAMV) && ISDIGIT (str[0]))
{
i = 1;
*pend = str;
+ is_param = 1;
+ param_idx = to_num (str[0]);
}
- else if (str[0] == '{' && (ISVARBEG (str[1]) || ISPOSBEG (&str[1])))
+ else if ((wsp->ws_options & WRDSO_PARAMV) && str[0] == '#')
+ {
+ char b[16];
+ snprintf (b, sizeof(b), "%d", (int) wsp->ws_paramc);
+ value = strdup (b);
+ if (!value)
+ return _wsplt_nomem (wsp);
+ 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;
+ return 0;
+ }
+ else if (str[0] == '{'
+ && (ISVARBEG (str[1])
+ || (is_param = (((wsp->ws_options & WRDSO_PARAMV)
+ && ISDIGIT (str[1]))
+ || ((wsp->ws_options & WRDSO_PARAM_NEGIDX)
+ && (str[1] == '-'
+ && ISDIGIT (str[2]))))) != 0))
{
str++;
len--;
- for (i = 1; i < len; i++)
+ for (i = str[0] == '-' ? 1 : 0; i < len; i++)
{
if (str[i] == ':')
{
@@ -1221,9 +1323,16 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
*pend = str + j;
break;
}
- else if (ISPOSBEG (&str[1]))
+ else if (is_param)
{
- if (!ISDIGIT (str[i]))
+ if (ISDIGIT (str[i]))
+ {
+ param_idx = param_idx * 10 + to_num (str[i]);
+ if ((str[0] == '-' && -param_idx < INT_MIN)
+ || param_idx > INT_MAX)
+ return expvar_recover (wsp, str - 1, ptail, pend, flg);
+ }
+ else
{
return expvar_recover (wsp, str - 1, ptail, pend, flg);
}
@@ -1233,6 +1342,10 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
return expvar_recover (wsp, str - 1, ptail, pend, flg);
}
}
+
+ if (is_param && str[0] == '-')
+ param_idx = wsp->ws_paramc - param_idx;
+
if (i == len)
return _wsplt_seterr (wsp, WRDSE_CBRACE);
}
@@ -1253,23 +1366,39 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
}
else
{
- rc = wordsplit_find_env (wsp, str, i, &vptr);
- if (rc == WRDSE_OK)
+ if (is_param)
{
- if (vptr)
+ if (param_idx >= 0 && param_idx < wsp->ws_paramc)
{
- value = strdup (vptr);
+ value = strdup (wsp->ws_paramv[param_idx]);
if (!value)
rc = WRDSE_NOSPACE;
+ else
+ rc = WRDSE_OK;
}
else
rc = WRDSE_UNDEF;
}
- else if (wsp->ws_flags & WRDSF_GETVAR)
- rc = wsp->ws_getvar (&value, str, i, wsp->ws_closure);
else
- rc = WRDSE_UNDEF;
-
+ {
+ rc = wordsplit_find_env (wsp, str, i, &vptr);
+ if (rc == WRDSE_OK)
+ {
+ if (vptr)
+ {
+ value = strdup (vptr);
+ if (!value)
+ rc = WRDSE_NOSPACE;
+ }
+ else
+ rc = WRDSE_UNDEF;
+ }
+ else if (wsp->ws_flags & WRDSF_GETVAR)
+ rc = wsp->ws_getvar (&value, str, i, wsp->ws_closure);
+ else
+ rc = WRDSE_UNDEF;
+ }
+
if (rc == WRDSE_OK
&& (!value || value[0] == 0)
&& defstr && defstr[-1] == ':')
@@ -1320,7 +1449,17 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
wordsplit_free (&ws);
if (defstr[-1] == '=')
- wsplt_assign_var (wsp, str, i, value);
+ {
+ if (is_param)
+ rc = wsplt_assign_param (wsp, param_idx, value);
+ else
+ rc = wsplt_assign_var (wsp, str, i, value);
+ }
+ if (rc)
+ {
+ free (value);
+ return rc;
+ }
}
else
{
@@ -1457,7 +1596,7 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
static int
begin_var_p (int c)
{
- return c == '{' || ISVARBEG (c) || ISDIGIT (c);
+ return c == '{' || c == '#' || ISVARBEG (c) || ISDIGIT (c);
}
static int
@@ -2057,11 +2196,13 @@ scan_word (struct wordsplit *wsp, size_t start, int consume_all)
if (command[i] == '$')
{
- if (!(wsp->ws_flags & WRDSF_NOVAR)
+ if ((!(wsp->ws_flags & WRDSF_NOVAR)
+ || (wsp->ws_options & WRDSO_NOVARSPLIT))
&& command[i+1] == '{'
&& find_closing_paren (command, i + 2, len, &i, "{}") == 0)
continue;
- if (!(wsp->ws_flags & WRDSF_NOCMD)
+ if ((!(wsp->ws_flags & WRDSF_NOCMD)
+ || (wsp->ws_options & WRDSO_NOCMDSPLIT))
&& command[i+1] == '('
&& find_closing_paren (command, i + 2, len, &i, "()") == 0)
continue;
@@ -2103,9 +2244,6 @@ scan_word (struct wordsplit *wsp, size_t start, int consume_all)
return _WRDS_OK;
}
-#define to_num(c) \
- (ISDIGIT(c) ? c - '0' : (ISXDIGIT(c) ? toupper(c) - 'A' + 10 : 255 ))
-
static int
xtonum (int *pval, const char *src, int base, int cnt)
{
@@ -2495,7 +2633,7 @@ wordsplit_free_words (struct wordsplit *ws)
void
wordsplit_free_envbuf (struct wordsplit *ws)
{
- if (ws->ws_flags & WRDSF_NOCMD)
+ if (!(ws->ws_flags & WRDSF_ENV))
return;
if (ws->ws_envbuf)
{
@@ -2510,6 +2648,23 @@ wordsplit_free_envbuf (struct wordsplit *ws)
}
void
+wordsplit_free_parambuf (struct wordsplit *ws)
+{
+ if (!(ws->ws_options & WRDSO_PARAMV))
+ return;
+ if (ws->ws_parambuf)
+ {
+ size_t i;
+
+ for (i = 0; ws->ws_parambuf[i]; i++)
+ free (ws->ws_parambuf[i]);
+ free (ws->ws_parambuf);
+ ws->ws_paramidx = ws->ws_paramsiz = 0;
+ ws->ws_parambuf = NULL;
+ }
+}
+
+void
wordsplit_clearerr (struct wordsplit *ws)
{
if (ws->ws_errno == WRDSE_USERERR)
@@ -2526,6 +2681,7 @@ wordsplit_free (struct wordsplit *ws)
free (ws->ws_wordv);
ws->ws_wordv = NULL;
wordsplit_free_envbuf (ws);
+ wordsplit_free_parambuf (ws);
}
int
@@ -2554,7 +2710,9 @@ const char *_wordsplit_errstr[] = {
N_("undefined variable"),
N_("input exhausted"),
N_("unbalanced parenthesis"),
- N_("globbing error")
+ N_("globbing error"),
+ N_("user-defined error"),
+ N_("invalid parameter number in assignment")
};
int _wordsplit_nerrs =
sizeof (_wordsplit_errstr) / sizeof (_wordsplit_errstr[0]);
diff --git a/lib/wordsplit.h b/lib/wordsplit.h
index 3a7ab25..d7eb26f 100644
--- a/lib/wordsplit.h
+++ b/lib/wordsplit.h
@@ -75,6 +75,16 @@ struct wordsplit
char **ws_envbuf; /* Storage for variables */
size_t ws_envidx; /* Index of first free slot */
size_t ws_envsiz; /* Size of the ws_envbuf array */
+
+ char const **ws_paramv; /* [WRDSO_PARAMV] User-supplied positional
+ parameters */
+ size_t ws_paramc; /* Number of positional parameters */
+
+ /* Temporary storage for parameters. Works similarly to ws_enbuf.
+ */
+ char **ws_parambuf;
+ size_t ws_paramidx;
+ size_t ws_paramsiz;
int (*ws_getvar) (char **ret, const char *var, size_t len, void *clos);
/* [Input] (WRDSF_GETVAR, !WRDSF_NOVAR) Looks up
@@ -99,7 +109,7 @@ struct wordsplit
See ws_getvar for a discussion of possible
return values. */
-
+
const char *ws_input; /* Input string (the S argument to wordsplit. */
size_t ws_len; /* Length of ws_input. */
size_t ws_endp; /* Points past the last processed byte in
@@ -201,9 +211,7 @@ struct wordsplit
#define WRDSO_FAILGLOB 0x00000002
/* Allow a leading period to be matched by metacharacters. */
#define WRDSO_DOTGLOB 0x00000004
-#if 0 /* Unused value */
-#define WRDSO_ARGV 0x00000008
-#endif
+/* Unused value: 0x00000008 */
/* Keep backslash in unrecognized escape sequences in words */
#define WRDSO_BSKEEP_WORD 0x00000010
/* Handle octal escapes in words */
@@ -220,6 +228,19 @@ struct wordsplit
#define WRDSO_OESC_QUOTE 0x00000200
/* Handle hex escapes in quoted strings */
#define WRDSO_XESC_QUOTE 0x00000400
+/* Unused: 0x00000800 */
+/* Don't split variable references, even if they contain whitespace
+ (e.g. ${VAR:-foo bar}) */
+#define WRDSO_NOVARSPLIT 0x00001000
+/* Don't split commands, even containing whitespace, e.g.
+ $(echo foo bar) */
+#define WRDSO_NOCMDSPLIT 0x00002000
+
+/* Enable positional parameters */
+#define WRDSO_PARAMV 0x00004000
+/* Enable negative positional indices (${-1} is the last positional
+ parameter) */
+#define WRDSO_PARAM_NEGIDX 0x00008000
#define WRDSO_BSKEEP WRDSO_BSKEEP_WORD
#define WRDSO_OESC WRDSO_OESC_WORD
@@ -245,12 +266,14 @@ struct wordsplit
#define WRDSE_PAREN 7
#define WRDSE_GLOBERR 8
#define WRDSE_USERERR 9
+#define WRDSE_BADPARAM 10
int wordsplit (const char *s, wordsplit_t *ws, int flags);
int wordsplit_len (const char *s, size_t len, wordsplit_t *ws, int flags);
void wordsplit_free (wordsplit_t *ws);
void wordsplit_free_words (wordsplit_t *ws);
void wordsplit_free_envbuf (wordsplit_t *ws);
+void wordsplit_free_parambuf (struct wordsplit *ws);
int wordsplit_get_words (wordsplit_t *ws, size_t *wordc, char ***wordv);
static inline void wordsplit_getwords (wordsplit_t *ws, size_t *wordc, char ***wordv)
diff --git a/src/map.c b/src/map.c
index 2826232..41cbd30 100644
--- a/src/map.c
+++ b/src/map.c
@@ -223,42 +223,25 @@ getvar(char **ret, const char *var, size_t len, void *clos)
struct rush_request *req = clos;
const char *s = NULL;
char *p;
+ struct vardef *vd;
- if (*var == '-') {
- unsigned long n;
- errno = 0;
- n = strtoul(var + 1, NULL, 10);
- if (errno || n == 0 || n > req->argc)
- return WRDSE_UNDEF;
- n = req->argc - n;
- s = req->argv[n];
- } else if (c_isdigit(*var)) {
- unsigned long n;
- errno = 0;
- n = strtoul(var, NULL, 10);
- if (errno || n >= req->argc)
- return WRDSE_UNDEF;
- s = req->argv[n];
- } else {
- struct vardef *vd;
- for (vd = request_vars; vd->name; vd++) {
- if (strncmp(vd->name, var, len) == 0) {
- s = vd->expand(clos);
- break;
- }
+ for (vd = request_vars; vd->name; vd++) {
+ if (strncmp(vd->name, var, len) == 0) {
+ s = vd->expand(clos);
+ break;
}
+ }
- if (!s && req->env_count) {
- /* Look up in the environment */
- int i;
-
- for (i = 0; req->env[i]; i++) {
- if (strlen(req->env[i]) > len
- && req->env[i][len] == '='
- && memcmp(req->env[i], var, len) == 0) {
- s = req->env[i] + len + 1;
- break;
- }
+ if (!s && req->env_count) {
+ /* Look up in the environment */
+ int i;
+
+ for (i = 0; req->env[i]; i++) {
+ if (strlen(req->env[i]) > len
+ && req->env[i][len] == '='
+ && memcmp(req->env[i], var, len) == 0) {
+ s = req->env[i] + len + 1;
+ break;
}
}
}
@@ -283,18 +266,29 @@ rush_expand_string(const char *string, struct rush_request *req)
| WRDSF_CLOSURE
| WRDSF_OPTIONS;
char *result;
-
+
ws.ws_getvar = getvar;
ws.ws_closure = req;
- ws.ws_options = WRDSO_BSKEEP_QUOTE;
+ ws.ws_paramv = (char const**) req->argv;
+ ws.ws_paramc = req->argc;
+ ws.ws_options = WRDSO_BSKEEP_QUOTE | WRDSO_NOCMDSPLIT
+ | WRDSO_PARAMV | WRDSO_PARAM_NEGIDX;
if (req->var_kv) {
ws.ws_env = (const char **)req->var_kv;
wsflags |= WRDSF_ENV|WRDSF_ENV_KV;
}
result = expandref(string, &req->backref[req->backref_cur], "%");
- if (wordsplit(result, &ws, wsflags))
+ switch (wordsplit(result, &ws, wsflags)) {
+ case 0:
+ break;
+ case WRDSE_UNDEF:
+ die(config_error, &req->i18n, "%s", wordsplit_strerror(&ws));
+ break;
+ default:
die(system_error, &req->i18n, "%s", wordsplit_strerror(&ws));
+ }
+
free(result);
result = ws.ws_wordv[0];
ws.ws_wordv[0] = NULL;
diff --git a/src/rush.c b/src/rush.c
index 91ece37..0c4acd2 100644
--- a/src/rush.c
+++ b/src/rush.c
@@ -571,7 +571,8 @@ reparse_cmdline(struct rush_request *req)
struct wordsplit ws;
argcv_free(req->argc, req->argv);
- if (wordsplit(req->cmdline, &ws, WRDSF_DEFFLAGS))
+ ws.ws_options = WRDSO_NOVARSPLIT | WRDSO_NOCMDSPLIT;
+ if (wordsplit(req->cmdline, &ws, WRDSF_DEFFLAGS|WRDSF_OPTIONS))
die(system_error, &req->i18n, _("wordsplit(%s) failed: %s"),
req->cmdline, wordsplit_strerror(&ws));
wordsplit_get_words(&ws, &req->argc, &req->argv);
@@ -1109,8 +1110,9 @@ main(int argc, char **argv)
}
req.cmdline = xstrdup(command);
-
- if (wordsplit(req.cmdline, &ws, WRDSF_DEFFLAGS))
+
+ ws.ws_options = WRDSO_NOVARSPLIT | WRDSO_NOCMDSPLIT;
+ if (wordsplit(req.cmdline, &ws, WRDSF_DEFFLAGS|WRDSF_OPTIONS))
die(system_error, NULL,
_("wordsplit(%s) failed: %s"),
req.cmdline, wordsplit_strerror(&ws));

Return to:

Send suggestions and report system problems to the System administrator.