From 458c044148cd2cb4a373a7953e58bc091887d20a Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Tue, 9 Sep 2014 00:35:55 +0300 Subject: Add wordsplit test (from mailutils) * configure.ac (AC_CONFIG_SRCDIR): Fix argument * src/Make-inst.am (include_HEADERS): Add grecsopt.h * tests/.gitignore: Add wsp.c * tests/Makefile.am: Add new tests. * tests/testsuite.at: Likewise. * tests/wordsplit.at: New testcase. * tests/wsp.c: New test program. --- build-aux/.gitignore | 1 + configure.ac | 6 +- src/Make-inst.am | 2 +- tests/.gitignore | 1 + tests/Makefile.am | 4 +- tests/testsuite.at | 2 + tests/wordsplit.at | 441 +++++++++++++++++++++++++++++++++++++++++++++++++++ tests/wsp.c | 377 +++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 829 insertions(+), 5 deletions(-) create mode 100644 tests/wordsplit.at create mode 100644 tests/wsp.c diff --git a/build-aux/.gitignore b/build-aux/.gitignore index ce7170e..0f6c7db 100644 --- a/build-aux/.gitignore +++ b/build-aux/.gitignore @@ -1,4 +1,5 @@ config.* +compile depcomp install-sh lt* diff --git a/configure.ac b/configure.ac index b3c455c..2507e8b 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ # This file is part of Grecs -*- autoconf -*- -# Copyright (C) 2011-2012 Sergey Poznyakoff +# Copyright (C) 2011-2014 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 @@ -15,8 +15,8 @@ # along with Grecs. If not, see . AC_PREREQ(2.63) -AC_INIT([libgrecs], 1.0, [gray+grecs@gnu.org.ua]) -AC_CONFIG_SRCDIR([src/grecs.h]) +AC_INIT([libgrecs], [1.0], [gray+grecs@gnu.org.ua]) +AC_CONFIG_SRCDIR([src/grecs.hin]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_HEADER([config.h]) AM_INIT_AUTOMAKE([1.11 gnu tar-ustar dist-xz std-options silent-rules]) diff --git a/src/Make-inst.am b/src/Make-inst.am index 65495bc..81b7747 100644 --- a/src/Make-inst.am +++ b/src/Make-inst.am @@ -17,6 +17,6 @@ include Make.am lib_LTLIBRARIES=libgrecs.la libgrecs_la_SOURCES = $(GRECS_SRC) -include_HEADERS = grecs.h wordsplit.h +include_HEADERS = grecs.h grecsopt.h wordsplit.h m4datadir = $(datadir)/aclocal dist_m4data_DATA = libgrecs.m4 diff --git a/tests/.gitignore b/tests/.gitignore index 1860fd4..22061b6 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -9,3 +9,4 @@ gcfver package.m4 testsuite testsuite.log +wsp diff --git a/tests/Makefile.am b/tests/Makefile.am index 76cfbfe..61c6a49 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -107,6 +107,7 @@ TESTSUITE_AT = \ sort01.at\ testsuite.at\ vercmp.at\ + wordsplit.at\ @GRECS_DISTCK_AT@ TESTSUITE = $(srcdir)/testsuite @@ -136,7 +137,8 @@ noinst_PROGRAMS = \ gcfenum\ gcfpeek\ gcfset\ - gcfver + gcfver\ + wsp LDADD = @GRECS_LDADD@ $(LIBINTL) AM_CPPFLAGS = @GRECS_INCLUDES@ @GRECS_HOST_PROJECT_INCLUDES@ diff --git a/tests/testsuite.at b/tests/testsuite.at index 2d42d4d..ff36835 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -112,4 +112,6 @@ m4_include([locus01.at]) m4_include([locus02.at]) m4_include([path-locus.at]) +m4_include([wordsplit.at]) + # End of testsuite.at diff --git a/tests/wordsplit.at b/tests/wordsplit.at new file mode 100644 index 0000000..d74cbe9 --- /dev/null +++ b/tests/wordsplit.at @@ -0,0 +1,441 @@ +# This file is part of grecs -*- Autotest -*- +# Copyright (C) 2014 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, or (at your option) +# any later version. +# +# Grecs is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Grecs. If not, see . + +AT_BANNER(Wordsplit) + +dnl ------------------------------------------------------------ +dnl TESTWSP([NAME], [KW = `'], [OPTS], [INPUT], [STDOUT = `'], +dnl [STDERR = `'], [ENV]) +dnl +m4_pushdef([TESTWSP],[ +AT_SETUP([$1]) +AT_KEYWORDS([wordsplit wsp $2]) +AT_CHECK([$7 wsp $3 <<'EOT' +[$4] +EOT +], +[0], +[$5], +[$6]) +AT_CLEANUP +]) + +dnl ------------------------------------------------------------ +dnl The first part reproduces legacy argcv tests +dnl ------------------------------------------------------------ + +TESTWSP([simple input],[wsp-simple wsp00],[], +[1 2 3], +[NF: 3 +0: 1 +1: 2 +2: 3 +]) + +TESTWSP([quoted space],[wsp-quoted wsp01],[], +[quoted\ space], +[NF: 1 +0: "quoted space" +]) + +TESTWSP([tab character],[wsp-tab wsp02],[], +[a "tab character"], +[NF: 2 +0: a +1: tab\tcharacter +]) + +TESTWSP([octal and hex escapes],[wsp-escape wsp-escape0 wsp03],[], +[\157\143\164\141\154\40and\x20\x68\x65\x78], +[NF: 1 +0: "octal and hex" +]) + +TESTWSP([octal and hex escapes 2],[wsp-escape wsp-escape1 wsp04],[], +[\157\143\164\141\154\40 and \x20\x68\x65\x78], +[NF: 3 +0: "octal " +1: and +2: " hex" +]) + +TESTWSP([escape representation],[wsp-escape wsp-escape2 wsp05],[], +[A\x3-\48\39], +[NF: 1 +0: A\003-\0048\0039 +]) + +dnl ------------------------------------------------------------ +dnl Test worsplit-specific behavior +dnl ------------------------------------------------------------ +TESTWSP([append],[wsp-append wsp06],[append], +[jeden dwa trzy +cztery +piec szesc], +[NF: 3 +0: jeden +1: dwa +2: trzy +NF: 4 +0: jeden +1: dwa +2: trzy +3: cztery +NF: 6 +0: jeden +1: dwa +2: trzy +3: cztery +4: piec +5: szesc +]) + +TESTWSP([dooffs],[wsp-doofs wsp07],[dooffs 3 jeden dwa trzy], +[cztery piec], +[NF: 2 (3) +(0): jeden +(1): dwa +(2): trzy +3: cztery +4: piec +]) + +TESTWSP([variable substitutions: single var],[wsp-var wsp-var00 wsp08],[], +[a $FOO test], +[NF: 3 +0: a +1: bar +2: test +], +[], +[FOO=bar]) + +TESTWSP([variable substitutions: concatenated vars],[wsp-var wsp-var01 wsp09], +[], +[a $FOO${BAR}ent test], +[NF: 3 +0: a +1: stringent +2: test +], +[], +[FOO=str BAR=ing]) + +TESTWSP([variable substitutions: field splitting],[wsp-var wsp-var02 wsp10],[], +[a $FOO test], +[NF: 4 +0: a +1: variable +2: substitution +3: test +], +[], +[FOO="variable substitution"]) + +TESTWSP([variable substitutions: double-quoted variable], +[wsp-var wsp-var03 wsp11],[], +[a "$FOO" test], +[NF: 3 +0: a +1: "variable substitution" +2: test +], +[], +[FOO="variable substitution"]) + +TESTWSP([variable substitutions: single-quoted variable], +[wsp-var wsp-var04 wsp12],[], +[a '$FOO' test], +[NF: 3 +0: a +1: $FOO +2: test +], +[], +[FOO="variable substitution"]) + +TESTWSP([undefined variables 1],[wsp-var wsp-var05 wsp13],[], +[a $FOO test a${FOO}b], +[NF: 3 +0: a +1: test +2: ab +], +[], +[unset FOO;]) + +TESTWSP([undefined variables 2],[wsp-var wsp-var06 wsp14],[keepundef], +[a $FOO test a${FOO}b], +[NF: 4 +0: a +1: $FOO +2: test +3: a${FOO}b +], +[], +[unset FOO;]) + +TESTWSP([warn about undefined variables],[wsp-var wsp-var07 wsp15],[warnundef], +[$FOO], +[NF: 0 +], +[warning: undefined variable `FOO' +], +[unset FOO;]) + +TESTWSP([bail out on undefined variables],[wsp-var wsp-var08 wsp16],[undef], +[$FOO], +[], +[undefined variable +], +[unset FOO;]) + +TESTWSP([disable variable expansion],[wsp-var wsp-var09 wsp17],[novar], +[$FOO], +[NF: 1 +0: $FOO +], +[], +[FOO=bar]) + +TESTWSP([K/V environment],[wsp-var wsp-var10 wsp18 wsp-env-kv wsp-env_kv], +[env_kv], +[$FOO a$BAZ], +[NF: 2 +0: bar +1: aqux +], +[], +[FOO=bar BAZ=qux]) + +TESTWSP([nosplit with expansion], +[wsp-var wsp-var11 wsp19 wsp-var-nosplit],[nosplit], +[a $FOO test], +[NF: 1 +0: "a variable expansion test\n" +], +[], +[FOO="variable expansion"]) + +TESTWSP([nosplit without expansion],[wsp-var wsp-var12 wsp20],[nosplit novar], +[a $FOO test], +[NF: 1 +0: "a $FOO test\n" +], +[], +[FOO="variable expansion"]) + +TESTWSP([ignore quotes],[wsp-ignore-quotes wsp21],[-quote], +["a text"], +[NF: 2 +0: "\"a" +1: "text\"" +]) + +TESTWSP([custom delimiters (squeeze)],[wsp-delim wsp-delim00 wsp22], +[delim : -ws trimnl], +[semicolon: separated::list: of :words], +[NF: 5 +0: semicolon +1: " separated" +2: list +3: " of " +4: words +]) + +TESTWSP([custom delimiters (no squeeze)],[wsp-delim wsp-delim01 wsp23], +[delim : -ws -squeeze_delims trimnl], +[semicolon: separated::list: of :words], +[NF: 6 +0: semicolon +1: " separated" +2: "" +3: list +4: " of " +5: words +]) + +TESTWSP([custom, with returned delimiters],[wsp-delim wsp-delim02 wsp24], +[delim : -ws trimnl return_delims], +[semicolon: separated::list: of :words], +[NF: 9 +0: semicolon +1: : +2: " separated" +3: : +4: list +5: : +6: " of " +7: : +8: words +]) + +TESTWSP([custom, with returned & squeezed delimiters], +[wsp-delim wsp-delim03 wsp25], +[delim : -ws trimnl return_delims -squeeze_delims], +[semicolon: separated::list: of :words], +[NF: 10 +0: semicolon +1: : +2: " separated" +3: : +4: : +5: list +6: : +7: " of " +8: : +9: words +]) + +TESTWSP([sed expressions],[wsp-sed wsp-sed00 wsp26],[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 +]) + +TESTWSP([C escapes on],[wcp-c-escape wsp27],[cescapes], +[a\ttab form\ffeed and new\nline], +[NF: 4 +0: a\ttab +1: form\ffeed +2: and +3: new\nline +]) + +TESTWSP([C escapes off],[wcp-c-escape-off wsp28],[-cescapes], +[a\ttab form\ffeed and new\nline], +[NF: 4 +0: attab +1: formffeed +2: and +3: newnline +]) + +TESTWSP([ws elimination],[wsp-ws-elim wsp29],[delim ' ()' ws return_delims], +[( list items )], +[NF: 4 +0: ( +1: list +2: items +3: ) +]) + +TESTWSP([empty quotes],[wsp-empty-quotes wsp30],[delim : ws return_delims], +[t=""], +[NF: 1 +0: t= +]) + +TESTWSP([delimiter following empty quotes], +[wsp31],[delim : ws return_delims], +[t="":r], +[NF: 3 +0: t= +1: : +2: r +]) + +TESTWSP([suppress ws trimming within quotes], +[wsp32], +[default delim , ws return_delims], +[nocomponent,nonewline, formatfield="In message %{text}, "], +[NF: 5 +0: nocomponent +1: , +2: nonewline +3: , +4: "formatfield=In message %{text}, " +]) + +TESTWSP([unescape], +[wsp-unescape wsp33],[-default novar nocmd quote escape '\"'], +[\Seen "quote \"" "bs \\"], +[NF: 3 +0: \\Seen +1: "quote \"" +2: "bs \\" +]) + +TESTWSP([dquote],[wsp34],[-default novar nocmd dquote], +[a "quoted example" isn't it], +[NF: 4 +0: a +1: "quoted example" +2: isn't +3: it +]) + +TESTWSP([squote],[wsp35],[-default novar nocmd squote], +[a 'quoted example' isn"t it], +[NF: 4 +0: a +1: "quoted example" +2: "isn\"t" +3: it +]) + +TESTWSP([incremental],[wsp-incr wsp36],[incremental], +[incremental "input test" line + + +], +[NF: 1 +0: incremental +NF: 1 +0: "input test" +NF: 1 +0: line +], +[input exhausted +]) + +TESTWSP([incremental append],[wsp-incr wsp37],[incremental append], +[incremental "input test" line + + +], +[NF: 1 +0: incremental +NF: 2 +0: incremental +1: "input test" +NF: 3 +0: incremental +1: "input test" +2: line +], +[input exhausted +]) + +TESTWSP([incremental ws], +[wsp-incr wsp38],[return_delims -squeeze_delims incremental ws], +[a list test + + +], +[NF: 1 +0: a +NF: 1 +0: list +NF: 1 +0: test +], +[input exhausted +]) + +m4_popdef([TESTWSP]) diff --git a/tests/wsp.c b/tests/wsp.c new file mode 100644 index 0000000..e06008f --- /dev/null +++ b/tests/wsp.c @@ -0,0 +1,377 @@ +/* grecs - Gray's Extensible Configuration System + Copyright (C) 2014 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. + + Grecs is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Grecs. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#include +#include +#include "wordsplit.h" + +extern char **environ; + +char *progname; + +struct kwd +{ + const char *name; + int tok; +}; + +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 }, + { NULL, 0 } +}; + +struct kwd string_keytab[] = { + { "delim", WRDSF_DELIM }, + { "comment", WRDSF_COMMENT }, + { "escape", WRDSF_ESCAPE }, + { NULL, 0 } +}; + +static int +kwxlat (struct kwd *kwp, const char *str, int *res) +{ + for (; kwp->name; kwp++) + if (strcmp (kwp->name, str) == 0) + { + *res = kwp->tok; + return 0; + } + return -1; +} + +static void +help () +{ + size_t i; + + printf ("usage: %s [options]\n", progname); + printf ("options are:\n"); + printf (" [-]trimnl\n"); + printf (" [-]plaintext\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++) + { + printf (" -%s\n", bool_keytab[i].name); + printf (" %s ARG\n", bool_keytab[i].name); + } + putchar ('\n'); + printf (" -dooffs\n"); + printf (" dooffs COUNT ARGS...\n"); + exit (0); +} + +void +print_qword (const char *word, int plaintext) +{ + static char *qbuf = NULL; + static size_t qlen = 0; + int quote; + size_t size = wordsplit_c_quoted_length (word, 0, "e); + + if (plaintext) + { + printf ("%s", word); + return; + } + + if (*word == 0) + quote = 1; + + if (size >= qlen) + { + qlen = size + 1; + qbuf = realloc (qbuf, qlen); + assert (qbuf != NULL); + } + wordsplit_c_quote_copy (qbuf, word, 0); + qbuf[size] = 0; + if (quote) + printf ("\"%s\"", qbuf); + else + printf ("%s", qbuf); +} + +/* Convert environment to K/V form */ +static char ** +make_env_kv () +{ + size_t i, j, size; + char **newenv; + + /* Count the number of entries */ + for (i = 0; environ[i]; i++) + ; + + size = (i - 1) * 2 + 1; + newenv = calloc (size, sizeof (newenv[0])); + assert (newenv != NULL); + + for (i = j = 0; environ[i]; i++) + { + size_t len = strcspn (environ[i], "="); + char *p = malloc (len+1); + assert (p != NULL); + memcpy (p, environ[i], len); + p[len] = 0; + newenv[j++] = p; + p = strdup (environ[i] + len + 1); + assert (p != NULL); + newenv[j++] = p; + } + newenv[j] = NULL; + return newenv; +} + +int +main (int argc, char **argv) +{ + char buf[1024], *ptr; + int i, offarg = 0; + int trimnl_option = 0; + int plaintext_option = 0; + int wsflags = (WRDSF_DEFFLAGS & ~WRDSF_NOVAR) | + WRDSF_ENOMEMABRT | + WRDSF_ENV | WRDSF_SHOWERR; + struct wordsplit ws; + int next_call = 0; + + progname = argv[0]; + + for (i = 1; i < argc; i++) + { + char *opt = argv[i]; + int negate; + int flag; + + if (opt[0] == '-') + { + negate = 1; + opt++; + } + else if (opt[0] == '+') + { + negate = 0; + opt++; + } + else + negate = 0; + + if (strcmp (opt, "h") == 0 || + strcmp (opt, "help") == 0 || + strcmp (opt, "-help") == 0) + { + help (); + } + + if (strcmp (opt, "trimnl") == 0) + { + trimnl_option = !negate; + continue; + } + + if (strcmp (opt, "plaintext") == 0) + { + plaintext_option = !negate; + continue; + } + + if (kwxlat (bool_keytab, opt, &flag) == 0) + { + if (negate) + wsflags &= ~flag; + else + wsflags |= flag; + continue; + } + + if (kwxlat (string_keytab, opt, &flag) == 0) + { + if (negate) + wsflags &= ~flag; + else + { + i++; + if (i == argc) + { + fprintf (stderr, "%s: missing argument for %s\n", + progname, opt); + exit (1); + } + + switch (flag) + { + case WRDSF_DELIM: + ws.ws_delim = argv[i]; + break; + + case WRDSF_COMMENT: + ws.ws_comment = argv[i]; + break; + + case WRDSF_ESCAPE: + ws.ws_escape = argv[i]; + break; + } + + wsflags |= flag; + } + continue; + } + + if (strcmp (opt, "dooffs") == 0) + { + if (negate) + wsflags &= ~WRDSF_DOOFFS; + else + { + char *p; + + i++; + + if (i == argc) + { + fprintf (stderr, "%s: missing arguments for %s\n", + progname, opt); + exit (1); + } + ws.ws_offs = strtoul (argv[i], &p, 10); + if (*p) + { + fprintf (stderr, "%s: invalid number: %s\n", + progname, argv[i]); + exit (1); + } + + i++; + if (i + ws.ws_offs > argc) + { + fprintf (stderr, "%s: not enough arguments for %s\n", + progname, opt); + exit (1); + } + offarg = i; + i += ws.ws_offs - 1; + wsflags |= WRDSF_DOOFFS; + } + continue; + } + + fprintf (stderr, "%s: unrecognized argument for %s\n", + progname, opt); + exit (1); + } + + if (wsflags & WRDSF_ENV_KV) + ws.ws_env = (const char **) make_env_kv (); + else + ws.ws_env = (const char **) environ; + + if (wsflags & WRDSF_INCREMENTAL) + trimnl_option = 1; + + next_call = 0; + while ((ptr = fgets (buf, sizeof (buf), stdin))) + { + int rc; + size_t i; + + if (trimnl_option) + { + size_t len = strlen (ptr); + if (len && ptr[len-1] == '\n') + ptr[len-1] = 0; + } + + if (wsflags & WRDSF_INCREMENTAL) + { + if (next_call) + { + if (*ptr == 0) + ptr = NULL; + else + free ((void*)ws.ws_input); + } + else + next_call = 1; + if (ptr) + { + ptr = strdup (ptr); + assert (ptr != NULL); + } + } + + rc = wordsplit (ptr, &ws, wsflags); + if (rc) + { + if (!(wsflags & WRDSF_SHOWERR)) + wordsplit_perror (&ws); + continue; + } + + if (offarg) + { + for (i = 0; i < ws.ws_offs; i++) + ws.ws_wordv[i] = argv[offarg + i]; + offarg = 0; + } + + wsflags |= WRDSF_REUSE; + printf ("NF: %lu", (unsigned long) ws.ws_wordc); + if (wsflags & WRDSF_DOOFFS) + printf (" (%lu)", (unsigned long) ws.ws_offs); + putchar ('\n'); + for (i = 0; i < ws.ws_offs; i++) + { + printf ("(%lu): ", (unsigned long) i); + print_qword (ws.ws_wordv[i], plaintext_option); + putchar ('\n'); + } + for (; i < ws.ws_offs + ws.ws_wordc; i++) + { + printf ("%lu: ", (unsigned long) i); + print_qword (ws.ws_wordv[i], plaintext_option); + putchar ('\n'); + } + } + return 0; +} -- cgit v1.2.1 From ade873626bb39688e58db38e2c23aaa53b3845fb Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Tue, 9 Sep 2014 01:01:41 +0300 Subject: wordsplit: ws_getvar allocates memory. * src/wordsplit.c (ISVARBEG,ISVARCHR): New macros. (expvar): ws_getvar allocates memory. * src/wordsplit.h (wordsplit): Remove const from the return value: the function should allocate memory. --- src/wordsplit.c | 28 ++++++++++++++++++---------- src/wordsplit.h | 2 +- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/wordsplit.c b/src/wordsplit.c index 9047369..5df8672 100644 --- a/src/wordsplit.c +++ b/src/wordsplit.c @@ -48,6 +48,9 @@ #define ISALNUM(c) (ISALPHA(c) || ISDIGIT(c)) #define ISPRINT(c) (' ' <= ((unsigned) (c)) && ((unsigned) (c)) <= 127) +#define ISVARBEG(c) (ISALPHA(c) || c == '_') +#define ISVARCHR(c) (ISALNUM(c) || c == '_') + #define ALLOC_INIT 128 #define ALLOC_INCR 128 @@ -704,15 +707,15 @@ expvar (struct wordsplit *wsp, const char *str, size_t len, { size_t i = 0; const char *defstr = NULL; - const char *value; + char *value; const char *vptr; struct wordsplit_node *newnode; const char *start = str - 1; - if (ISALPHA (str[0]) || str[0] == '_') + if (ISVARBEG (str[0])) { for (i = 1; i < len; i++) - if (!(ISALNUM (str[i]) || str[i] == '_')) + if (!ISVARCHR (str[i])) break; *pend = str + i - 1; } @@ -791,7 +794,11 @@ expvar (struct wordsplit *wsp, const char *str, size_t len, if (wsp->ws_flags & WRDSF_KEEPUNDEF) value = NULL; else - value = ""; + { + value = strdup (""); + if (!value) + return _wsplt_nomem (wsp); + } } /* FIXME: handle defstr */ if (value) @@ -803,12 +810,11 @@ expvar (struct wordsplit *wsp, const char *str, size_t len, wsnode_insert (wsp, newnode, *ptail, 0); *ptail = newnode; newnode->flags = _WSNF_WORD | _WSNF_NOEXPAND | flg; - newnode->v.word = strdup (value); - if (!newnode->v.word) - return _wsplt_nomem (wsp); + newnode->v.word = value; } else if (*value == 0) { + free (value); /* Empty string is a special case */ if (wsnode_new (wsp, &newnode)) return 1; @@ -819,11 +825,13 @@ expvar (struct wordsplit *wsp, const char *str, size_t len, else { struct wordsplit ws; - int i; + int i, rc; ws.ws_delim = wsp->ws_delim; - if (wordsplit (value, &ws, - WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_DELIM | WRDSF_WS)) + rc = wordsplit (value, &ws, + WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_DELIM | WRDSF_WS); + free (value); + if (rc) { wordsplit_free (&ws); return 1; diff --git a/src/wordsplit.h b/src/wordsplit.h index 35e125a..4e2b117 100644 --- a/src/wordsplit.h +++ b/src/wordsplit.h @@ -36,7 +36,7 @@ struct wordsplit __attribute__ ((__format__ (__printf__, 1, 2))); const char **ws_env; - const char *(*ws_getvar) (const char *, size_t, void *); + char *(*ws_getvar) (const char *, size_t, void *); void *ws_closure; const char *ws_input; -- cgit v1.2.1 From 9553f7a60019dd0f3350dfbb0146c54010d3b784 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Tue, 9 Sep 2014 01:17:29 +0300 Subject: wordsplit: support ${x:-word}, ${x:+word} and ${x:?word} * src/wordsplit.c (expvar): Support default value. --- src/wordsplit.c | 70 ++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 16 deletions(-) diff --git a/src/wordsplit.c b/src/wordsplit.c index 5df8672..2a64f1f 100644 --- a/src/wordsplit.c +++ b/src/wordsplit.c @@ -780,27 +780,65 @@ expvar (struct wordsplit *wsp, const char *str, size_t len, } else if (wsp->ws_flags & WRDSF_GETVAR) value = wsp->ws_getvar (str, i, wsp->ws_closure); - else if (wsp->ws_flags & WRDSF_UNDEF) - { - wsp->ws_errno = WRDSE_UNDEF; - if (wsp->ws_flags & WRDSF_SHOWERR) - wordsplit_perror (wsp); - return 1; - } - else + + if (!value) { - if (wsp->ws_flags & WRDSF_WARNUNDEF) - wsp->ws_error (_("warning: undefined variable `%.*s'"), (int) i, str); - if (wsp->ws_flags & WRDSF_KEEPUNDEF) - value = NULL; + if (defstr) + { + size_t size; + if (*defstr == '-') + { + size = *pend - ++defstr; + value = malloc (size + 1); + if (!value) + return _wsplt_nomem (wsp); + memcpy (value, defstr, size); + value[size] = 0; + } + else if (*defstr == '?') + { + size = *pend - ++defstr; + if (size == 0) + wsp->ws_error (_("%.*s: variable null or not set"), + (int) i, str); + else + wsp->ws_error ("%.*s: %.*s", + (int) i, str, (int) size, defstr); + } + } + else if (wsp->ws_flags & WRDSF_UNDEF) + { + wsp->ws_errno = WRDSE_UNDEF; + if (wsp->ws_flags & WRDSF_SHOWERR) + wordsplit_perror (wsp); + return 1; + } else { - value = strdup (""); - if (!value) - return _wsplt_nomem (wsp); + if (wsp->ws_flags & WRDSF_WARNUNDEF) + wsp->ws_error (_("warning: undefined variable `%.*s'"), + (int) i, str); + if (wsp->ws_flags & WRDSF_KEEPUNDEF) + value = NULL; + else + { + value = strdup (""); + if (!value) + return _wsplt_nomem (wsp); + } } } - /* FIXME: handle defstr */ + else if (defstr && *defstr == '+') + { + size_t size = *pend - ++defstr; + free (value); + value = malloc (size + 1); + if (!value) + return _wsplt_nomem (wsp); + memcpy (value, defstr, size); + value[size] = 0; + } + if (value) { if (flg & _WSNF_QUOTE) -- cgit v1.2.1 From f90f4f2a4d2a56a3682b7c76fbc7964cf97d74a1 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Tue, 9 Sep 2014 08:52:06 +0300 Subject: wordsplit: Fix expansion of ${...} * src/wordsplit.c (expvar): Ensure value is set. Pass WRDSF_QUOTE to wordsplit (scan_word): Treat ${...} as a single word. * tests/wordsplit.at: Add more tests. * tests/wsp.c: Fix error message. --- src/wordsplit.c | 16 +++++++++---- tests/wordsplit.at | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/wsp.c | 2 +- 3 files changed, 80 insertions(+), 6 deletions(-) diff --git a/src/wordsplit.c b/src/wordsplit.c index 2a64f1f..79c5910 100644 --- a/src/wordsplit.c +++ b/src/wordsplit.c @@ -612,8 +612,7 @@ node_split_prefix (struct wordsplit *wsp, static int find_closing_cbrace (const char *str, size_t i, size_t len, size_t * poff) { - enum - { st_init, st_squote, st_dquote } state = st_init; + enum { st_init, st_squote, st_dquote } state = st_init; size_t level = 1; for (; i < len; i++) @@ -780,7 +779,9 @@ expvar (struct wordsplit *wsp, const char *str, size_t len, } else if (wsp->ws_flags & WRDSF_GETVAR) value = wsp->ws_getvar (str, i, wsp->ws_closure); - + else + value = NULL; + if (!value) { if (defstr) @@ -867,7 +868,8 @@ expvar (struct wordsplit *wsp, const char *str, size_t len, ws.ws_delim = wsp->ws_delim; rc = wordsplit (value, &ws, - WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_DELIM | WRDSF_WS); + WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_DELIM | + WRDSF_WS | WRDSF_QUOTE); free (value); if (rc) { @@ -1193,7 +1195,11 @@ scan_word (struct wordsplit *wsp, size_t start) } } - if (ISDELIM (wsp, command[i])) + if (!(wsp->ws_flags & WRDSF_NOVAR) + && command[i] == '$' && command[i+1] == '{' + && find_closing_cbrace (command, i + 2, len, &i) == 0) + continue; + else if (ISDELIM (wsp, command[i])) break; else i++; diff --git a/tests/wordsplit.at b/tests/wordsplit.at index d74cbe9..d87fa74 100644 --- a/tests/wordsplit.at +++ b/tests/wordsplit.at @@ -438,4 +438,72 @@ NF: 1 [input exhausted ]) +TESTWSP([default value (defined)],[wsp-var wsp-var13 wsp39],[], +[${FOO:-bar}], +[NF: 1 +0: qux +], +[], +[FOO=qux]) + +TESTWSP([default value],[wsp-var wsp-var14 wsp40],[], +[${FOO:-bar}], +[NF: 1 +0: bar +]) + +TESTWSP([default error message (var defined)],[wsp-var wsp-var15 wsp41],[], +[a ${FOO:?} test], +[NF: 3 +0: a +1: bar +2: test +], +[], +[FOO=bar]) + +TESTWSP([default error message],[wsp-var wsp-var16 wsp42],[], +[${FOO:?}], +[NF: 0 +], +[FOO: variable null or not set +]) + +TESTWSP([custom error message (defined)],[wsp-var wsp-var17 wsp43],[], +[a ${FOO:?please define it} test], +[NF: 3 +0: a +1: bar +2: test +], +[], +[FOO=bar]) + +TESTWSP([custom error message],[wsp-var wsp-var18 wsp44],[], +[a ${FOO:?please define it} test], +[NF: 2 +0: a +1: test +], +[FOO: please define it +]) + +TESTWSP([alternate value (defined)],[wsp-var wsp-var19 wsp45],[], +[a ${FOO:+isset} test], +[NF: 3 +0: a +1: isset +2: test +], +[], +[FOO=bar]) + +TESTWSP([alternate value],[wsp-var wsp-var20 wsp46],[], +[a ${FOO:+isset} test], +[NF: 2 +0: a +1: test +]) + + m4_popdef([TESTWSP]) diff --git a/tests/wsp.c b/tests/wsp.c index e06008f..e929de7 100644 --- a/tests/wsp.c +++ b/tests/wsp.c @@ -296,7 +296,7 @@ main (int argc, char **argv) continue; } - fprintf (stderr, "%s: unrecognized argument for %s\n", + fprintf (stderr, "%s: unrecognized argument: %s\n", progname, opt); exit (1); } -- cgit v1.2.1 From a74d130fce4d5fe1907de5eaf51b946ddb93ab6d Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Wed, 10 Sep 2014 17:01:59 +0300 Subject: Makefile.am: Fix rule for gitid.h --- Makefile.am | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Makefile.am b/Makefile.am index 2c1b5d2..0d71dd2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,14 +25,16 @@ BUILT_SOURCES = gitid.h README gitid.h: @if test -e .git; then \ url=`git config --get remote.origin.url | sed 's|.*://||;s|/gitroot/|/|'`; \ - if test "$$url" = "git.gnu.org.ua/grecs.git"; then \ + case $$url in \ + git.gnu.org.ua/grecs.git|*@git.gnu.org.ua/grecs.git) \ dirty=`git diff-index --name-only HEAD 2>/dev/null` || dirty=;\ test -n "$$dirty" && dirty="-dirty"; \ ID=`git log -1 --pretty='format:%H-%ct-%ae'`$$dirty;\ - echo "#define GRECS_GIT_ID \"$$ID\"" > gitid.tmp; \ - cmp gitid.tmp gitid.h >/dev/null 2>&1 || mv gitid.tmp gitid.h; \ - rm -f gitid.tmp; \ - fi; \ + echo "#define GRECS_GIT_ID \"$$ID\"";; \ + *) echo "#define GRECS_GIT_ID \"unknown\"";; \ + esac > gitid.tmp; \ + cmp gitid.tmp gitid.h >/dev/null 2>&1 || mv gitid.tmp gitid.h; \ + rm -f gitid.tmp; \ fi; README: -- cgit v1.2.1 From 742b48487f8170ac13a7fd3273ee2fc8944cf6a5 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Wed, 10 Sep 2014 17:40:47 +0300 Subject: Test ws_getvar/ws_closure functionality. * tests/wsp.c (help): Update. (wsp_getvar): New function. (main): Callect all arguments in form X=Y to an array and set ws_getvar member if this array is not empty. * tests/wordsplit.at: Add tests for ws_getenv function. --- tests/wordsplit.at | 38 ++++++++++++++++++++++++++++++++++---- tests/wsp.c | 43 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 74 insertions(+), 7 deletions(-) diff --git a/tests/wordsplit.at b/tests/wordsplit.at index d87fa74..15a51bc 100644 --- a/tests/wordsplit.at +++ b/tests/wordsplit.at @@ -469,7 +469,7 @@ TESTWSP([default error message],[wsp-var wsp-var16 wsp42],[], [FOO: variable null or not set ]) -TESTWSP([custom error message (defined)],[wsp-var wsp-var17 wsp43],[], +TESTWSP([custom error message (defined)],[wsp-var wsp-var17 wsp-custom-err wsp-custom-err00 wsp43],[], [a ${FOO:?please define it} test], [NF: 3 0: a @@ -479,7 +479,7 @@ TESTWSP([custom error message (defined)],[wsp-var wsp-var17 wsp43],[], [], [FOO=bar]) -TESTWSP([custom error message],[wsp-var wsp-var18 wsp44],[], +TESTWSP([custom error message],[wsp-var wsp-var18 wsp-custom-err wsp-custom-err01 wsp44],[], [a ${FOO:?please define it} test], [NF: 2 0: a @@ -488,7 +488,7 @@ TESTWSP([custom error message],[wsp-var wsp-var18 wsp44],[], [FOO: please define it ]) -TESTWSP([alternate value (defined)],[wsp-var wsp-var19 wsp45],[], +TESTWSP([alternate value (defined)],[wsp-var wsp-var19 wsp-alt wsp-alt00 wsp45],[], [a ${FOO:+isset} test], [NF: 3 0: a @@ -498,12 +498,42 @@ TESTWSP([alternate value (defined)],[wsp-var wsp-var19 wsp45],[], [], [FOO=bar]) -TESTWSP([alternate value],[wsp-var wsp-var20 wsp46],[], +TESTWSP([alternate value],[wsp-var wsp-var20 wsp-alt wsp-alt01 wsp46],[], [a ${FOO:+isset} test], [NF: 2 0: a 1: test ]) +TESTWSP([getvar],[wsp-var wsp-var21 wsp-getvar wsp-getvar00 wsp47], +[foo=bar x=quux], +[begin $foo $x end], +[NF: 4 +0: begin +1: bar +2: quux +3: end +]) + +TESTWSP([getvar and env],[wsp-var wsp-var22 wsp-getvar wsp-getvar01 wsp48], +[foo=bar x=quux], +[begin $foo $TVAR $x end], +[NF: 5 +0: begin +1: bar +2: 12 +3: quux +4: end +], +[], +[TVAR=12]) + +TESTWSP([getvar, alternate value],[wsp-var wsp-var23 wsp-alt wsp-alt02 wsp-getvar02 wsp49], +[foo=bar], +[a ${foo:+isset}], +[NF: 2 +0: a +1: isset +]) m4_popdef([TESTWSP]) diff --git a/tests/wsp.c b/tests/wsp.c index e929de7..3e82e9a 100644 --- a/tests/wsp.c +++ b/tests/wsp.c @@ -81,7 +81,7 @@ help () { size_t i; - printf ("usage: %s [options]\n", progname); + printf ("usage: %s [options] [VAR=VALUE...]\n", progname); printf ("options are:\n"); printf (" [-]trimnl\n"); printf (" [-]plaintext\n"); @@ -161,7 +161,26 @@ make_env_kv () newenv[j] = NULL; return newenv; } - + +static char * +wsp_getvar (const char *vptr, size_t vlen, void *data) +{ + char **base = data; + int i; + + for (i = 0; base[i]; i++) + { + size_t l = strcspn (base[i], "="); + if (l == vlen && memcmp (base[i], vptr, vlen) == 0) + { + char *p = strdup (base[i] + vlen + 1); + assert (p != NULL); + return p; + } + } + return NULL; +} + int main (int argc, char **argv) { @@ -174,7 +193,10 @@ main (int argc, char **argv) WRDSF_ENV | WRDSF_SHOWERR; struct wordsplit ws; int next_call = 0; - + char *fenvbase[128]; + size_t fenvidx = 0; + size_t fenvmax = sizeof (fenvbase) / sizeof (fenvbase[0]); + progname = argv[0]; for (i = 1; i < argc; i++) @@ -296,11 +318,26 @@ main (int argc, char **argv) continue; } + if (strchr(opt, '=')) + { + assert (fenvidx < fenvmax - 1); + fenvbase[fenvidx++] = opt; + continue; + } + fprintf (stderr, "%s: unrecognized argument: %s\n", progname, opt); exit (1); } + if (fenvidx) + { + fenvbase[fenvidx] = NULL; + wsflags |= WRDSF_GETVAR | WRDSF_CLOSURE; + ws.ws_getvar = wsp_getvar; + ws.ws_closure = fenvbase; + } + if (wsflags & WRDSF_ENV_KV) ws.ws_env = (const char **) make_env_kv (); else -- cgit v1.2.1 From 0cd404dc1c2023ec0b4d4d7056a2f1c2c4902de6 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Wed, 10 Sep 2014 18:19:29 +0300 Subject: Bugfix --- tests/wordsplit.at | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/wordsplit.at b/tests/wordsplit.at index 15a51bc..dbebcae 100644 --- a/tests/wordsplit.at +++ b/tests/wordsplit.at @@ -503,7 +503,9 @@ TESTWSP([alternate value],[wsp-var wsp-var20 wsp-alt wsp-alt01 wsp46],[], [NF: 2 0: a 1: test -]) +], +[], +[unset FOO;]) TESTWSP([getvar],[wsp-var wsp-var21 wsp-getvar wsp-getvar00 wsp47], [foo=bar x=quux], -- cgit v1.2.1 From fa06607d14f75aae80eec72511a1aba6a9097f33 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Wed, 10 Sep 2014 18:30:29 +0300 Subject: Minor change * tests/wsp.c (main): use additional variable to keep the allocated value in incremental mode. --- tests/wsp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/wsp.c b/tests/wsp.c index 3e82e9a..de90c54 100644 --- a/tests/wsp.c +++ b/tests/wsp.c @@ -184,7 +184,7 @@ wsp_getvar (const char *vptr, size_t vlen, void *data) int main (int argc, char **argv) { - char buf[1024], *ptr; + char buf[1024], *ptr, *saved_ptr; int i, offarg = 0; int trimnl_option = 0; int plaintext_option = 0; @@ -366,13 +366,13 @@ main (int argc, char **argv) if (*ptr == 0) ptr = NULL; else - free ((void*)ws.ws_input); + free (saved_ptr); } else next_call = 1; if (ptr) { - ptr = strdup (ptr); + ptr = saved_ptr = strdup (ptr); assert (ptr != NULL); } } -- cgit v1.2.1 From 172e5b17e8f8284e30fd3885f7e56deb539591d6 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Thu, 25 Sep 2014 08:53:20 +0300 Subject: Update copyright dates. --- src/wordsplit.c | 2 +- src/wordsplit.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wordsplit.c b/src/wordsplit.c index 79c5910..1fe5d8c 100644 --- a/src/wordsplit.c +++ b/src/wordsplit.c @@ -1,5 +1,5 @@ /* wordsplit - a word splitter - Copyright (C) 2009-2012 Sergey Poznyakoff + Copyright (C) 2009-2014 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 diff --git a/src/wordsplit.h b/src/wordsplit.h index 4e2b117..682c57e 100644 --- a/src/wordsplit.h +++ b/src/wordsplit.h @@ -1,5 +1,5 @@ /* wordsplit - a word splitter - Copyright (C) 2009-2012 Sergey Poznyakoff + Copyright (C) 2009-2014 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 -- cgit v1.2.1 From a52fcb463f6d1bbd2e883fdb0f7cea3cbe8c11f6 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Thu, 23 Oct 2014 08:48:45 +0300 Subject: wordsplit: implement command expansion * src/wordsplit.c (wordsplit_init): Change handling of lacking WRDSF_NOCMD. (find_closing_cbrace): Rename to find_closing_paren, take additional argument. All uses changed. (node_expand_vars): Rewrite as a generalized function node_expand. (wordsplit_varexp): Use node_expand. (expcmd, wordsplit_cmdexp): New functions. (scan_word): Handle $(command) notation (wordsplit_process_list): Command expansion. * src/wordsplit.h (wordsplit) : New member. * tests/wsp.c (wsp_runcmd): New function. (main): Set ws_command unless WRDSF_NOCMD flag is set. --- src/wordsplit.c | 208 ++++++++++++++++++++++++++++++++++++++++++++++++-------- src/wordsplit.h | 2 + tests/wsp.c | 64 +++++++++++++++++ 3 files changed, 245 insertions(+), 29 deletions(-) diff --git a/src/wordsplit.c b/src/wordsplit.c index 1fe5d8c..71b5dbf 100644 --- a/src/wordsplit.c +++ b/src/wordsplit.c @@ -131,11 +131,14 @@ wordsplit_init (struct wordsplit *wsp, const char *input, size_t len, if (!(wsp->ws_flags & WRDSF_NOCMD)) { - errno = EINVAL; - wsp->ws_errno = WRDSE_NOSUPP; - if (wsp->ws_flags & WRDSF_SHOWERR) - wordsplit_perror (wsp); - return wsp->ws_errno; + if (!wsp->ws_command) + { + errno = EINVAL; + wsp->ws_errno = WRDSE_NOSUPP; + if (wsp->ws_flags & WRDSF_SHOWERR) + wordsplit_perror (wsp); + return wsp->ws_errno; + } } if (wsp->ws_flags & WRDSF_SHOWDBG) @@ -610,7 +613,8 @@ node_split_prefix (struct wordsplit *wsp, } static int -find_closing_cbrace (const char *str, size_t i, size_t len, size_t * poff) +find_closing_paren (const char *str, size_t i, size_t len, size_t *poff, + char *paren) { enum { st_init, st_squote, st_dquote } state = st_init; size_t level = 1; @@ -622,18 +626,23 @@ find_closing_cbrace (const char *str, size_t i, size_t len, size_t * poff) case st_init: switch (str[i]) { - case '{': - level++; - break; - - case '}': - if (--level == 0) + default: + if (str[i] == paren[0]) + { + level++; + break; + } + else if (str[i] == paren[1]) { - *poff = i; - return 0; + if (--level == 0) + { + *poff = i; + return 0; + } + break; } break; - + case '"': state = st_dquote; break; @@ -730,7 +739,7 @@ expvar (struct wordsplit *wsp, const char *str, size_t len, size_t j; defstr = str + i + 1; - if (find_closing_cbrace (str, i + 1, len, &j)) + if (find_closing_paren (str, i + 1, len, &j, "{}")) { wsp->ws_errno = WRDSE_CBRACE; return 1; @@ -919,7 +928,19 @@ expvar (struct wordsplit *wsp, const char *str, size_t len, } static int -node_expand_vars (struct wordsplit *wsp, struct wordsplit_node *node) +begin_var_p (int c) +{ + return c == '{' || ISVARBEG (c); +} + +static int +node_expand (struct wordsplit *wsp, struct wordsplit_node *node, + int (*beg_p) (int), + int (*ws_exp_fn) (struct wordsplit *wsp, + const char *str, size_t len, + struct wordsplit_node **ptail, + const char **pend, + int flg)) { const char *str = wsnode_ptr (wsp, node); size_t slen = wsnode_len (node); @@ -935,7 +956,7 @@ node_expand_vars (struct wordsplit *wsp, struct wordsplit_node *node) p++; continue; } - if (*p == '$') + if (*p == '$' && beg_p (p[1])) { size_t n = p - str; @@ -944,8 +965,8 @@ node_expand_vars (struct wordsplit *wsp, struct wordsplit_node *node) if (node_split_prefix (wsp, &tail, node, off, n, _WSNF_JOIN)) return 1; p++; - if (expvar (wsp, p, slen - n, &tail, &p, - node->flags & (_WSNF_JOIN | _WSNF_QUOTE))) + if (ws_exp_fn (wsp, p, slen - n, &tail, &p, + node->flags & (_WSNF_JOIN | _WSNF_QUOTE))) return 1; off += p - str + 1; str = p + 1; @@ -956,7 +977,7 @@ node_expand_vars (struct wordsplit *wsp, struct wordsplit_node *node) if (tail != node) tail->flags |= _WSNF_JOIN; if (node_split_prefix (wsp, &tail, node, off, p - str, - node->flags & _WSNF_JOIN)) + node->flags & (_WSNF_JOIN|_WSNF_QUOTE))) return 1; } if (tail != node) @@ -966,7 +987,7 @@ node_expand_vars (struct wordsplit *wsp, struct wordsplit_node *node) } return 0; } - + /* Remove NULL lists */ static void wsnode_nullelim (struct wordsplit *wsp) @@ -994,7 +1015,114 @@ wordsplit_varexp (struct wordsplit *wsp) { struct wordsplit_node *next = p->next; if (!(p->flags & _WSNF_NOEXPAND)) - if (node_expand_vars (wsp, p)) + if (node_expand (wsp, p, begin_var_p, expvar)) + return 1; + p = next; + } + + wsnode_nullelim (wsp); + return 0; +} + +static int +begin_cmd_p (int c) +{ + return c == '('; +} + +static int +expcmd (struct wordsplit *wsp, const char *str, size_t len, + struct wordsplit_node **ptail, const char **pend, int flg) +{ + size_t j; + char *value; + struct wordsplit_node *newnode; + + str++; + len--; + + if (find_closing_paren (str, 0, len, &j, "()")) + { + wsp->ws_errno = WRDSE_CBRACE; + return 1; + } + + *pend = str + j; + value = wsp->ws_command (str, j, wsp); + + if (value) + { + if (flg & _WSNF_QUOTE) + { + 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; + } + else if (*value == 0) + { + free (value); + /* Empty string is a special case */ + if (wsnode_new (wsp, &newnode)) + return 1; + wsnode_insert (wsp, newnode, *ptail, 0); + *ptail = newnode; + newnode->flags = _WSNF_NULL; + } + else + { + struct wordsplit ws; + int i, rc; + + ws.ws_delim = wsp->ws_delim; + rc = wordsplit (value, &ws, + WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_DELIM | + WRDSF_WS | WRDSF_QUOTE); + free (value); + if (rc) + { + wordsplit_free (&ws); + return 1; + } + for (i = 0; i < ws.ws_wordc; i++) + { + if (wsnode_new (wsp, &newnode)) + return 1; + wsnode_insert (wsp, newnode, *ptail, 0); + *ptail = newnode; + newnode->flags = _WSNF_WORD | + _WSNF_NOEXPAND | + (i + 1 < ws.ws_wordc ? (flg & ~_WSNF_JOIN) : flg); + newnode->v.word = strdup (ws.ws_wordv[i]); + if (!newnode->v.word) + return _wsplt_nomem (wsp); + } + wordsplit_free (&ws); + } + } + else + { + if (wsnode_new (wsp, &newnode)) + return 1; + wsnode_insert (wsp, newnode, *ptail, 0); + *ptail = newnode; + newnode->flags = _WSNF_NULL; + } + return 0; +} + +static int +wordsplit_cmdexp (struct wordsplit *wsp) +{ + struct wordsplit_node *p; + + for (p = wsp->ws_head; p;) + { + struct wordsplit_node *next = p->next; + if (!(p->flags & _WSNF_NOEXPAND)) + if (node_expand (wsp, p, begin_cmd_p, expcmd)) return 1; p = next; } @@ -1195,11 +1323,19 @@ scan_word (struct wordsplit *wsp, size_t start) } } - if (!(wsp->ws_flags & WRDSF_NOVAR) - && command[i] == '$' && command[i+1] == '{' - && find_closing_cbrace (command, i + 2, len, &i) == 0) - continue; - else if (ISDELIM (wsp, command[i])) + if (command[i] == '$') + { + if (!(wsp->ws_flags & WRDSF_NOVAR) + && command[i+1] == '{' + && find_closing_paren (command, i + 2, len, &i, "{}") == 0) + continue; + if (!(wsp->ws_flags & WRDSF_NOCMD) + && command[i+1] == '(' + && find_closing_paren (command, i + 2, len, &i, "()") == 0) + continue; + } + + if (ISDELIM (wsp, command[i])) break; else i++; @@ -1482,7 +1618,21 @@ wordsplit_process_list (struct wordsplit *wsp, size_t start) } if (wsp->ws_flags & WRDSF_SHOWDBG) { - wsp->ws_debug ("Expanded list:"); + wsp->ws_debug ("After variable expansion:"); + wordsplit_dump_nodes (wsp); + } + } + + if (!(wsp->ws_flags & WRDSF_NOCMD)) + { + if (wordsplit_cmdexp (wsp)) + { + wordsplit_free_nodes (wsp); + return wsp->ws_errno; + } + if (wsp->ws_flags & WRDSF_SHOWDBG) + { + wsp->ws_debug ("After command expansion:"); wordsplit_dump_nodes (wsp); } } diff --git a/src/wordsplit.h b/src/wordsplit.h index 682c57e..96088f9 100644 --- a/src/wordsplit.h +++ b/src/wordsplit.h @@ -39,6 +39,8 @@ struct wordsplit char *(*ws_getvar) (const char *, size_t, void *); void *ws_closure; + char *(*ws_command) (const char *, size_t, struct wordsplit const *); + const char *ws_input; size_t ws_len; size_t ws_endp; diff --git a/tests/wsp.c b/tests/wsp.c index de90c54..6fb61da 100644 --- a/tests/wsp.c +++ b/tests/wsp.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "wordsplit.h" extern char **environ; @@ -181,6 +182,66 @@ wsp_getvar (const char *vptr, size_t vlen, void *data) return NULL; } +static char * +wsp_runcmd (const char *str, size_t len, struct wordsplit const *parent) +{ + FILE *fp; + char *cmd; + int c, lastc; + char *buffer = NULL; + size_t bufsize = 0; + size_t buflen = 0; + + cmd = malloc (len + 1); + if (!cmd) + { + parent->ws_error ("memory exhausted"); + abort (); + } + memcpy (cmd, str, len); + cmd[len] = 0; + + fp = popen(cmd, "r"); + if (!fp) + { + parent->ws_error ("can't run %s: %s", cmd, strerror (errno)); + return NULL; + } + + while ((c = fgetc (fp)) != EOF) + { + lastc = c; + if (c == '\n') + c = ' '; + if (buflen == bufsize) + { + if (bufsize == 0) + bufsize = 80; + else + bufsize *= 2; + buffer = realloc (buffer, bufsize); + if (!buffer) + { + parent->ws_error ("can't run %s: %s", cmd, strerror (errno)); + return NULL; + } + } + buffer[buflen++] = c; + } + + if (buffer) + { + if (lastc == '\n') + --buflen; + buffer[buflen] = 0; + } + + close (fp); + free (cmd); + + return buffer; +} + int main (int argc, char **argv) { @@ -343,6 +404,9 @@ main (int argc, char **argv) else ws.ws_env = (const char **) environ; + if (!(wsflags & WRDSF_NOCMD)) + ws.ws_command = wsp_runcmd; + if (wsflags & WRDSF_INCREMENTAL) trimnl_option = 1; -- cgit v1.2.1 From da0d91390850412d9bd9987f3c056d54e6627f92 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Thu, 23 Oct 2014 12:12:02 +0300 Subject: Change prototypes of ws_getvar and ws_command. New invocation sequence ensures proper error handling. This is an incompatible change. Authors using ws_getvar member will have to rewrite their ws_getvar function accordingly. * src/wordsplit.c (wordsplit_init0): Call wordsplit_clearerr on reuse. (wordsplit_init): Fix ws_errno (expvar): Change invocation of ws_getvar. (expcmd): Change invocation of ws_command. (wordsplit_clearerr): New function. (wordsplit_strerror): Handle WRDSE_USERERR. * src/wordsplit.h (ws_getvar): Change return value and signature of ws_getvar and ws_command. New member 'ws_usererr'. (WRDSF_ARGV): New flag. (WRDSE_OK): New define. Same as WRDSE_EOF. (WRDSE_USERERR): New error code. (wordsplit_clearerr): New proto. * tests/wsp.c (wsp_getvar, wsp_runcmd): Rewrite. --- src/wordsplit.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- src/wordsplit.h | 14 +++++++++--- tests/wsp.c | 50 +++++++++++++++++++++++++----------------- 3 files changed, 104 insertions(+), 27 deletions(-) diff --git a/src/wordsplit.c b/src/wordsplit.c index 71b5dbf..a7feb82 100644 --- a/src/wordsplit.c +++ b/src/wordsplit.c @@ -96,6 +96,7 @@ wordsplit_init0 (struct wordsplit *wsp) { if (!(wsp->ws_flags & WRDSF_APPEND)) wordsplit_free_words (wsp); + wordsplit_clearerr (wsp); } else { @@ -134,7 +135,7 @@ wordsplit_init (struct wordsplit *wsp, const char *input, size_t len, if (!wsp->ws_command) { errno = EINVAL; - wsp->ws_errno = WRDSE_NOSUPP; + wsp->ws_errno = WRDSE_USAGE; if (wsp->ws_flags & WRDSF_SHOWERR) wordsplit_perror (wsp); return wsp->ws_errno; @@ -787,7 +788,30 @@ expvar (struct wordsplit *wsp, const char *str, size_t len, return _wsplt_nomem (wsp); } else if (wsp->ws_flags & WRDSF_GETVAR) - value = wsp->ws_getvar (str, i, wsp->ws_closure); + { + int rc = wsp->ws_getvar (&value, str, i, wsp->ws_closure); + switch (rc) + { + case WRDSE_OK: + break; + + case WRDSE_NOSPACE: + return _wsplt_nomem (wsp); + + case WRDSE_UNDEF: + value = NULL; + break; + + case WRDSE_USERERR: + wsp->ws_usererr = value; + /* fall through */ + default: + wsp->ws_errno = rc; + if (wsp->ws_flags & WRDSF_SHOWERR) + wordsplit_perror (wsp); + return 1; + } + } else value = NULL; @@ -819,7 +843,7 @@ expvar (struct wordsplit *wsp, const char *str, size_t len, else if (wsp->ws_flags & WRDSF_UNDEF) { wsp->ws_errno = WRDSE_UNDEF; - if (wsp->ws_flags & WRDSF_SHOWERR) + if (wsp->ws_flags & WRDSF_SHOWERR) wordsplit_perror (wsp); return 1; } @@ -1034,6 +1058,7 @@ static int expcmd (struct wordsplit *wsp, const char *str, size_t len, struct wordsplit_node **ptail, const char **pend, int flg) { + int rc; size_t j; char *value; struct wordsplit_node *newnode; @@ -1048,7 +1073,30 @@ expcmd (struct wordsplit *wsp, const char *str, size_t len, } *pend = str + j; - value = wsp->ws_command (str, j, wsp); + if (wsp->ws_flags & WRDSF_ARGV) + { + struct wordsplit ws; + + ws.ws_delim = wsp->ws_delim; + rc = wordsplit_len (str, j, &ws, + WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_DELIM | + (wsp->ws_flags & (WRDSF_WS | WRDSF_QUOTE))); + rc = wsp->ws_command (&value, str, j, ws.ws_wordv, wsp->ws_closure); + } + else + rc = wsp->ws_command (&value, str, j, NULL, wsp->ws_closure); + + if (rc == WRDSE_NOSPACE) + return _wsplt_nomem (wsp); + else if (rc) + { + if (rc == WRDSE_USERERR) + wsp->ws_usererr = value; + wsp->ws_errno = rc; + if (wsp->ws_flags & WRDSF_SHOWERR) + wordsplit_perror (wsp); + return 1; + } if (value) { @@ -1752,6 +1800,15 @@ wordsplit_free_words (struct wordsplit *ws) ws->ws_wordc = 0; } +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; +} + void wordsplit_free (struct wordsplit *ws) { @@ -1820,6 +1877,8 @@ int _wordsplit_nerrs = const char * wordsplit_strerror (struct wordsplit *ws) { + if (ws->ws_errno == WRDSE_USERERR) + return ws->ws_usererr; if (ws->ws_errno < _wordsplit_nerrs) return _wordsplit_errstr[ws->ws_errno]; return N_("unknown error"); diff --git a/src/wordsplit.h b/src/wordsplit.h index 96088f9..4a0cd88 100644 --- a/src/wordsplit.h +++ b/src/wordsplit.h @@ -36,19 +36,20 @@ struct wordsplit __attribute__ ((__format__ (__printf__, 1, 2))); const char **ws_env; - char *(*ws_getvar) (const char *, size_t, void *); + int (*ws_getvar) (char **, const char *, size_t, void *); void *ws_closure; - char *(*ws_command) (const char *, size_t, struct wordsplit const *); + int (*ws_command) (char **, const char *, size_t, char **, void *); const char *ws_input; size_t ws_len; size_t ws_endp; int ws_errno; + char *ws_usererr; struct wordsplit_node *ws_head, *ws_tail; }; -/* Wordsplit flags. Only 2 bits of a 32-bit word remain unused. +/* Wordsplit flags. Only 1 bit of a 32-bit word remains unused. It is getting crowded... */ /* Append the words found to the array resulting from a previous call. */ @@ -125,10 +126,14 @@ struct wordsplit /* Incremental mode */ #define WRDSF_INCREMENTAL 0x20000000 +/* ws_command needs argv parameter */ +#define WRDSF_ARGV 0x40000000 + #define WRDSF_DEFFLAGS \ (WRDSF_NOVAR | WRDSF_NOCMD | \ WRDSF_QUOTE | WRDSF_SQUEEZE_DELIMS | WRDSF_CESCAPES) +#define WRDSE_OK 0 #define WRDSE_EOF 0 #define WRDSE_QUOTE 1 #define WRDSE_NOSPACE 2 @@ -137,6 +142,7 @@ struct wordsplit #define WRDSE_CBRACE 5 #define WRDSE_UNDEF 6 #define WRDSE_NOINPUT 7 +#define WRDSE_USERERR 8 int wordsplit (const char *s, struct wordsplit *p, int flags); int wordsplit_len (const char *s, size_t len, @@ -157,5 +163,7 @@ void wordsplit_c_quote_copy (char *dst, const char *src, int quote_hex); void wordsplit_perror (struct wordsplit *ws); const char *wordsplit_strerror (struct wordsplit *ws); +void wordsplit_clearerr (struct wordsplit *ws); + #endif diff --git a/tests/wsp.c b/tests/wsp.c index 6fb61da..f01f6b6 100644 --- a/tests/wsp.c +++ b/tests/wsp.c @@ -22,6 +22,7 @@ #include #include #include +#include "grecs.h" #include "wordsplit.h" extern char **environ; @@ -163,8 +164,8 @@ make_env_kv () return newenv; } -static char * -wsp_getvar (const char *vptr, size_t vlen, void *data) +static int +wsp_getvar (char **ret, const char *vptr, size_t vlen, void *data) { char **base = data; int i; @@ -175,15 +176,17 @@ wsp_getvar (const char *vptr, size_t vlen, void *data) if (l == vlen && memcmp (base[i], vptr, vlen) == 0) { char *p = strdup (base[i] + vlen + 1); - assert (p != NULL); - return p; + if (p == NULL) + return WRDSE_NOSPACE; + *ret = p; + return WRDSE_OK; } } - return NULL; + return WRDSE_UNDEF; } -static char * -wsp_runcmd (const char *str, size_t len, struct wordsplit const *parent) +static int +wsp_runcmd (char **ret, const char *str, size_t len, char **argv, void *closure) { FILE *fp; char *cmd; @@ -194,18 +197,20 @@ wsp_runcmd (const char *str, size_t len, struct wordsplit const *parent) cmd = malloc (len + 1); if (!cmd) - { - parent->ws_error ("memory exhausted"); - abort (); - } + return WRDSE_NOSPACE; memcpy (cmd, str, len); cmd[len] = 0; fp = popen(cmd, "r"); if (!fp) { - parent->ws_error ("can't run %s: %s", cmd, strerror (errno)); - return NULL; + size_t size = 0; + ret = NULL; + if (grecs_asprintf (ret, &size, "can't run %s: %s", + cmd, strerror (errno))) + return WRDSE_NOSPACE; + else + return WRDSE_USERERR; } while ((c = fgetc (fp)) != EOF) @@ -215,16 +220,20 @@ wsp_runcmd (const char *str, size_t len, struct wordsplit const *parent) c = ' '; if (buflen == bufsize) { + char *p; + if (bufsize == 0) bufsize = 80; else bufsize *= 2; - buffer = realloc (buffer, bufsize); - if (!buffer) + p = realloc (buffer, bufsize); + if (!p) { - parent->ws_error ("can't run %s: %s", cmd, strerror (errno)); - return NULL; + free (buffer); + free (cmd); + return WRDSE_NOSPACE; } + buffer = p; } buffer[buflen++] = c; } @@ -236,10 +245,11 @@ wsp_runcmd (const char *str, size_t len, struct wordsplit const *parent) buffer[buflen] = 0; } - close (fp); + pclose (fp); free (cmd); - return buffer; + *ret = buffer; + return WRDSE_OK; } int @@ -379,7 +389,7 @@ main (int argc, char **argv) continue; } - if (strchr(opt, '=')) + if (strchr (opt, '=')) { assert (fenvidx < fenvmax - 1); fenvbase[fenvidx++] = opt; -- cgit v1.2.1 From 480aed2bd583eb0c357756fdce60a44ba089747c Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Thu, 23 Oct 2014 15:35:40 +0300 Subject: wordsplit: fix variable expansion within commands * src/wordsplit.c (_wsplt_seterr): New static function. Use instead of assigning value to ws_errno. (coalesce_segment): Propagate _WSNF_QUOTE flag to the resulting node. (wordsplit_process_list): Coalesce the list before running command expansion. * tests/testsuite.at: Test wordsplit first. If it fails, most other tests will fail too. * tests/wordsplit.at: Test command expansion. --- src/wordsplit.c | 62 +++++++++++++++---------------- tests/testsuite.at | 4 +- tests/wordsplit.at | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 35 deletions(-) diff --git a/src/wordsplit.c b/src/wordsplit.c index a7feb82..72a4e40 100644 --- a/src/wordsplit.c +++ b/src/wordsplit.c @@ -74,6 +74,15 @@ _wsplt_error (const char *fmt, ...) static void wordsplit_free_nodes (struct wordsplit *); +static int +_wsplt_seterr (struct wordsplit *wsp, int ec) +{ + wsp->ws_errno = ec; + if (wsp->ws_flags & WRDSF_SHOWERR) + wordsplit_perror (wsp); + return ec; +} + static int _wsplt_nomem (struct wordsplit *wsp) { @@ -123,10 +132,8 @@ wordsplit_init (struct wordsplit *wsp, const char *input, size_t len, if (!(wsp->ws_flags & WRDSF_NOVAR) && !(wsp->ws_flags & (WRDSF_ENV | WRDSF_GETVAR))) { + _wsplt_seterr (wsp, WRDSE_USAGE); errno = EINVAL; - wsp->ws_errno = WRDSE_USAGE; - if (wsp->ws_flags & WRDSF_SHOWERR) - wordsplit_perror (wsp); return wsp->ws_errno; } @@ -134,10 +141,8 @@ wordsplit_init (struct wordsplit *wsp, const char *input, size_t len, { if (!wsp->ws_command) { + _wsplt_seterr (wsp, WRDSE_USAGE); errno = EINVAL; - wsp->ws_errno = WRDSE_USAGE; - if (wsp->ws_flags & WRDSF_SHOWERR) - wordsplit_perror (wsp); return wsp->ws_errno; } } @@ -464,6 +469,7 @@ coalesce_segment (struct wordsplit *wsp, struct wordsplit_node *node) cur += slen; if (p != node) { + node->flags |= p->flags & _WSNF_QUOTE; wsnode_remove (wsp, p); stop = p == end; wsnode_free (p); @@ -741,10 +747,7 @@ expvar (struct wordsplit *wsp, const char *str, size_t len, defstr = str + i + 1; if (find_closing_paren (str, i + 1, len, &j, "{}")) - { - wsp->ws_errno = WRDSE_CBRACE; - return 1; - } + return _wsplt_seterr (wsp, WRDSE_CBRACE); *pend = str + j; } else if (str[i] == '}') @@ -753,10 +756,7 @@ expvar (struct wordsplit *wsp, const char *str, size_t len, *pend = str + i; } else - { - wsp->ws_errno = WRDSE_CBRACE; - return 1; - } + return _wsplt_seterr (wsp, WRDSE_CBRACE); } else { @@ -806,9 +806,7 @@ expvar (struct wordsplit *wsp, const char *str, size_t len, wsp->ws_usererr = value; /* fall through */ default: - wsp->ws_errno = rc; - if (wsp->ws_flags & WRDSF_SHOWERR) - wordsplit_perror (wsp); + _wsplt_seterr (wsp, rc); return 1; } } @@ -842,9 +840,7 @@ expvar (struct wordsplit *wsp, const char *str, size_t len, } else if (wsp->ws_flags & WRDSF_UNDEF) { - wsp->ws_errno = WRDSE_UNDEF; - if (wsp->ws_flags & WRDSF_SHOWERR) - wordsplit_perror (wsp); + _wsplt_seterr (wsp, WRDSE_UNDEF); return 1; } else @@ -1068,7 +1064,7 @@ expcmd (struct wordsplit *wsp, const char *str, size_t len, if (find_closing_paren (str, 0, len, &j, "()")) { - wsp->ws_errno = WRDSE_CBRACE; + _wsplt_seterr (wsp, WRDSE_CBRACE); return 1; } @@ -1092,9 +1088,7 @@ expcmd (struct wordsplit *wsp, const char *str, size_t len, { if (rc == WRDSE_USERERR) wsp->ws_usererr = value; - wsp->ws_errno = rc; - if (wsp->ws_flags & WRDSF_SHOWERR) - wordsplit_perror (wsp); + _wsplt_seterr (wsp, rc); return 1; } @@ -1299,9 +1293,7 @@ scan_qstring (struct wordsplit *wsp, size_t start, size_t * end) else { wsp->ws_endp = start; - wsp->ws_errno = WRDSE_QUOTE; - if (wsp->ws_flags & WRDSF_SHOWERR) - wordsplit_perror (wsp); + _wsplt_seterr (wsp, WRDSE_QUOTE); return _WRDS_ERR; } return 0; @@ -1673,6 +1665,15 @@ wordsplit_process_list (struct wordsplit *wsp, size_t start) if (!(wsp->ws_flags & WRDSF_NOCMD)) { + if (wsnode_coalesce (wsp)) + return wsp->ws_errno; + + if (wsp->ws_flags & WRDSF_SHOWDBG) + { + wsp->ws_debug ("Coalesced list:"); + wordsplit_dump_nodes (wsp); + } + if (wordsplit_cmdexp (wsp)) { wordsplit_free_nodes (wsp); @@ -1724,12 +1725,7 @@ wordsplit_len (const char *command, size_t length, struct wordsplit *wsp, start = skip_delim (wsp); if (wsp->ws_endp == wsp->ws_len) - { - wsp->ws_errno = WRDSE_NOINPUT; - if (wsp->ws_flags & WRDSF_SHOWERR) - wordsplit_perror (wsp); - return wsp->ws_errno; - } + return _wsplt_seterr (wsp, WRDSE_NOINPUT); cmdptr = wsp->ws_input + wsp->ws_endp; cmdlen = wsp->ws_len - wsp->ws_endp; diff --git a/tests/testsuite.at b/tests/testsuite.at index ff36835..aa8b2eb 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -47,6 +47,8 @@ dnl # Begin tests AT_INIT m4_if(GRECS_DISTCK_AT,,,[m4_include(GRECS_DISTCK_AT)]) +m4_include([wordsplit.at]) + AT_BANNER([Formats]) m4_include([format00.at]) m4_include([format01.at]) @@ -112,6 +114,4 @@ m4_include([locus01.at]) m4_include([locus02.at]) m4_include([path-locus.at]) -m4_include([wordsplit.at]) - # End of testsuite.at diff --git a/tests/wordsplit.at b/tests/wordsplit.at index dbebcae..2cc9e4a 100644 --- a/tests/wordsplit.at +++ b/tests/wordsplit.at @@ -538,4 +538,111 @@ TESTWSP([getvar, alternate value],[wsp-var wsp-var23 wsp-alt wsp-alt02 wsp-getva 1: isset ]) +dnl Something that doesn't fit into TESTWSP + +AT_SETUP([simple command expansion]) +AT_KEYWORDS([wordsplit wsp wsp-cmd wsp-cmd-1 wsp50]) +AT_CHECK([ +mkdir dir +> dir/file + +wsp -nocmd <<'EOT' +begin $(find dir) end +EOT +], +[0], +[NF: 4 +0: begin +1: dir +2: dir/file +3: end +]) +AT_CLEANUP + +AT_SETUP([quoted command expansion]) +AT_KEYWORDS([wordsplit wsp wsp-cmd wsp-cmd-2 wsp51]) +AT_CHECK([ +mkdir dir +> dir/file + +wsp -nocmd <<'EOT' +begin "$(find dir)" end +EOT +], +[0], +[NF: 3 +0: begin +1: "dir dir/file" +2: end +]) +AT_CLEANUP + +AT_SETUP([coalesced command expansion]) +AT_KEYWORDS([wordsplit wsp wsp-cmd wsp-cmd-3 wsp52]) +AT_CHECK([ +mkdir dir +> dir/file + +wsp -nocmd <<'EOT' +begin($(find dir))end +EOT +], +[0], +[NF: 2 +0: begin(dir +1: dir/file)end +]) +AT_CLEANUP + +AT_SETUP([quoted coalesced command expansion]) +AT_KEYWORDS([wordsplit wsp wsp-cmd wsp-cmd-4 wsp53]) +AT_CHECK([ +mkdir dir +> dir/file + +wsp -nocmd <<'EOT' +"begin($(find dir))end" +EOT +], +[0], +[NF: 1 +0: "begin(dir dir/file)end" +]) +AT_CLEANUP + +AT_SETUP([variable and command expansion]) +AT_KEYWORDS([wordsplit wsp wsp-var wsp-var24 wsp-cmd wsp-cmd-5 wsp54]) +AT_CHECK([ +mkdir dir +> dir/file + +DIR=dir wsp -nocmd -novar<<'EOT' +begin $(find $DIR) end +EOT +], +[0], +[NF: 4 +0: begin +1: dir +2: dir/file +3: end +]) +AT_CLEANUP + +AT_SETUP([variable and command expansion in quotes]) +AT_KEYWORDS([wordsplit wsp wsp-var wsp-var25 wsp-cmd wsp-cmd-6 wsp55]) +AT_CHECK([ +mkdir dir +> dir/file + +DIR=dir wsp -nocmd -novar<<'EOT' +"begin($(find $DIR))end" +EOT +], +[0], +[NF: 1 +0: "begin(dir dir/file)end" +]) +AT_CLEANUP + m4_popdef([TESTWSP]) -- cgit v1.2.1 From a606dfb96f75bd37266145be8f21cddba9f1b5e0 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Thu, 23 Oct 2014 17:29:01 +0300 Subject: cleanup in wordsplit --- src/wordsplit.c | 83 +++++++++++++++++++++------------------------------------ src/wordsplit.h | 31 +++++++++------------ 2 files changed, 43 insertions(+), 71 deletions(-) diff --git a/src/wordsplit.c b/src/wordsplit.c index 72a4e40..0592859 100644 --- a/src/wordsplit.c +++ b/src/wordsplit.c @@ -803,6 +803,8 @@ expvar (struct wordsplit *wsp, const char *str, size_t len, break; case WRDSE_USERERR: + if (wsp->ws_errno == WRDSE_USERERR) + free (wsp->ws_usererr); wsp->ws_usererr = value; /* fall through */ default: @@ -1008,7 +1010,7 @@ node_expand (struct wordsplit *wsp, struct wordsplit_node *node, return 0; } -/* Remove NULL lists */ +/* Remove NULL nodes from the list */ static void wsnode_nu