aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2019-05-14 15:31:59 +0300
committerSergey Poznyakoff <gray@gnu.org>2019-05-14 15:43:25 +0300
commit8652a500669059d77ce7bace6e9e9da1b30c54b0 (patch)
tree54ee082e3804c0574e67252be8fbceb48b8f156a
parent65f41a742e025487f8ec7f2e7ca2a3af3283fc96 (diff)
downloadgrecs-8652a500669059d77ce7bace6e9e9da1b30c54b0.tar.gz
grecs-8652a500669059d77ce7bace6e9e9da1b30c54b0.tar.bz2
wordsplit: rewrite positional parameters implementation
This improves 3e07e3ad * include/wordsplit.h (ws_paramv,ws_paramc) (ws_parambuf,ws_paramidx,ws_paramsiz): New fields. (WRDSO_PARAMV,WRDSO_PARAM_NEGIDX): New options. (WRDSE_BADPARAM): New error code. (wordsplit_free_parambuf): New proto. * src/wordsplit.c (wordsplit_init): Initialize new fields. (wsplt_assign_var): Fix double-free and memory leak. (expvar): Expand positional parameters. (begin_var_p): Add '#' (wordsplit_free_envbuf): Fix condition. (wordsplit_free_parambuf): New function. (wordsplit_free): Call wordsplit_free_parambuf. (_wordsplit_errstr): New error description. * tests/wordsplit.at: Update wsp invocations. Test positional parameters. * tests/wsp.c: Rewrite.
-rw-r--r--include/wordsplit.h18
-rw-r--r--src/wordsplit.c185
-rw-r--r--tests/wordsplit.at122
-rw-r--r--tests/wsp.c818
4 files changed, 688 insertions, 455 deletions
diff --git a/include/wordsplit.h b/include/wordsplit.h
index a175275..d7eb26f 100644
--- a/include/wordsplit.h
+++ b/include/wordsplit.h
@@ -78,2 +78,12 @@ struct wordsplit
+ 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);
@@ -228,2 +238,8 @@ struct wordsplit
+/* 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
@@ -252,2 +268,3 @@ struct wordsplit
#define WRDSE_USERERR 9
+#define WRDSE_BADPARAM 10
@@ -258,2 +275,3 @@ 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);
diff --git a/src/wordsplit.c b/src/wordsplit.c
index 521a1eb..6a33636 100644
--- a/src/wordsplit.c
+++ b/src/wordsplit.c
@@ -1,3 +1,3 @@
/* wordsplit - a word splitter
- Copyright (C) 2009-2018 Sergey Poznyakoff
+ Copyright (C) 2009-2019 Sergey Poznyakoff
@@ -29,2 +29,3 @@
#include <glob.h>
+#include <limits.h>
@@ -58,2 +59,5 @@
+#define to_num(c) \
+ (ISDIGIT(c) ? c - '0' : (ISXDIGIT(c) ? toupper(c) - 'A' + 10 : 255 ))
+
#define ALLOC_INIT 128
@@ -287,2 +291,10 @@ wordsplit_init (struct wordsplit *wsp, const char *input, size_t len,
+ 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;
@@ -1038,3 +1050,3 @@ static int
wsplt_assign_var (struct wordsplit *wsp, const char *name, size_t namelen,
- char *value)
+ char const *value)
{
@@ -1073,3 +1085,3 @@ wsplt_assign_var (struct wordsplit *wsp, const char *name, size_t namelen,
free (newenv[j-1]);
- free (newenv[j-1]);
+ free (newenv);
return _wsplt_nomem (wsp);
@@ -1144,2 +1156,66 @@ wsplt_assign_var (struct wordsplit *wsp, const char *name, size_t namelen,
+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
@@ -1179,2 +1255,4 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
struct wordsplit ws;
+ int is_param = 0;
+ long param_idx = 0;
@@ -1187,3 +1265,3 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
}
- else if (ISDIGIT (str[0]))
+ else if ((wsp->ws_options & WRDSO_PARAMV) && ISDIGIT (str[0]))
{
@@ -1191,4 +1269,27 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
*pend = str;
+ is_param = 1;
+ param_idx = to_num (str[0]);
}
- else if (str[0] == '{' && (ISVARBEG (str[1]) || ISDIGIT (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))
{
@@ -1196,3 +1297,3 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
len--;
- for (i = 1; i < len; i++)
+ for (i = str[0] == '-' ? 1 : 0; i < len; i++)
{
@@ -1224,5 +1325,12 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
}
- else if (ISDIGIT (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
{
@@ -1236,2 +1344,6 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
}
+
+ if (is_param && str[0] == '-')
+ param_idx = wsp->ws_paramc - param_idx;
+
if (i == len)
@@ -1256,2 +1368,17 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
{
+ if (is_param)
+ {
+ if (param_idx >= 0 && param_idx < wsp->ws_paramc)
+ {
+ value = strdup (wsp->ws_paramv[param_idx]);
+ if (!value)
+ rc = WRDSE_NOSPACE;
+ else
+ rc = WRDSE_OK;
+ }
+ else
+ rc = WRDSE_UNDEF;
+ }
+ else
+ {
rc = wordsplit_find_env (wsp, str, i, &vptr);
@@ -1272,2 +1399,3 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
rc = WRDSE_UNDEF;
+ }
@@ -1323,3 +1451,13 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
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;
+ }
}
@@ -1460,3 +1598,3 @@ begin_var_p (int c)
{
- return c == '{' || ISVARBEG (c) || ISDIGIT (c);
+ return c == '{' || c == '#' || ISVARBEG (c) || ISDIGIT (c);
}
@@ -2108,5 +2246,2 @@ scan_word (struct wordsplit *wsp, size_t start, int consume_all)
-#define to_num(c) \
- (ISDIGIT(c) ? c - '0' : (ISXDIGIT(c) ? toupper(c) - 'A' + 10 : 255 ))
-
static int
@@ -2500,3 +2635,3 @@ wordsplit_free_envbuf (struct wordsplit *ws)
{
- if (ws->ws_flags & WRDSF_NOCMD)
+ if (!(ws->ws_flags & WRDSF_ENV))
return;
@@ -2515,2 +2650,19 @@ 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)
@@ -2531,2 +2683,3 @@ wordsplit_free (struct wordsplit *ws)
wordsplit_free_envbuf (ws);
+ wordsplit_free_parambuf (ws);
}
@@ -2559,3 +2712,5 @@ const char *_wordsplit_errstr[] = {
N_("unbalanced parenthesis"),
- N_("globbing error")
+ N_("globbing error"),
+ N_("user-defined error"),
+ N_("invalid parameter number in assignment")
};
diff --git a/tests/wordsplit.at b/tests/wordsplit.at
index 1f2e80d..ebd168b 100644
--- a/tests/wordsplit.at
+++ b/tests/wordsplit.at
@@ -110,3 +110,3 @@ dnl Test worsplit-specific behavior
dnl ------------------------------------------------------------
-TESTWSP([append],[wsp-append],[append],
+TESTWSP([append],[wsp-append],[-append],
[jeden dwa trzy
@@ -135,3 +135,3 @@ TOTAL: 2
-TESTWSP([dooffs],[wsp-doofs ],[dooffs 3 jeden dwa trzy],
+TESTWSP([dooffs],[wsp-doofs ],[-dooffs jeden dwa trzy],
[cztery piec],
@@ -216,3 +216,3 @@ TOTAL: 3
-TESTWSP([undefined variables 2],[],[keepundef],
+TESTWSP([undefined variables 2],[],[-keepundef],
[a $FOO test a${FOO}b],
@@ -228,3 +228,3 @@ TOTAL: 4
-TESTWSP([warn about undefined variables],[],[warnundef],
+TESTWSP([warn about undefined variables],[],[-warnundef],
[$FOO],
@@ -237,3 +237,3 @@ TOTAL: 0
-TESTWSP([bail out on undefined variables],[],[undef],
+TESTWSP([bail out on undefined variables],[],[-undef],
[$FOO],
@@ -244,3 +244,3 @@ TESTWSP([bail out on undefined variables],[],[undef],
-TESTWSP([disable variable expansion],[],[novar],
+TESTWSP([disable variable expansion],[],[-novar],
[$FOO],
@@ -254,3 +254,3 @@ TOTAL: 1
TESTWSP([K/V environment],[wsp-env-kv wsp-env_kv],
-[env_kv],
+[-env_kv],
[$FOO a$BAZ],
@@ -264,3 +264,3 @@ TOTAL: 2
-TESTWSP([nosplit with variable expansion],[wsp-var-nosplit],[nosplit],
+TESTWSP([nosplit with variable expansion],[wsp-var-nosplit],[-nosplit],
[a $FOO test],
@@ -273,3 +273,3 @@ TOTAL: 1
-TESTWSP([nosplit without variable expansion],[],[nosplit novar],
+TESTWSP([nosplit without variable expansion],[],[-nosplit -novar],
[a $FOO test],
@@ -282,3 +282,3 @@ TOTAL: 1
-TESTWSP([nosplit: empty expansion],[],[nosplit trimnl],
+TESTWSP([nosplit: empty expansion],[],[-nosplit -trimnl],
[$FOO],
@@ -291,11 +291,2 @@ TOTAL: 1
-TESTWSP([default value (defined)],[],[],
-[${FOO:-bar}],
-[NF: 1
-0: qux
-TOTAL: 1
-],
-[],
-[FOO=qux])
-
TESTWSP([default value],[],[],
@@ -453,3 +444,3 @@ WSPGROUP()
-TESTWSP([ignore quotes],[wsp-ignore-quotes ],[-quote],
+TESTWSP([ignore quotes],[wsp-ignore-quotes ],[-noquote],
["a text"],
@@ -464,3 +455,3 @@ WSPGROUP(wsp-delim)
TESTWSP([custom delimiters (squeeze)],[],
-[delim : -ws trimnl],
+[-delim : -nows -trimnl],
[semicolon: separated::list: of :words],
@@ -476,3 +467,3 @@ TOTAL: 5
TESTWSP([custom delimiters (no squeeze)],[],
-[delim : -ws -squeeze_delims trimnl],
+[-delim : -nows -nosqueeze_delims -trimnl],
[semicolon: separated::list: of :words],
@@ -489,3 +480,3 @@ TOTAL: 6
TESTWSP([custom, with returned delimiters],[],
-[delim : -ws trimnl return_delims],
+[-delim : -nows -trimnl -return_delims],
[semicolon: separated::list: of :words],
@@ -505,3 +496,3 @@ TOTAL: 9
TESTWSP([custom, with returned & squeezed delimiters],[],
-[delim : -ws trimnl return_delims -squeeze_delims],
+[-delim : -nows -trimnl -return_delims -nosqueeze_delims],
[semicolon: separated::list: of :words],
@@ -523,3 +514,3 @@ WSPGROUP(wsp-sed)
-TESTWSP([sed expressions],[],[sed],
+TESTWSP([sed expressions],[],[-sed],
[arg1 s/foo/bar/g;s/bar baz/quz quux/ arg2],
@@ -534,3 +525,3 @@ WSPGROUP()
-TESTWSP([C escapes on],[wcp-c-escape],[cescapes],
+TESTWSP([C escapes on],[wcp-c-escape],[-cescapes],
[a\ttab form\ffeed and new\nline],
@@ -544,3 +535,3 @@ TOTAL: 4
-TESTWSP([C escapes off],[wcp-c-escape-off],[-cescapes],
+TESTWSP([C escapes off],[wcp-c-escape-off],[-nocescapes],
[a\ttab form\ffeed and new\nline],
@@ -554,3 +545,3 @@ TOTAL: 4
-TESTWSP([ws elimination],[wsp-ws-elim],[delim ' ()' ws return_delims],
+TESTWSP([ws elimination],[wsp-ws-elim],[-delim ' ()' -ws -return_delims],
[( list items )],
@@ -565,3 +556,3 @@ TOTAL: 4
TESTWSP([ws elimination + return delim],[wsp-ws-elim-ret],
-[-default novar nocmd delim ":," return_delims ws dquote],
+[-nodefault -novar -nocmd -delim ":," -return_delims -ws -dquote],
["foo" : "bar", "quux" : "baaz" ],
@@ -578,3 +569,3 @@ TOTAL: 7
-TESTWSP([empty quotes],[wsp-empty-quotes],[delim : ws return_delims],
+TESTWSP([empty quotes],[wsp-empty-quotes],[-delim : -ws -return_delims],
[t=""],
@@ -586,3 +577,3 @@ TOTAL: 1
TESTWSP([delimiter following empty quotes],
-[],[delim : ws return_delims],
+[],[-delim : -ws -return_delims],
[t="":r],
@@ -597,3 +588,3 @@ TESTWSP([suppress ws trimming within quotes],
[],
-[default delim , ws return_delims],
+[-default -delim , -ws -return_delims],
[nocomponent,nonewline, formatfield="In message %{text}, "],
@@ -610,3 +601,3 @@ TESTWSP([unescape],
[wsp-unescape wsp-unescape-simple],
-[-default novar nocmd quote escape ':+:\\""'],
+[-nodefault -novar -nocmd -quote -escape ':+:\\""'],
[\Seen "quote \"" "bs \\"],
@@ -621,3 +612,3 @@ TESTWSP([unescape: word/quote],
[wsp-unescape wsp-unescape-word],
-[-default novar nocmd quote escape-word '\\""' escape-quote ':+0x:\\""'],
+[-nodefault -novar -nocmd -quote -escape-word '\\""' -escape-quote ':+0x:\\""'],
[\Seen "quote \"" "bs \\" "3\x31 \101" 3\x31 \101],
@@ -633,3 +624,3 @@ TOTAL: 6
-TESTWSP([dquote],[],[-default novar nocmd dquote],
+TESTWSP([dquote],[],[-nodefault -novar -nocmd -dquote],
[a "quoted example" isn't it],
@@ -643,3 +634,3 @@ TOTAL: 4
-TESTWSP([squote],[],[-default novar nocmd squote],
+TESTWSP([squote],[],[-nodefault -novar -nocmd -squote],
[a 'quoted example' isn"t it],
@@ -655,3 +646,3 @@ WSPGROUP(wsp-incr)
-TESTWSP([incremental],[],[incremental],
+TESTWSP([incremental],[],[-incremental],
[incremental "input test" line
@@ -673,3 +664,3 @@ TOTAL: 3
-TESTWSP([incremental append],[],[incremental append],
+TESTWSP([incremental append],[],[-incremental -append],
[incremental "input test" line
@@ -695,3 +686,3 @@ TOTAL: 3
TESTWSP([incremental ws],
-[],[return_delims -squeeze_delims incremental ws],
+[],[-return_delims -nosqueeze_delims -incremental -ws],
[a list test
@@ -713,3 +704,3 @@ TOTAL: 3
-TESTWSP([incremental nosplit],[],[incremental nosplit],
+TESTWSP([incremental nosplit],[],[-incremental -nosplit],
[incremental "input test" line
@@ -723,3 +714,3 @@ TOTAL: 1
-TESTWSP([simple command substitution],[],[-nocmd],
+TESTWSP([simple command substitution],[],[-cmd],
[begin $(words a b) end],
@@ -733,3 +724,3 @@ TOTAL: 4
-TESTWSP([quoted command substitution],[],[-nocmd],
+TESTWSP([quoted command substitution],[],[-cmd],
[begin "$(words a b)" end],
@@ -742,3 +733,3 @@ TOTAL: 3
-TESTWSP([coalesced command substitution],[],[-nocmd],
+TESTWSP([coalesced command substitution],[],[-cmd],
[begin($(words a b))end],
@@ -750,3 +741,3 @@ TOTAL: 2
-TESTWSP([quoted coalesced command substitution],[],[-nocmd],
+TESTWSP([quoted coalesced command substitution],[],[-cmd],
["begin($(words a b))end"],
@@ -757,3 +748,3 @@ TOTAL: 1
-TESTWSP([variable and command substitution],[],[-nocmd -novar],
+TESTWSP([variable and command substitution],[],[-cmd -var],
[begin $X $(words $X $Y) end],
@@ -768,3 +759,3 @@ TOTAL: 5
-TESTWSP([variable expansion and command substitution in quotes],[],[-nocmd -novar],
+TESTWSP([variable expansion and command substitution in quotes],[],[-cmd -var],
["${BEGIN}($(words $X $Y))end"],
@@ -775,3 +766,3 @@ TOTAL: 1
-TESTWSP([nested commands],[],[-nocmd -novar],
+TESTWSP([nested commands],[],[-cmd -var],
[$(words output $(words in$SUFFIX text) end)],
@@ -794,3 +785,3 @@ mkdir dir
-wsp pathexpand<<'EOT'
+wsp -pathexpand<<'EOT'
begin dir/*.c end
@@ -815,3 +806,3 @@ mkdir dir
-wsp pathexpand<<'EOT'
+wsp -pathexpand<<'EOT'
begin dir/*.d end
@@ -835,3 +826,3 @@ mkdir dir
-wsp pathexpand nullglob<<'EOT'
+wsp -pathexpand -nullglob<<'EOT'
begin dir/*.d end
@@ -854,3 +845,3 @@ mkdir dir
-wsp pathexpand failglob<<'EOT'
+wsp -pathexpand -failglob<<'EOT'
begin dir/*.d end
@@ -864,3 +855,3 @@ AT_CLEANUP
-TESTWSP([append],[],[-- extra arguments follow],
+TESTWSP([append],[],[-append-args extra arguments follow],
[some words and],
@@ -877,3 +868,3 @@ TOTAL: 3
TESTWSP([append + dooffs + env],[],
-[-env dooffs 2 preface words V=2 -- extra arguments follow],
+[-env none -dooffs preface words -- V=2 -append-args extra arguments follow],
[some words and var=$V],
@@ -894,3 +885,3 @@ TOTAL: 4
TESTWSP([maxwords],[],
-[trimnl maxwords 3],
+[-trimnl -maxwords 3],
[ws_maxwords limits the number of returned words],
@@ -904,3 +895,3 @@ TOTAL: 3
TESTWSP([maxwords return_delims],[],
-[trimnl maxwords 8 return_delims delim :-],
+[-trimnl -maxwords 8 -return_delims -delim :-],
[foo:::bar-:baz-quux:ux:zu],
@@ -919,3 +910,3 @@ TOTAL: 8
TESTWSP([maxwords return_delims -squeeze_delims],[],
-[trimnl maxwords 8 return_delims -squeeze_delims delim :-],
+[-trimnl -maxwords 8 -return_delims -nosqueeze_delims -delim :-],
[foo:::bar-:baz:qux-],
@@ -934,3 +925,3 @@ TOTAL: 8
TESTWSP([maxwords incremental],[],
-[trimnl maxwords 3 incremental],
+[-trimnl -maxwords 3 -incremental],
[foo bar baz qux uz
@@ -952,3 +943,3 @@ TOTAL: 3
-TESTWSP([variable nosplit],[],[novar novarsplit],
+TESTWSP([variable nosplit],[],[-novar -novarsplit],
[begin ${VAR:- a b} end],
@@ -961,3 +952,3 @@ TOTAL: 3
-TESTWSP([command nosplit],[],[nocmd nocmdsplit],
+TESTWSP([command nosplit],[],[-nocmd -nocmdsplit],
[begin $(words a b) end],
@@ -970,2 +961,15 @@ TOTAL: 3
+TESTWSP([positional parameters],[],[one two three four five six seven eight nine ten eleven twelve],
+[$0 $5 ${10}
+$#],
+[NF: 3
+0: one
+1: six
+2: eleven
+TOTAL: 3
+NF: 1
+0: 12
+TOTAL: 1
+])
+
m4_popdef([TESTWSP])
diff --git a/tests/wsp.c b/tests/wsp.c
index bd13e63..958d01f 100644
--- a/tests/wsp.c
+++ b/tests/wsp.c
@@ -1,3 +1,3 @@
/* grecs - Gray's Extensible Configuration System
- Copyright (C) 2014-2016 Sergey Poznyakoff
+ Copyright (C) 2014-2019 Sergey Poznyakoff
@@ -31,62 +31,166 @@ char *progname;
-struct kwd
+/* Global options */
+enum
{
- const char *name;
- int tok;
+ TRIMNL_OPTION = 0x01, /* Remove trailing newline */
+ PLAINTEXT_OPTION = 0x02 /* Print intput verbatim (no escapes) */
};
-struct kwd bool_keytab[] = {
- { "append", WRDSF_APPEND },
- /*{ "reuse", WRDSF_REUSE },*/
- { "undef", WRDSF_UNDEF },
- { "novar", WRDSF_NOVAR },
- { "nocmd", WRDSF_NOCMD },
- { "ws", WRDSF_WS },
- { "quote", WRDSF_QUOTE },
- { "squote", WRDSF_SQUOTE },
- { "dquote", WRDSF_DQUOTE },
- { "squeeze_delims", WRDSF_SQUEEZE_DELIMS },
- { "return_delims", WRDSF_RETURN_DELIMS },
- { "sed", WRDSF_SED_EXPR },
- { "debug", WRDSF_SHOWDBG },
- { "nosplit", WRDSF_NOSPLIT },
- { "keepundef", WRDSF_KEEPUNDEF },
- { "warnundef", WRDSF_WARNUNDEF },
- { "cescapes", WRDSF_CESCAPES },
- { "default", WRDSF_DEFFLAGS },
- { "env_kv", WRDSF_ENV_KV },
- { "incremental", WRDSF_INCREMENTAL },
- { "pathexpand", WRDSF_PATHEXPAND },
- { NULL, 0 }
+/* Environment types */
+enum env_type
+ {
+ env_none, /* No environment */
+ env_null, /* Null environment */
+ env_sys /* Use system environment */
};
-struct kwd opt_keytab[] = {
- { "nullglob", WRDSO_NULLGLOB },
- { "failglob", WRDSO_FAILGLOB },
- { "dotglob", WRDSO_DOTGLOB },
- { "bskeep_words", WRDSO_BSKEEP_WORD },
- { "bskeep_quote", WRDSO_BSKEEP_QUOTE },
- { "bskeep", WRDSO_BSKEEP_WORD|WRDSO_BSKEEP_QUOTE },
- { "novarsplit", WRDSO_NOVARSPLIT },
- { "nocmdsplit", WRDSO_NOCMDSPLIT },
- { NULL, 0 }
+struct wsclosure
+{
+ int options; /* Global options */
+ struct wordsplit ws; /* The wordsplit structure */
+ int wsflags; /* Wordsplit flags */
+ enum env_type env_type; /* Environment type */
+ int offarg; /* Index of the first of the initial words in
+ the argv array. The ws.ws_dooffs field gives
+ the number of such variables. Forces the
+ WRDSF_DOOFFS flag. */
+ char **fenvbase; /* Environment for testing the ws_getenv function */
+ int fenvidx; /* Number of variables in fenvbase */
+ int fenvmax; /* Size of fenbase (entries) */
+ int append_start; /* First argument to append (index in argv) */
+ int append_count; /* Number of arguments to append */
};
-struct kwd string_keytab[] = {
- { "delim", WRDSF_DELIM },
- { "comment", WRDSF_COMMENT },
- { "escape", WRDSF_ESCAPE },
- { NULL, 0 }
+/* Command line option types */
+enum
+ {
+ ws_no_argument, /* Option requires no arguments */
+ ws_boolean, /* Option is boolean (can be prefixed with -no) */
+ ws_required_argument, /* Option requires one argument */
+ ws_multiple_arguments /* Option takes multiple arguments, terminated with
+ "--" or end of argument list */
};
+/* Structure describing a single command-line option */
+struct wsopt
+{
+ const char *name; /* Option name */
+ int tok; /* Corresponding flag */
+ int arg; /* Option type (see the enum above) */
+ void (*setfn) (int tok, int neg, char *arg, struct wsclosure *wsc);
+ /* Setter function */
+};
+
+/* Index of the next argument in the argv */
+static int wsoptind = -1;
+
+/* Parse next argument from the command line. Return EOF on end of arguments
+ or when the "--" argument is seen. */
static int
-kwxlat (struct kwd *kwp, const char *str, int *res)
+getwsopt (int argc, char **argv, struct wsopt *wso, struct wsclosure *wsc)
+{
+ int negate = 0;
+ char *opt;
+
+ if (wsoptind == -1)
+ wsoptind = 1;
+ if (wsoptind == argc)
+ return EOF;
+
+ opt = argv[wsoptind++];
+ if (strcmp (opt, "--") == 0)
+ return EOF;
+ if (*opt != '-')
{
- for (; kwp->name; kwp++)
- if (strcmp (kwp->name, str) == 0)
+ if (strchr (opt, '='))
{
- *res = kwp->tok;
+ assert (wsc->fenvidx < wsc->fenvmax - 1);
+ wsc->fenvbase[wsc->fenvidx++] = opt;
return 0;
}
- return -1;
+ wsoptind--;
+ return EOF;
+ }
+ opt++; /* skip past initial dash */
+ if (strncmp (opt, "no-", 3) == 0)
+ {
+ negate = 1;
+ opt += 3;
+ }
+ else if (strncmp (opt, "no", 2) == 0)
+ {
+ negate = 1;
+ opt += 2;
+ }
+
+ for (; wso->name; wso++)
+ {
+ if (wso->arg == ws_boolean && wso->name[0] == 'n' && wso->name[1] == 'o'
+ && strcmp (wso->name + 2, opt) == 0)
+ {
+ negate ^= 1;
+ break;
+ }
+ if (strcmp (wso->name, opt) == 0)
+ break;
+ }
+
+ if (wso->name)
+ {
+ char *arg;
+ if (wso->arg == ws_multiple_arguments)
+ {
+ while (1)
+ {
+ if (wsoptind == argc)
+ break;
+ arg = argv[wsoptind++];
+ if (strcmp (arg, "--") == 0)
+ break;
+ wso->setfn (wso->tok, negate, arg, wsc);
+ }
+ }
+ else
+ {
+ if (wso->arg == ws_required_argument)
+ {
+ if (wsoptind == argc)
+ {
+ fprintf (stderr, "%s: missing arguments for -%s\n",
+ progname, opt);
+ exit (1);
+ }
+ arg = argv[wsoptind++];
+ }
+ wso->setfn (wso->tok, negate, arg, wsc);
+ }
+ return 0;
+ }
+
+ fprintf (stderr, "%s: unrecognized option: -%s\n",
+ progname, opt);
+ fprintf (stderr, "%s: try %s -help for more detail\n",
+ progname, progname);
+ exit (1);
+}
+
+/* Setter functions for various options */
+
+static void
+setfn_flag (int flag, int neg, char *arg, struct wsclosure *wsc)
+{
+ if (neg)
+ wsc->wsflags &= ~flag;
+ else
+ wsc->wsflags |= flag;
+}
+
+static void
+setfn_option (int flag, int neg, char *arg, struct wsclosure *wsc)
+{
+ wsc->wsflags |= WRDSF_OPTIONS;
+ if (neg)
+ wsc->ws.ws_options &= ~flag;
+ else
+ wsc->ws.ws_options |= flag;
}
@@ -94,3 +198,209 @@ kwxlat (struct kwd *kwp, const char *str, int *res)
static void
-help ()
+setfn_delim (int flag, int neg, char *arg, struct wsclosure *wsc)
+{
+ wsc->wsflags |= flag;
+ wsc->ws.ws_delim = arg;
+}
+
+static void
+setfn_comment (int flag, int neg, char *arg, struct wsclosure *wsc)
+{
+ wsc->wsflags |= flag;
+ wsc->ws.ws_comment = arg;
+}
+
+static void
+set_escape_string (wordsplit_t *ws, int *wsflags, int q, const char *str)
+{
+ if (*str == ':')
+ {
+ while (*++str != ':')
+ {
+ int f;
+ switch (*str)
+ {
+ case '+':
+ f = WRDSO_BSKEEP;
+ break;
+
+ case '0':
+ f = WRDSO_OESC;
+ break;
+
+ case 'x':
+ f = WRDSO_XESC;
+ break;
+
+ default:
+ fprintf (stderr, "%s: invalid escape flag near %s\n",
+ progname, str);
+ abort ();
+ }
+ WRDSO_ESC_SET (ws, q, f);
+ }
+ *wsflags |= WRDSF_OPTIONS;
+ ++str;
+ }
+ ws->ws_escape[q] = str;
+}
+
+static void
+setfn_escape (int flag, int neg, char *arg, struct wsclosure *wsc)
+{
+ wsc->wsflags |= flag;
+ set_escape_string (&wsc->ws, &wsc->wsflags, 0, arg);
+ set_escape_string (&wsc->ws, &wsc->wsflags, 1, arg);
+}
+
+static void
+setfn_escape_qw (char *arg, int quote, struct wsclosure *wsc)
+{
+ if (!(wsc->wsflags & WRDSF_ESCAPE))
+ {
+ wsc->wsflags |= WRDSF_ESCAPE;
+ wsc->ws.ws_escape[!quote] = NULL;
+ }
+ set_escape_string (&wsc->ws, &wsc->wsflags, quote, arg);
+}
+
+static void
+setfn_escape_word (int flag, int neg, char *arg, struct wsclosure *wsc)
+{
+ setfn_escape_qw (arg, 0, wsc);
+}
+
+static void
+setfn_escape_quote (int flag, int neg, char *arg, struct wsclosure *wsc)
+{
+ setfn_escape_qw (arg, 1, wsc);
+}
+
+static void
+setfn_maxwords (int flag, int neg, char *arg, struct wsclosure *wsc)
+{
+ char *p;
+
+ wsc->wsflags |= WRDSF_OPTIONS;
+ wsc->ws.ws_options |= WRDSO_MAXWORDS;
+
+ wsc->ws.ws_maxwords = strtoul (arg, &p, 10);
+ if (*p)
+ {
+ fprintf (stderr, "%s: invalid number: %s\n", progname, arg);
+ exit (1);
+ }
+}
+
+static void
+setfn_global (int flag, int neg, char *arg, struct wsclosure *wsc)
+{
+ if (neg)
+ wsc->options &= ~flag;
+ else
+ wsc->options |= flag;
+}
+
+static void
+setfn_env (int flag, int neg, char *arg, struct wsclosure *wsc)
+{
+ if (strcmp (arg, "none") == 0)
+ wsc->env_type = env_none;
+ else if (strcmp (arg, "null") == 0)
+ wsc->env_type = env_null;
+ else if (strcmp (arg, "sys") == 0)
+ wsc->env_type = env_sys;
+ else
+ {
+ fprintf (stderr, "%s: environment flag: %s\n", progname, arg);
+ exit (1);
+ }
+}
+
+static void
+setfn_dooffs (int flag, int neg, char *arg, struct wsclosure *wsc)
+{
+ if (!(wsc->wsflags & flag))
+ {
+ wsc->wsflags |= flag;
+ wsc->offarg = wsoptind - 1;
+ wsc->ws.ws_offs = 0;
+ }
+ wsc->ws.ws_offs++;
+}
+
+static void
+setfn_append (int flag, int neg, char *arg, struct wsclosure *wsc)
+{
+ if (wsc->append_count == 0)
+ wsc->append_start = wsoptind - 1;
+ wsc->append_count++;
+}
+
+static void help (void);
+
+static void
+setfn_help (int flag, int neg, char *arg, struct wsclosure *wsc)
+{
+ help ();
+ exit (0);
+}
+
+/* Available options: */
+struct wsopt opttab[] = {
+ /* Global options */
+ { "trimnl", TRIMNL_OPTION, ws_boolean, setfn_global },
+ { "plaintext", PLAINTEXT_OPTION, ws_boolean, setfn_global },
+ { "env", 0, ws_required_argument, setfn_env },
+
+ /* Wordsplit flags */
+ { "append", WRDSF_APPEND, ws_boolean, setfn_flag },
+ /*{ "reuse", WRDSF_REUSE, ws_boolean, setfn_flag },*/
+ { "undef", WRDSF_UNDEF, ws_boolean, setfn_flag },
+ { "novar", WRDSF_NOVAR, ws_boolean, setfn_flag },
+ { "nocmd", WRDSF_NOCMD, ws_boolean, setfn_flag },
+ { "ws", WRDSF_WS, ws_boolean, setfn_flag },
+ { "quote", WRDSF_QUOTE, ws_boolean, setfn_flag },
+ { "squote", WRDSF_SQUOTE, ws_boolean, setfn_flag },
+ { "dquote", WRDSF_DQUOTE, ws_boolean, setfn_flag },
+ { "squeeze_delims", WRDSF_SQUEEZE_DELIMS, ws_boolean, setfn_flag },
+ { "return_delims", WRDSF_RETURN_DELIMS, ws_boolean, setfn_flag },
+ { "sed", WRDSF_SED_EXPR, ws_boolean, setfn_flag },
+ { "debug", WRDSF_SHOWDBG, ws_boolean, setfn_flag },
+ { "nosplit", WRDSF_NOSPLIT, ws_boolean, setfn_flag },
+ { "keepundef", WRDSF_KEEPUNDEF, ws_boolean, setfn_flag },
+ { "warnundef", WRDSF_WARNUNDEF, ws_boolean, setfn_flag },
+ { "cescapes", WRDSF_CESCAPES, ws_boolean, setfn_flag },
+ { "default", WRDSF_DEFFLAGS, ws_boolean, setfn_flag },
+ { "env_kv", WRDSF_ENV_KV, ws_boolean, setfn_flag },
+ { "incremental", WRDSF_INCREMENTAL, ws_boolean, setfn_flag },
+ { "pathexpand", WRDSF_PATHEXPAND, ws_boolean, setfn_flag },
+ { "default", WRDSF_DEFFLAGS, ws_boolean, setfn_flag },
+ /* Wordsplit options */
+ { "nullglob", WRDSO_NULLGLOB, ws_boolean, setfn_option },
+ { "failglob", WRDSO_FAILGLOB, ws_boolean, setfn_option },
+ { "dotglob", WRDSO_DOTGLOB, ws_boolean, setfn_option },
+ { "bskeep_words", WRDSO_BSKEEP_WORD, ws_boolean, setfn_option },
+ { "bskeep_quote", WRDSO_BSKEEP_QUOTE, ws_boolean, setfn_option },
+ { "bskeep", WRDSO_BSKEEP_WORD|WRDSO_BSKEEP_QUOTE,
+ ws_boolean, setfn_option },
+ { "novarsplit", WRDSO_NOVARSPLIT, ws_boolean, setfn_option },
+ { "nocmdsplit", WRDSO_NOCMDSPLIT, ws_boolean, setfn_option },
+ { "maxwords", WRDSO_MAXWORDS, ws_required_argument, setfn_maxwords },
+ /* String options */
+ { "delim", WRDSF_DELIM, ws_required_argument, setfn_delim },
+ { "comment", WRDSF_COMMENT,ws_required_argument, setfn_comment },
+ { "escape", WRDSF_ESCAPE, ws_required_argument, setfn_escape },
+ { "escape-word", WRDSF_ESCAPE, ws_required_argument, setfn_escape_word },
+ { "escape-quote", WRDSF_ESCAPE, ws_required_argument, setfn_escape_quote },
+
+ { "dooffs", WRDSF_DOOFFS, ws_multiple_arguments, setfn_dooffs },
+ { "append-args", 0, ws_multiple_arguments, setfn_append },
+
+ { "help", 0, ws_no_argument, setfn_help },
+
+ { NULL, 0 }
+};
+
+static void
+help (void)
{
@@ -100,26 +410,25 @@ help ()
printf ("options are:\n");
- printf (" [-]trimnl\n");
- printf (" [-]plaintext\n");
- printf (" -env\n");
- printf (" env sys|none|null\n");
- putchar ('\n');
- for (i = 0; bool_keytab[i].name; i++)
- printf (" [-]%s\n", bool_keytab[i].name);
- putchar ('\n');
- for (i = 0; string_keytab[i].name; i++)
+ for (i = 0; opttab[i].name; i++)
{
- printf (" -%s\n", string_keytab[i].name);
- printf (" %s ARG\n", string_keytab[i].name);
+ printf (" -");
+ if (opttab[i].arg == ws_boolean)
+ printf ("[no]");
+ if (strncmp (opttab[i].name, "no", 2) == 0)
+ printf ("%s", opttab[i].name + 2);
+ else
+ printf ("%s", opttab[i].name);
+ switch (opttab[i].arg)
+ {
+ case ws_no_argument:
+ case ws_boolean:
+ break;
+ case ws_required_argument:
+ printf(" ARG");
+ break;
+ case ws_multiple_arguments:
+ printf(" ARGS... --");
}
- printf (" escape-word ARG\n");
- printf (" escape-quote ARG\n");
putchar ('\n');
- for (i = 0; opt_keytab[i].name; i++)
- {
- printf (" [-]%s\n", opt_keytab[i].name);
}
putchar ('\n');
- printf (" -dooffs\n");
- printf (" dooffs COUNT ARGS...\n");
- exit (0);
}
@@ -301,51 +610,2 @@ wsp_runcmd (char **ret, const char *str, size_t len, char **argv, void *closure)
-enum env_type
- {
- env_none,
- env_null,
- env_sys
- };
-
-struct kwd env_keytab[] = {
- { "none", env_none },
- { "null", env_null },
- { "sys", env_sys },
- { NULL }
-};
-
-static void
-set_escape_string (wordsplit_t *ws, int *wsflags, int q, const char *str)
-{
- if (*str == ':')
- {
- while (*++str != ':')
- {
- int f;
- switch (*str)
- {
- case '+':
- f = WRDSO_BSKEEP;
- break;
-
- case '0':
- f = WRDSO_OESC;</