diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2019-05-14 16:00:53 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2019-05-14 16:02:06 +0300 |
commit | 5a813adff5390060fdaa7ee68c433531aac3850c (patch) | |
tree | e1d94610dbe8ca7b9663dfab595c4658b5bcbc32 | |
parent | 89bfd929ae3c83fbc8338562fe3682cd6ca71bac (diff) | |
download | rush-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.c | 216 | ||||
-rw-r--r-- | lib/wordsplit.h | 31 | ||||
-rw-r--r-- | src/map.c | 66 | ||||
-rw-r--r-- | src/rush.c | 8 |
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) @@ -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; @@ -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)); |