aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/wordsplit.h20
-rw-r--r--src/wordsplit.c201
-rw-r--r--tests/wordsplit.at122
-rw-r--r--tests/wsp.c832
4 files changed, 704 insertions, 471 deletions
diff --git a/include/wordsplit.h b/include/wordsplit.h
index a175275..d7eb26f 100644
--- a/include/wordsplit.h
+++ b/include/wordsplit.h
@@ -72,12 +72,22 @@ struct wordsplit
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 */
+
+ 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
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
@@ -96,13 +106,13 @@ struct wordsplit
location pointed to by RET the expansion of
the command CMD (LEN bytes long). On input,
ARGV contains CMD split out to words.
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
ws_input. */
int ws_errno; /* [Output] Error code, if an error occurred. */
char *ws_usererr; /* Points to textual description of
@@ -223,12 +233,18 @@ struct wordsplit
(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
#define WRDSO_XESC WRDSO_XESC_WORD
/* Indices into ws_escape */
#define WRDSX_WORD 0
@@ -247,18 +263,20 @@ struct wordsplit
#define WRDSE_CBRACE 4
#define WRDSE_UNDEF 5
#define WRDSE_NOINPUT 6
#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)
__attribute__ ((deprecated));
static inline void
diff --git a/src/wordsplit.c b/src/wordsplit.c
index 521a1eb..6a33636 100644
--- a/src/wordsplit.c
+++ b/src/wordsplit.c
@@ -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.
@@ -24,12 +24,13 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <pwd.h>
#include <glob.h>
+#include <limits.h>
#if ENABLE_NLS
# include <gettext.h>
#else
# define gettext(msgid) msgid
#endif
@@ -53,12 +54,15 @@
#define ISVARBEG(c) (ISALPHA(c) || c == '_')
#define ISVARCHR(c) (ISALNUM(c) || c == '_')
#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
static void
_wsplt_alloc_die (struct wordsplit *wsp)
{
@@ -281,12 +285,20 @@ wordsplit_init (struct wordsplit *wsp, const char *input, size_t len,
{
wsp->ws_escape[WRDSX_WORD] = "";
wsp->ws_escape[WRDSX_QUOTE] = "\\\\\"\"";
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;
if (wsp->ws_flags & WRDSF_REUSE)
wordsplit_free_nodes (wsp);
@@ -1033,13 +1045,13 @@ wordsplit_find_env (struct wordsplit *wsp, const char *name, size_t len,
}
return WRDSE_UNDEF;
}
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;
if (wsp->ws_envidx + n >= wsp->ws_envsiz)
{
@@ -1068,13 +1080,13 @@ wsplt_assign_var (struct wordsplit *wsp, const char *name, size_t namelen,
{
newenv[j] = strdup (wsp->ws_env[j]);
if (!newenv[j])
{
for (; j > 1; j--)
free (newenv[j-1]);
- free (newenv[j-1]);
+ free (newenv);
return _wsplt_nomem (wsp);
}
}
newenv[j] = NULL;
wsp->ws_envbuf = newenv;
@@ -1139,12 +1151,76 @@ 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;
}
+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
expvar_recover (struct wordsplit *wsp, const char *str,
struct wordsplit_node **ptail, const char **pend, int flg)
{
@@ -1174,30 +1250,55 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
char *value;
const char *vptr;
struct wordsplit_node *newnode;
const char *start = str - 1;
int rc;
struct wordsplit ws;
+ int is_param = 0;
+ long param_idx = 0;
if (ISVARBEG (str[0]))
{
for (i = 1; i < len; i++)
if (!ISVARCHR (str[i]))
break;
*pend = str + i - 1;
}
- else if (ISDIGIT (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]) || 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))
{
str++;
len--;
- for (i = 1; i < len; i++)
+ for (i = str[0] == '-' ? 1 : 0; i < len; i++)
{
if (str[i] == ':')
{
size_t j;
defstr = str + i + 1;
@@ -1219,24 +1320,35 @@ 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]))
+ 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);
}
}
else if (!ISVARCHR (str[i]))
{
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);
}
else
{
return expvar_recover (wsp, str, ptail, pend, flg);
@@ -1251,29 +1363,45 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
{
rc = WRDSE_UNDEF;
defstr = NULL;
}
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] == ':')
{
free (value);
rc = WRDSE_UNDEF;
@@ -1318,13 +1446,23 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
value = ws.ws_wordv[0];
ws.ws_wordv[0] = NULL;
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
{
if (*defstr == '?')
{
size = *pend - ++defstr;
@@ -1455,13 +1593,13 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
return 0;
}
static int
begin_var_p (int c)
{
- return c == '{' || ISVARBEG (c) || ISDIGIT (c);
+ return c == '{' || 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,
@@ -2103,15 +2241,12 @@ 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)
{
int i, val;
for (i = 0, val = 0; i < cnt; i++, src++)
@@ -2495,13 +2630,13 @@ wordsplit_free_words (struct wordsplit *ws)
ws->ws_wordc = 0;
}
void
wordsplit_free_envbuf (struct wordsplit *ws)
{
- if (ws->ws_flags & WRDSF_NOCMD)
+ if (!(ws->ws_flags & WRDSF_ENV))
return;
if (ws->ws_envbuf)
{
size_t i;
for (i = 0; ws->ws_envbuf[i]; i++)
@@ -2510,12 +2645,29 @@ wordsplit_free_envbuf (struct wordsplit *ws)
ws->ws_envidx = ws->ws_envsiz = 0;
ws->ws_envbuf = NULL;
}
}
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)
free (ws->ws_usererr);
ws->ws_usererr = NULL;
ws->ws_errno = WRDSE_OK;
@@ -2526,12 +2678,13 @@ wordsplit_free (struct wordsplit *ws)
{
wordsplit_free_nodes (ws);
wordsplit_free_words (ws);
free (ws->ws_wordv);
ws->ws_wordv = NULL;
wordsplit_free_envbuf (ws);
+ wordsplit_free_parambuf (ws);
}
int
wordsplit_get_words (struct wordsplit *ws, size_t *wordc, char ***wordv)
{
char **p = realloc (ws->ws_wordv,
@@ -2554,13 +2707,15 @@ const char *_wordsplit_errstr[] = {
N_("memory exhausted"),
N_("invalid wordsplit usage"),
N_("unbalanced curly brace"),
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]);
const char *
wordsplit_strerror (struct wordsplit *ws)
diff --git a/tests/wordsplit.at b/tests/wordsplit.at
index 1f2e80d..ebd168b 100644
--- a/tests/wordsplit.at
+++ b/tests/wordsplit.at
@@ -105,13 +105,13 @@ TOTAL: 1
WSPGROUP()
dnl ------------------------------------------------------------
dnl Test worsplit-specific behavior
dnl ------------------------------------------------------------
-TESTWSP([append],[wsp-append],[append],
+TESTWSP([append],[wsp-append],[-append],
[jeden dwa trzy
cztery
piec szesc],
[NF: 3
0: jeden
1: dwa
@@ -130,13 +130,13 @@ NF: 6
3: cztery
4: piec
5: szesc
TOTAL: 2
])
-TESTWSP([dooffs],[wsp-doofs ],[dooffs 3 jeden dwa trzy],
+TESTWSP([dooffs],[wsp-doofs ],[-dooffs jeden dwa trzy],
[cztery piec],
[NF: 2 (3)
(0): jeden
(1): dwa
(2): trzy
3: cztery
@@ -211,96 +211,87 @@ TESTWSP([undefined variables 1],[],[],
2: ab
TOTAL: 3
],
[],
[unset FOO;])
-TESTWSP([undefined variables 2],[],[keepundef],
+TESTWSP([undefined variables 2],[],[-keepundef],
[a $FOO test a${FOO}b],
[NF: 4
0: a
1: $FOO
2: test
3: a${FOO}b
TOTAL: 4
],
[],
[unset FOO;])
-TESTWSP([warn about undefined variables],[],[warnundef],
+TESTWSP([warn about undefined variables],[],[-warnundef],
[$FOO],
[NF: 0
TOTAL: 0
],
[warning: undefined variable `FOO'
],
[unset FOO;])
-TESTWSP([bail out on undefined variables],[],[undef],
+TESTWSP([bail out on undefined variables],[],[-undef],
[$FOO],
[],
[undefined variable
],
[unset FOO;])
-TESTWSP([disable variable expansion],[],[novar],
+TESTWSP([disable variable expansion],[],[-novar],
[$FOO],
[NF: 1
0: $FOO
TOTAL: 1
],
[],
[FOO=bar])
TESTWSP([K/V environment],[wsp-env-kv wsp-env_kv],
-[env_kv],
+[-env_kv],
[$FOO a$BAZ],
[NF: 2
0: bar
1: aqux
TOTAL: 2
],
[],
[FOO=bar BAZ=qux])
-TESTWSP([nosplit with variable expansion],[wsp-var-nosplit],[nosplit],
+TESTWSP([nosplit with variable expansion],[wsp-var-nosplit],[-nosplit],
[a $FOO test],
[NF: 1
0: "a variable expansion test\n"
TOTAL: 1
],
[],
[FOO="variable expansion"])
-TESTWSP([nosplit without variable expansion],[],[nosplit novar],
+TESTWSP([nosplit without variable expansion],[],[-nosplit -novar],
[a $FOO test],
[NF: 1
0: "a $FOO test\n"
TOTAL: 1
],
[],
[FOO="variable expansion"])
-TESTWSP([nosplit: empty expansion],[],[nosplit trimnl],
+TESTWSP([nosplit: empty expansion],[],[-nosplit -trimnl],
[$FOO],
[NF: 1
0: ""
TOTAL: 1
],
[],
[FOO=""])
-TESTWSP([default value (defined)],[],[],
-[${FOO:-bar}],
-[NF: 1
-0: qux
-TOTAL: 1
-],
-[],
-[FOO=qux])
-
TESTWSP([default value],[],[],
[${FOO:-bar}],
[NF: 1
0: bar
TOTAL: 1
])
@@ -448,49 +439,49 @@ TESTWSP([getvar, alternate value],[wsp-getvar],
1: isset
TOTAL: 2
])
WSPGROUP()
-TESTWSP([ignore quotes],[wsp-ignore-quotes ],[-quote],
+TESTWSP([ignore quotes],[wsp-ignore-quotes ],[-noquote],
["a text"],
[NF: 2
0: "\"a"
1: "text\""
TOTAL: 2
])
WSPGROUP(wsp-delim)
TESTWSP([custom delimiters (squeeze)],[],
-[delim : -ws trimnl],
+[-delim : -nows -trimnl],
[semicolon: separated::list: of :words],
[NF: 5
0: semicolon
1: " separated"
2: list
3: " of "
4: words
TOTAL: 5
])
TESTWSP([custom delimiters (no squeeze)],[],
-[delim : -ws -squeeze_delims trimnl],
+[-delim : -nows -nosqueeze_delims -trimnl],
[semicolon: separated::list: of :words],
[NF: 6
0: semicolon
1: " separated"
2: ""
3: list
4: " of "
5: words
TOTAL: 6
])
TESTWSP([custom, with returned delimiters],[],
-[delim : -ws trimnl return_delims],
+[-delim : -nows -trimnl -return_delims],
[semicolon: separated::list: of :words],
[NF: 9
0: semicolon
1: :
2: " separated"
3: :
@@ -500,13 +491,13 @@ TESTWSP([custom, with returned delimiters],[],
7: :
8: words
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],
[NF: 10
0: semicolon
1: :
2: " separated"
3: :
@@ -518,145 +509,145 @@ TESTWSP([custom, with returned & squeezed delimiters],[],
9: words
TOTAL: 10
])
WSPGROUP(wsp-sed)
-TESTWSP([sed expressions],[],[sed],
+TESTWSP([sed expressions],[],[-sed],
[arg1 s/foo/bar/g;s/bar baz/quz quux/ arg2],
[NF: 3
0: arg1
1: "s/foo/bar/g;s/bar baz/quz quux/"
2: arg2
TOTAL: 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],
[NF: 4
0: a\ttab
1: form\ffeed
2: and
3: new\nline
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],
[NF: 4
0: attab
1: formffeed
2: and
3: newnline
TOTAL: 4
])
-TESTWSP([ws elimination],[wsp-ws-elim],[delim ' ()' ws return_delims],
+TESTWSP([ws elimination],[wsp-ws-elim],[-delim ' ()' -ws -return_delims],
[( list items )],
[NF: 4
0: (
1: list
2: items
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" ],
[NF: 7
0: foo
1: :
2: bar
3: ,
4: quux
5: :
6: baaz
TOTAL: 7
])
-TESTWSP([empty quotes],[wsp-empty-quotes],[delim : ws return_delims],
+TESTWSP([empty quotes],[wsp-empty-quotes],[-delim : -ws -return_delims],
[t=""],
[NF: 1
0: t=
TOTAL: 1
])
TESTWSP([delimiter following empty quotes],
-[],[delim : ws return_delims],
+[],[-delim : -ws -return_delims],
[t="":r],
[NF: 3
0: t=
1: :
2: r
TOTAL: 3
])
TESTWSP([suppress ws trimming within quotes],
[],
-[default delim , ws return_delims],
+[-default -delim , -ws -return_delims],
[nocomponent,nonewline, formatfield="In message %{text}, "],
[NF: 5
0: nocomponent
1: ,
2: nonewline
3: ,
4: "formatfield=In message %{text}, "
TOTAL: 5
])
TESTWSP([unescape],
[wsp-unescape wsp-unescape-simple],
-[-default novar nocmd quote escape ':+:\\""'],
+[-nodefault -novar -nocmd -quote -escape ':+:\\""'],
[\Seen "quote \"" "bs \\"],
[NF: 3
0: \\Seen
1: "quote \""
2: "bs \\"
TOTAL: 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],
[NF: 6
0: Seen
1: "quote \""
2: "bs \\"
3: "31 A"
4: 3x31
5: 101
TOTAL: 6
])
-TESTWSP([dquote],[],[-default novar nocmd dquote],
+TESTWSP([dquote],[],[-nodefault -novar -nocmd -dquote],
[a "quoted example" isn't it],
[NF: 4
0: a
1: "quoted example"
2: isn't
3: it
TOTAL: 4
])
-TESTWSP([squote],[],[-default novar nocmd squote],
+TESTWSP([squote],[],[-nodefault -novar -nocmd -squote],
[a 'quoted example' isn"t it],
[NF: 4
0: a
1: "quoted example"
2: "isn\"t"
3: it
TOTAL: 4
])
WSPGROUP(wsp-incr)
-TESTWSP([incremental],[],[incremental],
+TESTWSP([incremental],[],[-incremental],
[incremental "input test" line
],
[NF: 1
0: incremental
@@ -668,13 +659,13 @@ NF: 1
0: line
TOTAL: 3
],
[input exhausted
])
-TESTWSP([incremental append],[],[incremental append],
+TESTWSP([incremental append],[],[-incremental -append],
[incremental "input test" line
],
[NF: 1
0: incremental
@@ -690,13 +681,13 @@ NF: 3
TOTAL: 3
],
[input exhausted
])
TESTWSP([incremental ws],
-[],[return_delims -squeeze_delims incremental ws],
+[],[-return_delims -nosqueeze_delims -incremental -ws],
[a list test
],
[NF: 1
0: a
@@ -708,75 +699,75 @@ NF: 1
0: test
TOTAL: 3
],
[input exhausted
])
-TESTWSP([incremental nosplit],[],[incremental nosplit],
+TESTWSP([incremental nosplit],[],[-incremental -nosplit],
[incremental "input test" line
],
[NF: 1
0: "incremental input test line"
TOTAL: 1
],
[input exhausted
])
-TESTWSP([simple command substitution],[],[-nocmd],
+TESTWSP([simple command substitution],[],[-cmd],
[begin $(words a b) end],
[NF: 4
0: begin
1: a
2: b
3: end
TOTAL: 4
])
-TESTWSP([quoted command substitution],[],[-nocmd],
+TESTWSP([quoted command substitution],[],[-cmd],
[begin "$(words a b)" end],
[NF: 3
0: begin
1: "a b"
2: end
TOTAL: 3
])
-TESTWSP([coalesced command substitution],[],[-nocmd],
+TESTWSP([coalesced command substitution],[],[-cmd],
[begin($(words a b))end],
[NF: 2
0: begin(a
1: b)end
TOTAL: 2
])
-TESTWSP([quoted coalesced command substitution],[],[-nocmd],
+TESTWSP([quoted coalesced command substitution],[],[-cmd],
["begin($(words a b))end"],
[NF: 1
0: "begin(a b)end"
TOTAL: 1
])
-TESTWSP([variable and command substitution],[],[-nocmd -novar],
+TESTWSP([variable and command substitution],[],[-cmd -var],
[begin $X $(words $X $Y) end],
[NF: 5
0: begin
1: a
2: a
3: b
4: end
TOTAL: 5
],[],[X=a Y=b])
-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"],
[NF: 1
0: "begin(a b)end"
TOTAL: 1
],[],[X=a Y=b BEGIN=begin])
-TESTWSP([nested commands],[],[-nocmd -novar],
+TESTWSP([nested commands],[],[-cmd -var],
[$(words output $(words in$SUFFIX text) end)],
[NF: 4
0: output
1: input
2: text
3: end
@@ -789,13 +780,13 @@ AT_KEYWORDS([wordsplit wsp wsp-path wsp-path-1])
AT_CHECK([
mkdir dir
> dir/1.c
> dir/2.c
> dir/3.b
-wsp pathexpand<<'EOT'
+wsp -pathexpand<<'EOT'
begin dir/*.c end
EOT
],
[0],
[NF: 4
0: begin
@@ -810,13 +801,13 @@ AT_SETUP([pathname expansion: no match])
AT_KEYWORDS([wordsplit wsp wsp-path wsp-path-2])
AT_CHECK([
mkdir dir
> dir/1.c
> dir/2.b
-wsp pathexpand<<'EOT'
+wsp -pathexpand<<'EOT'
begin dir/*.d end
EOT
],
[0],
[NF: 3
0: begin
@@ -830,13 +821,13 @@ AT_SETUP([pathname expansion: nullglob])
AT_KEYWORDS([wordsplit wsp wsp-path wsp-path-3])
AT_CHECK([
mkdir dir
> dir/1.c
> dir/2.b
-wsp pathexpand nullglob<<'EOT'
+wsp -pathexpand -nullglob<<'EOT'
begin dir/*.d end
EOT
],
[0],
[NF: 2
0: begin
@@ -849,36 +840,36 @@ AT_SETUP([pathname expansion: failglob])
AT_KEYWORDS([wordsplit wsp wsp-path wsp-path-4])
AT_CHECK([
mkdir dir
> dir/1.c
> dir/2.b
-wsp pathexpand failglob<<'EOT'
+wsp -pathexpand -failglob<<'EOT'
begin dir/*.d end
EOT
],
[0],
[],
[no files match pattern dir/*.d
])
AT_CLEANUP
-TESTWSP([append],[],[-- extra arguments follow],
+TESTWSP([append],[],[-append-args extra arguments follow],
[some words and],
[NF: 6
0: some
1: words
2: and
3: extra
4: arguments
5: follow
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],
[NF: 7 (2)
(0): preface
(1): words
2: some
3: words
@@ -889,23 +880,23 @@ TESTWSP([append + dooffs + env],[],
8: follow
TOTAL: 4
])
# Maxwords
TESTWSP([maxwords],[],
-[trimnl maxwords 3],
+[-trimnl -maxwords 3],
[ws_maxwords limits the number of returned words],
[NF: 3
0: ws_maxwords
1: limits
2: "the number of returned words"
TOTAL: 3
])
TESTWSP([maxwords return_delims],[],
-[trimnl maxwords 8 return_delims delim :-],
+[-trimnl -maxwords 8 -return_delims -delim :-],
[foo:::bar-:baz-quux:ux:zu],
[NF: 8
0: foo
1: :
2: bar
3: -
@@ -914,13 +905,13 @@ TESTWSP([maxwords return_delims],[],
6: -
7: quux:ux:zu
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-],
[NF: 8
0: foo
1: :
2: :
3: :
@@ -929,13 +920,13 @@ TESTWSP([maxwords return_delims -squeeze_delims],[],
6: :
7: baz:qux-
TOTAL: 8
])
TESTWSP([maxwords incremental],[],
-[trimnl maxwords 3 incremental],
+[-trimnl -maxwords 3 -incremental],
[foo bar baz qux uz
],
[NF: 1
0: foo
@@ -947,30 +938,43 @@ NF: 1
0: "baz qux uz"
TOTAL: 3
],
[input exhausted
]))
-TESTWSP([variable nosplit],[],[novar novarsplit],
+TESTWSP([variable nosplit],[],[-novar -novarsplit],
[begin ${VAR:- a b} end],
[NF: 3
0: begin
1: "${VAR:- a b}"
2: end
TOTAL: 3
])
-TESTWSP([command nosplit],[],[nocmd nocmdsplit],
+TESTWSP([command nosplit],[],[-nocmd -nocmdsplit],
[begin $(words a b) end],
[NF: 3
0: begin
1: "$(words a b)"
2: end
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])
m4_popdef([wspnum])
m4_popdef([wspid])
m4_popdef([genkw])
m4_popdef([wspgroupnum])
m4_popdef([wspgroupname])
diff --git a/tests/wsp.c b/tests/wsp.c
index bd13e63..958d01f 100644
--- a/tests/wsp.c
+++ b/tests/wsp.c
@@ -1,8 +1,8 @@
/* grecs - Gray's Extensible Configuration System
- Copyright (C) 2014-2016 Sergey Poznyakoff
+ Copyright (C) 2014-2019 Sergey Poznyakoff
Grecs 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.
@@ -26,105 +26,414 @@
#include "wordsplit.h"
extern char **environ;
char *progname;
-struct kwd
+/* Global options */
+enum
+ {
+ TRIMNL_OPTION = 0x01, /* Remove trailing newline */
+ PLAINTEXT_OPTION = 0x02 /* Print intput verbatim (no escapes) */
+ };
+
+/* Environment types */
+enum env_type
+ {
+ env_none, /* No environment */
+ env_null, /* Null environment */
+ env_sys /* Use system environment */
+ };
+
+struct wsclosure
{
- const char *name;
- int tok;
+ 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 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 },