diff options
-rw-r--r-- | include/wordsplit.h | 20 | ||||
-rw-r--r-- | src/wordsplit.c | 201 | ||||
-rw-r--r-- | tests/wordsplit.at | 122 | ||||
-rw-r--r-- | tests/wsp.c | 832 |
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 }, |