diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2019-04-20 08:54:21 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2019-04-20 09:47:40 +0300 |
commit | 3e07e3ad30e8a7a091e213eb4df839b7cf7f1e64 (patch) | |
tree | a3c83a584f8e2b54e294ff1dfa04a2262459d5a4 | |
parent | 1498bd570eb001a6b2bc3f1a5074e8b384d6db30 (diff) | |
download | grecs-3e07e3ad30e8a7a091e213eb4df839b7cf7f1e64.tar.gz grecs-3e07e3ad30e8a7a091e213eb4df839b7cf7f1e64.tar.bz2 |
Improve variable handling in wordsplit.
Positional variables ($N and ${N}) are recognized. Variable names in
curly braces follow the same rules as unadorned ones.
This commit also changes memory reallocation strategy in wsplt_assign_var.
If ws_envbuf needs to be expanded, new allocation size is selected as
3/2 of the previous allocation, if that size is less than max(size_t).
-rw-r--r-- | include/wordsplit.h | 16 | ||||
-rw-r--r-- | src/wordsplit.c | 67 |
2 files changed, 60 insertions, 23 deletions
diff --git a/include/wordsplit.h b/include/wordsplit.h index 1a047f7..3a7ab25 100644 --- a/include/wordsplit.h +++ b/include/wordsplit.h @@ -1,8 +1,8 @@ /* wordsplit - a word splitter - Copyright (C) 2009-2018 Sergey Poznyakoff + Copyright (C) 2009-2019 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. @@ -63,15 +63,21 @@ struct wordsplit __attribute__ ((__format__ (__printf__, 1, 2))); /* [Input] (WRDSF_DEBUG) Function used for debug output. */ const char **ws_env; /* [Input] (WRDSF_ENV, !WRDSF_NOVAR) Array of environment variables. */ - char **ws_envbuf; - size_t ws_envidx; - size_t ws_envsiz; + /* Temporary storage for environment variables. It is initialized + upon first assignment which occurs during the parsing process + (e.g. ${x:=2}). When this happens, all variables from ws_env are + moved to ws_envbuf first, and the ws_envbuf address is assigned + to ws_env. From this moment on, all variable expansions are served + from ws_envbuf. */ + 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 */ int (*ws_getvar) (char **ret, const char *var, size_t len, void *clos); /* [Input] (WRDSF_GETVAR, !WRDSF_NOVAR) Looks up the name VAR (LEN bytes long) in the table of variables and if found returns in memory location pointed to by RET the value of that @@ -194,14 +200,14 @@ struct wordsplit /* Print error message if path expansion produces empty string */ #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 -/* Keep backslash in unrecognized escape sequences in words */ #endif +/* Keep backslash in unrecognized escape sequences in words */ #define WRDSO_BSKEEP_WORD 0x00000010 /* Handle octal escapes in words */ #define WRDSO_OESC_WORD 0x00000020 /* Handle hex escapes in words */ #define WRDSO_XESC_WORD 0x00000040 diff --git a/src/wordsplit.c b/src/wordsplit.c index bad59b1..f94015a 100644 --- a/src/wordsplit.c +++ b/src/wordsplit.c @@ -1083,18 +1083,22 @@ wsplt_assign_var (struct wordsplit *wsp, const char *name, size_t namelen, wsp->ws_env = (const char**) wsp->ws_envbuf; wsp->ws_flags |= WRDSF_ENV; } } else { - wsp->ws_envsiz *= 2; - newenv = realloc (wsp->ws_envbuf, - wsp->ws_envsiz * sizeof (wsp->ws_envbuf[0])); + size_t n = wsp->ws_envsiz; + + if ((size_t) -1 / 3 * 2 / sizeof (wsp->ws_envbuf[0]) <= n) + return _wsplt_nomem (wsp); + n += (n + 1) / 2; + newenv = realloc (wsp->ws_envbuf, n * sizeof (wsp->ws_envbuf[0])); if (!newenv) return _wsplt_nomem (wsp); wsp->ws_envbuf = newenv; + wsp->ws_envsiz = n; wsp->ws_env = (const char**) wsp->ws_envbuf; } } if (wsp->ws_flags & WRDSF_ENV_KV) { @@ -1125,12 +1129,35 @@ wsplt_assign_var (struct wordsplit *wsp, const char *name, size_t namelen, wsp->ws_env[wsp->ws_envidx++] = v; } wsp->ws_env[wsp->ws_envidx++] = NULL; 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 +expvar_recover (struct wordsplit *wsp, const char *str, + struct wordsplit_node **ptail, const char **pend, int flg) +{ + struct wordsplit_node *newnode; + + if (wsnode_new (wsp, &newnode)) + return 1; + wsnode_insert (wsp, newnode, *ptail, 0); + *ptail = newnode; + newnode->flags = _WSNF_WORD | flg; + newnode->v.word = malloc (3); + if (!newnode->v.word) + return _wsplt_nomem (wsp); + newnode->v.word[0] = '$'; + newnode->v.word[1] = str[0]; + newnode->v.word[2] = 0; + *pend = str; + return 0; +} + static int expvar (struct wordsplit *wsp, const char *str, size_t len, struct wordsplit_node **ptail, const char **pend, int flg) { size_t i = 0; const char *defstr = NULL; @@ -1145,13 +1172,18 @@ expvar (struct wordsplit *wsp, const char *str, size_t len, { for (i = 1; i < len; i++) if (!ISVARCHR (str[i])) break; *pend = str + i - 1; } - else if (str[0] == '{') + else if (ISDIGIT (str[0])) + { + i = 1; + *pend = str; + } + else if (str[0] == '{' && (ISVARBEG (str[1]) || ISDIGIT (str[1]))) { str++; len--; for (i = 1; i < len; i++) { if (str[i] == ':') @@ -1177,31 +1209,30 @@ expvar (struct wordsplit *wsp, const char *str, size_t len, defstr = str + i; if (find_closing_paren (str, i, len, &j, "{}")) return _wsplt_seterr (wsp, WRDSE_CBRACE); *pend = str + j; break; } + else if (ISDIGIT (str[1])) + { + if (!ISDIGIT (str[i])) + { + return expvar_recover (wsp, str - 1, ptail, pend, flg); + } + } + else if (!ISVARCHR (str[i])) + { + return expvar_recover (wsp, str - 1, ptail, pend, flg); + } } if (i == len) return _wsplt_seterr (wsp, WRDSE_CBRACE); } else { - if (wsnode_new (wsp, &newnode)) - return 1; - wsnode_insert (wsp, newnode, *ptail, 0); - *ptail = newnode; - newnode->flags = _WSNF_WORD | flg; - newnode->v.word = malloc (3); - if (!newnode->v.word) - return _wsplt_nomem (wsp); - newnode->v.word[0] = '$'; - newnode->v.word[1] = str[0]; - newnode->v.word[2] = 0; - *pend = str; - return 0; + return expvar_recover (wsp, str, ptail, pend, flg); } /* Actually expand the variable */ /* str - start of the variable name i - its length defstr - default replacement str */ @@ -1414,13 +1445,13 @@ expvar (struct wordsplit *wsp, const char *str, size_t len, return 0; } static int begin_var_p (int c) { - return c == '{' || ISVARBEG (c); + return c == '{' || ISVARBEG (c) || ISDIGIT (c); } static int node_expand (struct wordsplit *wsp, struct wordsplit_node *node, int (*beg_p) (int), int (*ws_exp_fn) (struct wordsplit *wsp, |