diff options
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | NEWS | 28 | ||||
-rw-r--r-- | README | 4 | ||||
m--------- | acvmod | 0 | ||||
-rwxr-xr-x | bootstrap | 2 | ||||
-rw-r--r-- | configure.ac | 6 | ||||
-rw-r--r-- | doc/vmod-dbrw.3 | 8 | ||||
-rw-r--r-- | doc/vmod-dbrw.texi | 2 | ||||
-rw-r--r-- | src/Makefile.am | 12 | ||||
-rw-r--r-- | src/dbrw.h | 19 | ||||
-rw-r--r-- | src/vmod_dbrw.c | 26 | ||||
-rw-r--r-- | src/vmod_dbrw.vcc | 4 | ||||
-rw-r--r-- | src/wordsplit.c | 2546 | ||||
-rw-r--r-- | src/wordsplit.h | 271 | ||||
-rw-r--r-- | tests/Makefile.am | 6 | ||||
m--------- | wordsplit | 0 |
17 files changed, 81 insertions, 2859 deletions
diff --git a/.gitmodules b/.gitmodules index 00ae21c..6f6db00 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "acvmod"] path = acvmod url = git://git.gnu.org.ua/acvmod.git +[submodule "wordsplit"] + path = wordsplit + url = git://git.gnu.org.ua/wordsplit.git diff --git a/Makefile.am b/Makefile.am index a5248b8..df797dd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ # This file is part of vmod-dbrw -# Copyright (C) 2013-2017 Sergey Poznyakoff +# Copyright (C) 2013-2019 Sergey Poznyakoff # # Vmod-dbrw is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -19,3 +19,4 @@ SUBDIRS = src doc tests include acvmod/top.am +EXTRA_DIST += wordsplit/wordsplit.c wordsplit/wordsplit.h @@ -1,9 +1,23 @@ -vmod-dbrw -- history of user-visible changes. 2019-02-13 +vmod-dbrw -- history of user-visible changes. 2022-08-21 See the end of file for copying conditions. Please send vmod-dbrw bug reports to <gray@gnu.org> -Version 2.4.90 (git) +Version 2.8, 2022-08-21 + +* Support for Varnish 7.1 + +Version 2.7, 2020-04-09 + +* Drop support for Varnish versions prior to 6.0 + + +Version 2.6, 2020-03-03 + +* Builds with Varnish 6.3.1 + + +Version 2.5, 2019-02-15 * req.http.X-VMOD-DBRW-Error @@ -17,6 +31,14 @@ set. The 'eq' flag instructs dbrw.rewrite to use exact string match, instead of regular expressions. The 'regex' flag instructs it to use regular expression matching. It is the default. +* Improve error handling in mysql submodule + +Previously, error codes ER_PARSE_ERROR and ER_EMPTY_QUERY were treated +as permanent conditions, causing mysql connection to be closed and +disabled. This is no longer the case, as they both can well mean a +transient condition (e.g. ER_PARSE_ERROR returned for the 'Illegal mix +of collations' error). + Version 2.4, 2018-12-10 @@ -93,7 +115,7 @@ Initial release ========================================================================= Copyright information: -Copyright (C) 2013-2019 Sergey Poznyakoff +Copyright (C) 2013-2020 Sergey Poznyakoff Permission is granted to anyone to make or distribute verbatim copies of this document as received, in any medium, provided that the @@ -30,7 +30,7 @@ of this approach is that rewrite rules stored in a database are easier to maintain. This version supports MySQL and PostgreSQL databases and is known to -work for Varnish versions 4.1 through 6.0.2. +work for Varnish versions 6.0.6 through 6.4.0. * Example @@ -222,7 +222,7 @@ Send bug reports and suggestions to <gray@gnu.org> * Copyright information: -Copyright (C) 2013-2018 Sergey Poznyakoff +Copyright (C) 2013-2020 Sergey Poznyakoff Permission is granted to anyone to make or distribute verbatim copies of this document as received, in any medium, provided that the diff --git a/acvmod b/acvmod -Subproject 5b214e3d72f9e261a37cf31deb41e7f8a61a181 +Subproject 0516e2461e8f2e3b33a7fffa13705cdb1de77c5 @@ -4,6 +4,6 @@ do test -d $dir || mkdir $dir done git submodule init -git submodule update +git submodule update --init --recursive test -f ChangeLog || touch ChangeLog autoreconf -f -i -s diff --git a/configure.ac b/configure.ac index fd618d3..470d09b 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ # This file is part of vmod-dbrw -*- autoconf -*- -# Copyright (C) 2013-2019 Sergey Poznyakoff +# Copyright (C) 2013-2022 Sergey Poznyakoff # # Vmod-dbrw is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with vmod-dbrw. If not, see <http://www.gnu.org/licenses/>. AC_PREREQ(2.69) -AC_INIT([vmod-dbrw], 2.4.90, [gray@gnu.org]) +AC_INIT([vmod-dbrw], 2.8, [gray@gnu.org]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR(src/vmod_dbrw.vcc) @@ -43,7 +43,7 @@ AC_PROG_MAKE_SET AC_HEADER_STDC AC_CHECK_HEADERS([sys/stdlib.h]) -AM_VARNISHAPI([4.1],[6.0.2]) +AM_VARNISHAPI([6.0],[7.1.0]) AC_DEFINE_UNQUOTED([VARNISHAPI_MAJOR],[$VARNISHAPI_MAJOR], [Varnish API major version number]) diff --git a/doc/vmod-dbrw.3 b/doc/vmod-dbrw.3 index 9bec008..023e215 100644 --- a/doc/vmod-dbrw.3 +++ b/doc/vmod-dbrw.3 @@ -1,5 +1,5 @@ .\" This file is part of Vmod-dbrw -*- nroff -*- -.\" Copyright (C) 2013-2018 Sergey Poznyakoff +.\" Copyright (C) 2013-2022 Sergey Poznyakoff .\" .\" Vmod-dbrw is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by @@ -13,7 +13,7 @@ .\" .\" You should have received a copy of the GNU General Public License .\" along with vmod-dbrw. If not, see <http://www.gnu.org/licenses/>. -.TH VMOD-DBRW 3 "February 13, 2019" "VMOD-DBRW" "User Reference" +.TH VMOD-DBRW 3 "August 21, 2022" "VMOD-DBRW" "User Reference" .SH NAME vmod-dbrw \- Database-driven rewrite rules for Varnish Cache .SH SYNOPSIS @@ -318,7 +318,7 @@ sub vcl_synth { .\" for man-based doc pages. .if "\V[MANCGI]"WEBDOC" \{\ . ds package vmod-dbrw -. ds version 2.4 +. ds version 2.8 . so download.inc \} .SH "SEE ALSO" @@ -354,7 +354,7 @@ Sergey Poznyakoff .SH "BUG REPORTS" Report bugs to <gray@gnu.org>. .SH COPYRIGHT -Copyright \(co 2013-2018 Sergey Poznyakoff +Copyright \(co 2013-2022 Sergey Poznyakoff .br .na License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> diff --git a/doc/vmod-dbrw.texi b/doc/vmod-dbrw.texi index 6a987ed..200ca0d 100644 --- a/doc/vmod-dbrw.texi +++ b/doc/vmod-dbrw.texi @@ -30,7 +30,7 @@ Published by the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -Copyright @copyright{} 2013-2018 Sergey Poznyakoff +Copyright @copyright{} 2013-2020 Sergey Poznyakoff Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or diff --git a/src/Makefile.am b/src/Makefile.am index 6cda3aa..2f23477 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,5 @@ # This file is part of vmod-dbrw -# Copyright (C) 2013-2017 Sergey Poznyakoff +# Copyright (C) 2013-2019 Sergey Poznyakoff # # Vmod-dbrw is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,14 +14,20 @@ # You should have received a copy of the GNU General Public License # along with vmod-dbrw. If not, see <http://www.gnu.org/licenses/>. -AM_CPPFLAGS=$(VARNISHAPI_CFLAGS) -I$(srcdir) -I$(builddir) +AM_CPPFLAGS=\ + $(VARNISHAPI_CFLAGS)\ + -I$(srcdir)\ + -I$(builddir)\ + -I$(top_srcdir)/wordsplit noinst_LTLIBRARIES = libsql.la libsql_la_SOURCES = \ be.c\ dbrw.h\ - sql.c\ + sql.c +nodist_libsql_la_SOURCES = \ wordsplit.h\ wordsplit.c +VPATH += $(top_srcdir)/wordsplit if USE_MYSQL libsql_la_SOURCES += mysql.c @@ -1,5 +1,5 @@ /* This file is part of vmod-dbrw - Copyright (C) 2013-2018 Sergey Poznyakoff + Copyright (C) 2013-2020 Sergey Poznyakoff Vmod-dbrw is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,17 +21,16 @@ #include <syslog.h> #include <regex.h> -#if VARNISHAPI_MAJOR > 5 -# include "cache/cache.h" -# include "vcl.h" -# include "vcc_if.h" +#include "cache/cache.h" +#include "vcl.h" +#include "vcc_if.h" + +#ifdef VPFX +# define VEVENT(a) VPFX(a) #else -# include "vcl.h" -# include "vrt.h" -# include "vcc_if.h" -# include "cache/cache.h" +/* For compatibility with varnish prior to 6.2 */ +# define VEVENT(a) a #endif -#define WSPTR(s) ((s)->ws) struct dbrw_connection; diff --git a/src/vmod_dbrw.c b/src/vmod_dbrw.c index beeb9af..f6b6446 100644 --- a/src/vmod_dbrw.c +++ b/src/vmod_dbrw.c @@ -1,5 +1,5 @@ /* This file is part of vmod-dbrw - Copyright (C) 2013-2019 Sergey Poznyakoff + Copyright (C) 2013-2022 Sergey Poznyakoff Vmod-dbrw is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -121,7 +121,7 @@ disconnect(void) } int -dbrw_event(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e) +VEVENT(dbrw_event)(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e) { switch (e) { case VCL_EVENT_LOAD: @@ -311,12 +311,12 @@ expand_backref(VRT_CTX, const char *str, const char *val, unsigned u; char *b, *p; - u = WS_Reserve(WSPTR(ctx), 0); - p = b = WSPTR(ctx)->f; + u = WS_ReserveAll(ctx->ws); + p = b = ctx->ws->f; while (*val) { if (u == 0) { - WS_Release(WSPTR(ctx), 0); + WS_Release(ctx->ws, 0); return NULL; } if (*val == '\\' || *val == '$') { @@ -329,7 +329,7 @@ expand_backref(VRT_CTX, const char *str, const char *val, matches[n].rm_so; if (len > u) { - WS_Release(WSPTR(ctx), 0); + WS_Release(ctx->ws, 0); return NULL; } @@ -355,7 +355,7 @@ expand_backref(VRT_CTX, const char *str, const char *val, if (qry) { size_t len = strlen(qry); if (len + 1 >= u) { - WS_Release(WSPTR(ctx), 0); + WS_Release(ctx->ws, 0); return NULL; } if (strchr(b, '?')) @@ -368,18 +368,22 @@ expand_backref(VRT_CTX, const char *str, const char *val, } if (u == 0) { - WS_Release(WSPTR(ctx), 0); + WS_Release(ctx->ws, 0); return NULL; } *p++ = 0; - WS_ReleaseP(WSPTR(ctx), p); + WS_ReleaseP(ctx->ws, p); return b; } #define ISEMPTY(s) ((s) == NULL || (s)[0] == 0) +#if VARNISHAPI_MAJOR > 6 +# define vrt_magic_string_end 0 +#endif + static void dbrw_sethdr(VRT_CTX, int where, const char *what, const char *value) { @@ -405,7 +409,7 @@ findmatch(VRT_CTX, struct dbrw_connection *conn, char **param) debug(conn->conf, 2, ("query returned %u tuples, %u columns", nt, nf)); if (nf < 3) - return WS_Copy(WSPTR(ctx), sql_get_column(conn, 0, 0), -1); + return WS_Copy(ctx->ws, sql_get_column(conn, 0, 0), -1); /* Three or four fields: result @@ -428,7 +432,7 @@ findmatch(VRT_CTX, struct dbrw_connection *conn, char **param) char *qry = NULL; if (ISEMPTY(pat)) { - res = WS_Copy(WSPTR(ctx), sql_get_column(conn, i, 0), -1); + res = WS_Copy(ctx->ws, sql_get_column(conn, i, 0), -1); break; } diff --git a/src/vmod_dbrw.vcc b/src/vmod_dbrw.vcc index 9a15af1..479ca66 100644 --- a/src/vmod_dbrw.vcc +++ b/src/vmod_dbrw.vcc @@ -1,5 +1,5 @@ # This file is part of vmod-dbrw -# Copyright (C) 2013-2016 Sergey Poznyakoff +# Copyright (C) 2013-2020 Sergey Poznyakoff # # Vmod-dbrw is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with vmod-dbrw. If not, see <http://www.gnu.org/licenses/>. -$Module dbrw 3 Database-driven rewrite rules for Varnish Cache +$Module dbrw 3 "Database-driven rewrite rules for Varnish Cache" COLOPHON ======== diff --git a/src/wordsplit.c b/src/wordsplit.c deleted file mode 100644 index bad59b1..0000000 --- a/src/wordsplit.c +++ /dev/null @@ -1,2546 +0,0 @@ -/* wordsplit - a word splitter - Copyright (C) 2009-2018 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. - - This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <errno.h> -#include <ctype.h> -#include <unistd.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <stdarg.h> -#include <pwd.h> -#include <glob.h> - -#if ENABLE_NLS -# include <gettext.h> -#else -# define gettext(msgid) msgid -#endif -#define _(msgid) gettext (msgid) -#define N_(msgid) msgid - -#include <wordsplit.h> - -#define ISWS(c) ((c)==' '||(c)=='\t'||(c)=='\n') -#define ISDELIM(ws,c) \ - (strchr ((ws)->ws_delim, (c)) != NULL) -#define ISPUNCT(c) (strchr("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",(c))!=NULL) -#define ISUPPER(c) ('A' <= ((unsigned) (c)) && ((unsigned) (c)) <= 'Z') -#define ISLOWER(c) ('a' <= ((unsigned) (c)) && ((unsigned) (c)) <= 'z') -#define ISALPHA(c) (ISUPPER(c) || ISLOWER(c)) -#define ISDIGIT(c) ('0' <= ((unsigned) (c)) && ((unsigned) (c)) <= '9') -#define ISXDIGIT(c) (strchr("abcdefABCDEF", c)!=NULL) -#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 WSP_RETURN_DELIMS(wsp) \ - ((wsp)->ws_flags & WRDSF_RETURN_DELIMS || ((wsp)->ws_options & WRDSO_MAXWORDS)) - -#define ALLOC_INIT 128 -#define ALLOC_INCR 128 - -static void -_wsplt_alloc_die (struct wordsplit *wsp) -{ - wsp->ws_error ("%s", _("memory exhausted")); - abort (); -} - -static void -_wsplt_error (const char *fmt, ...) -{ - va_list ap; - - va_start (ap, fmt); - vfprintf (stderr, fmt, ap); - va_end (ap); - fputc ('\n', stderr); -} - -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) -{ - errno = ENOMEM; - wsp->ws_errno = WRDSE_NOSPACE; - if (wsp->ws_flags & WRDSF_ENOMEMABRT) - wsp->ws_alloc_die (wsp); - if (wsp->ws_flags & WRDSF_SHOWERR) - wordsplit_perror (wsp); - if (!(wsp->ws_flags & WRDSF_REUSE)) - wordsplit_free (wsp); - wordsplit_free_nodes (wsp); - return wsp->ws_errno; -} - -static int wordsplit_run (const char *command, size_t length, - struct wordsplit *wsp, - int flags, int lvl); - -static int wordsplit_init (struct wordsplit *wsp, const char *input, size_t len, - int flags); -static int wordsplit_process_list (struct wordsplit *wsp, size_t start); -static int wordsplit_finish (struct wordsplit *wsp); - -static int -_wsplt_subsplit (struct wordsplit *wsp, struct wordsplit *wss, - char const *str, int len, - int flags, int finalize) -{ - int rc; - - wss->ws_delim = wsp->ws_delim; - wss->ws_debug = wsp->ws_debug; - wss->ws_error = wsp->ws_error; - wss->ws_alloc_die = wsp->ws_alloc_die; - - if (!(flags & WRDSF_NOVAR)) - { - wss->ws_env = wsp->ws_env; - wss->ws_getvar = wsp->ws_getvar; - flags |= wsp->ws_flags & (WRDSF_ENV | WRDSF_ENV_KV | WRDSF_GETVAR); - } - if (!(flags & WRDSF_NOCMD)) - { - wss->ws_command = wsp->ws_command; - } - - if ((flags & (WRDSF_NOVAR|WRDSF_NOCMD)) != (WRDSF_NOVAR|WRDSF_NOCMD)) - { - wss->ws_closure = wsp->ws_closure; - flags |= wsp->ws_flags & WRDSF_CLOSURE; - } - - wss->ws_options = wsp->ws_options; - - flags |= WRDSF_DELIM - | WRDSF_ALLOC_DIE - | WRDSF_ERROR - | WRDSF_DEBUG - | (wsp->ws_flags & (WRDSF_SHOWDBG | WRDSF_SHOWERR | WRDSF_OPTIONS)); - - rc = wordsplit_init (wss, str, len, flags); - if (rc) - return rc; - wss->ws_lvl = wsp->ws_lvl + 1; - rc = wordsplit_process_list (wss, 0); - if (rc) - { - wordsplit_free_nodes (wss); - return rc; - } - if (finalize) - { - rc = wordsplit_finish (wss); - wordsplit_free_nodes (wss); - } - return rc; -} - -static void -_wsplt_seterr_sub (struct wordsplit *wsp, struct wordsplit *wss) -{ - if (wsp->ws_errno == WRDSE_USERERR) - free (wsp->ws_usererr); - wsp->ws_errno = wss->ws_errno; - if (wss->ws_errno == WRDSE_USERERR) - { - wsp->ws_usererr = wss->ws_usererr; - wss->ws_errno = WRDSE_EOF; - wss->ws_usererr = NULL; - } -} - -static void -wordsplit_init0 (struct wordsplit *wsp) -{ - if (wsp->ws_flags & WRDSF_REUSE) - { - if (!(wsp->ws_flags & WRDSF_APPEND)) - wordsplit_free_words (wsp); - wordsplit_clearerr (wsp); - } - else - { - wsp->ws_wordv = NULL; - wsp->ws_wordc = 0; - wsp->ws_wordn = 0; - } - - wsp->ws_errno = 0; -} - -char wordsplit_c_escape_tab[] = "\\\\\"\"a\ab\bf\fn\nr\rt\tv\v"; - -static int -wordsplit_init (struct wordsplit *wsp, const char *input, size_t len, - int flags) -{ - wsp->ws_flags = flags; - - if (!(wsp->ws_flags & WRDSF_ALLOC_DIE)) - wsp->ws_alloc_die = _wsplt_alloc_die; - if (!(wsp->ws_flags & WRDSF_ERROR)) - wsp->ws_error = _wsplt_error; - - if (!(wsp->ws_flags & WRDSF_NOVAR)) - { - /* These will be initialized on first variable assignment */ - wsp->ws_envidx = wsp->ws_envsiz = 0; - wsp->ws_envbuf = NULL; - } - - if (!(wsp->ws_flags & WRDSF_NOCMD)) - { - if (!wsp->ws_command) - { - _wsplt_seterr (wsp, WRDSE_USAGE); - errno = EINVAL; - return wsp->ws_errno; - } - } - - if (wsp->ws_flags & WRDSF_SHOWDBG) - { - if (!(wsp->ws_flags & WRDSF_DEBUG)) - { - if (wsp->ws_flags & WRDSF_ERROR) - wsp->ws_debug = wsp->ws_error; - else if (wsp->ws_flags & WRDSF_SHOWERR) - wsp->ws_debug = _wsplt_error; - else - wsp->ws_flags &= ~WRDSF_SHOWDBG; - } - } - - wsp->ws_input = input; - wsp->ws_len = len; - - if (!(wsp->ws_flags & WRDSF_DOOFFS)) - wsp->ws_offs = 0; - - if (!(wsp->ws_flags & WRDSF_DELIM)) - wsp->ws_delim = " \t\n"; - - if (!(wsp->ws_flags & WRDSF_COMMENT)) - wsp->ws_comment = NULL; - - if (!(wsp->ws_flags & WRDSF_CLOSURE)) - wsp->ws_closure = NULL; - - if (!(wsp->ws_flags & WRDSF_OPTIONS)) - wsp->ws_options = 0; - - if (wsp->ws_flags & WRDSF_ESCAPE) - { - if (!wsp->ws_escape[WRDSX_WORD]) - wsp->ws_escape[WRDSX_WORD] = ""; - if (!wsp->ws_escape[WRDSX_QUOTE]) - wsp->ws_escape[WRDSX_QUOTE] = ""; - } - else - { - if (wsp->ws_flags & WRDSF_CESCAPES) - { - wsp->ws_escape[WRDSX_WORD] = wordsplit_c_escape_tab; - wsp->ws_escape[WRDSX_QUOTE] = wordsplit_c_escape_tab; - wsp->ws_options |= WRDSO_OESC_QUOTE | WRDSO_OESC_WORD - | WRDSO_XESC_QUOTE | WRDSO_XESC_WORD; - } - else - { - wsp->ws_escape[WRDSX_WORD] = ""; - wsp->ws_escape[WRDSX_QUOTE] = "\\\\\"\""; - wsp->ws_options |= WRDSO_BSKEEP_QUOTE; - } - } - - wsp->ws_endp = 0; - wsp->ws_wordi = 0; - - if (wsp->ws_flags & WRDSF_REUSE) - wordsplit_free_nodes (wsp); - wsp->ws_head = wsp->ws_tail = NULL; - - wordsplit_init0 (wsp); - - return 0; -} - -static int -alloc_space (struct wordsplit *wsp, size_t count) -{ - size_t offs = (wsp->ws_flags & WRDSF_DOOFFS) ? wsp->ws_offs : 0; - char **ptr; - size_t newalloc; - - if (wsp->ws_wordv == NULL) - { - newalloc = offs + count > ALLOC_INIT ? count : ALLOC_INIT; - ptr = calloc (newalloc, sizeof (ptr[0])); - } - else if (wsp->ws_wordn < offs + wsp->ws_wordc + count) - { - newalloc = offs + wsp->ws_wordc + - (count > ALLOC_INCR ? count : ALLOC_INCR); - ptr = realloc (wsp->ws_wordv, newalloc * sizeof (ptr[0])); - } - else - return 0; - - if (ptr) - { - wsp->ws_wordn = newalloc; - wsp->ws_wordv = ptr; - } - else - return _wsplt_nomem (wsp); - return 0; -} - - -/* Node state flags */ -#define _WSNF_NULL 0x01 /* null node (a noop) */ -#define _WSNF_WORD 0x02 /* node contains word in v.word */ -#define _WSNF_QUOTE 0x04 /* text is quoted */ -#define _WSNF_NOEXPAND 0x08 /* text is not subject to expansion */ -#define _WSNF_JOIN 0x10 /* node must be joined with the next node */ -#define _WSNF_SEXP 0x20 /* is a sed expression */ -#define _WSNF_DELIM 0x40 /* node is a delimiter */ - -#define _WSNF_EMPTYOK 0x0100 /* special flag indicating that - wordsplit_add_segm must add the - segment even if it is empty */ - -struct wordsplit_node -{ - struct wordsplit_node *prev; /* Previous element */ - struct wordsplit_node *next; /* Next element */ - int flags; /* Node flags */ - union - { - struct - { - size_t beg; /* Start of word in ws_input */ - size_t end; /* End of word in ws_input */ - } segm; - char *word; - } v; -}; - -static const char * -wsnode_flagstr (int flags) -{ - static char retbuf[7]; - char *p = retbuf; - - if (flags & _WSNF_WORD) - *p++ = 'w'; - else if (flags & _WSNF_NULL) - *p++ = 'n'; - else - *p++ = '-'; - if (flags & _WSNF_QUOTE) - *p++ = 'q'; - else - *p++ = '-'; - if (flags & _WSNF_NOEXPAND) - *p++ = 'E'; - else - *p++ = '-'; - if (flags & _WSNF_JOIN) - *p++ = 'j'; - else - *p++ = '-'; - if (flags & _WSNF_SEXP) - *p++ = 's'; - else - *p++ = '-'; - if (flags & _WSNF_DELIM) - *p++ = 'd'; - else - *p++ = '-'; - *p = 0; - return retbuf; -} - -static const char * -wsnode_ptr (struct wordsplit *wsp, struct wordsplit_node *p) -{ - if (p->flags & _WSNF_NULL) - return ""; - else if (p->flags & _WSNF_WORD) - return p->v.word; - else - return wsp->ws_input + p->v.segm.beg; -} - -static size_t -wsnode_len (struct wordsplit_node *p) -{ - if (p->flags & _WSNF_NULL) - return 0; - else if (p->flags & _WSNF_WORD) - return strlen (p->v.word); - else - return p->v.segm.end - p->v.segm.beg; -} - -static int -wsnode_new (struct wordsplit *wsp, struct wordsplit_node **pnode) -{ - struct wordsplit_node *node = calloc (1, sizeof (*node)); - if (!node) - return _wsplt_nomem (wsp); - *pnode = node; - return 0; -} - -static void -wsnode_free (struct wordsplit_node *p) -{ - if (p->flags & _WSNF_WORD) - free (p->v.word); - free (p); -} - -static void -wsnode_append (struct wordsplit *wsp, struct wordsplit_node *node) -{ - node->next = NULL; - node->prev = wsp->ws_tail; - if (wsp->ws_tail) - wsp->ws_tail->next = node; - else - wsp->ws_head = node; - wsp->ws_tail = node; -} - -static void -wsnode_remove (struct wordsplit *wsp, struct wordsplit_node *node) -{ - struct wordsplit_node *p; - - p = node->prev; - if (p) - { - p->next = node->next; - if (!node->next) - p->flags &= ~_WSNF_JOIN; - } - else - wsp->ws_head = node->next; - - p = node->next; - if (p) - p->prev = node->prev; - else - wsp->ws_tail = node->prev; - - node->next = node->prev = NULL; -} - -static struct wordsplit_node * -wsnode_tail (struct wordsplit_node *p) -{ - while (p && p->next) - p = p->next; - return p; -} - -static void -wsnode_insert (struct wordsplit *wsp, struct wordsplit_node *node, - struct wordsplit_node *anchor, int before) -{ - if (!wsp->ws_head) - { - node->next = node->prev = NULL; - wsp->ws_head = wsp->ws_tail = node; - } - else if (before) - { - if (anchor->prev) - wsnode_insert (wsp, node, anchor->prev, 0); - else - { - struct wordsplit_node *tail = wsnode_tail (node); - node->prev = NULL; - tail->next = anchor; - anchor->prev = tail; - wsp->ws_head = node; - } - } - else - { - struct wordsplit_node *p; - struct wordsplit_node *tail = wsnode_tail (node); - - p = anchor->next; - if (p) - p->prev = tail; - else - wsp->ws_tail = tail; - tail->next = p; - node->prev = anchor; - anchor->next = node; - } -} - -static int -wordsplit_add_segm (struct wordsplit *wsp, size_t beg, size_t end, int flg) -{ - struct wordsplit_node *node; - int rc; - - if (end == beg && !(flg & _WSNF_EMPTYOK)) - return 0; - rc = wsnode_new (wsp, &node); - if (rc) - return rc; - node->flags = flg & ~(_WSNF_WORD | _WSNF_EMPTYOK); - node->v.segm.beg = beg; - node->v.segm.end = end; - wsnode_append (wsp, node); - return 0; -} - -static void -wordsplit_free_nodes (struct wordsplit *wsp) -{ - struct wordsplit_node *p; - - for (p = wsp->ws_head; p;) - { - struct wordsplit_node *next = p->next; - wsnode_free (p); - p = next; - } - wsp->ws_head = wsp->ws_tail = NULL; -} - -static void -wordsplit_dump_nodes (struct wordsplit *wsp) -{ - struct wordsplit_node *p; - int n = 0; - - for (p = wsp->ws_head, n = 0; p; p = p->next, n++) - { - if (p->flags & _WSNF_WORD) - wsp->ws_debug ("(%02d) %4d: %p: %#04x (%s):%s;", - wsp->ws_lvl, - n, p, p->flags, wsnode_flagstr (p->flags), p->v.word); - else - wsp->ws_debug ("(%02d) %4d: %p: %#04x (%s):%.*s;", - wsp->ws_lvl, - n, p, p->flags, wsnode_flagstr (p->flags), - (int) (p->v.segm.end - p->v.segm.beg), - wsp->ws_input + p->v.segm.beg); - } -} - -static int -coalesce_segment (struct wordsplit *wsp, struct wordsplit_node *node) -{ - struct wordsplit_node *p, *end; - size_t len = 0; - char *buf, *cur; - int stop; - - if (!(node->flags & _WSNF_JOIN)) - return 0; - - for (p = node; p && (p->flags & _WSNF_JOIN); p = p->next) - { - len += wsnode_len (p); - } - if (p) - len += wsnode_len (p); - end = p; - - buf = malloc (len + 1); - if (!buf) - return _wsplt_nomem (wsp); - cur = buf; - - p = node; - for (stop = 0; !stop;) - { - struct wordsplit_node *next = p->next; - const char *str = wsnode_ptr (wsp, p); - size_t slen = wsnode_len (p); - - memcpy (cur, str, slen); - cur += slen; - if (p != node) - { - node->flags |= p->flags & _WSNF_QUOTE; - wsnode_remove (wsp, p); - stop = p == end; - wsnode_free (p); - } - p = next; - } - - *cur = 0; - - node->flags &= ~_WSNF_JOIN; - - if (node->flags & _WSNF_WORD) - free (node->v.word); - else - node->flags |= _WSNF_WORD; - node->v.word = buf; - return 0; -} - -static void wordsplit_string_unquote_copy (struct wordsplit *ws, int inquote, - char *dst, const char *src, - size_t n); - -static int -wsnode_quoteremoval (struct wordsplit *wsp) -{ - struct wordsplit_node *p; - - for (p = wsp->ws_head; p; p = p->next) - { - const char *str = wsnode_ptr (wsp, p); - size_t slen = wsnode_len (p); - int unquote; - - if (wsp->ws_flags & WRDSF_QUOTE) - unquote = !(p->flags & _WSNF_NOEXPAND); - else - unquote = 0; - - if (unquote) - { - if (!(p->flags & _WSNF_WORD)) - { - char *newstr = malloc (slen + 1); - if (!newstr) - return _wsplt_nomem (wsp); - memcpy (newstr, str, slen); - newstr[slen] = 0; - p->v.word = newstr; - p->flags |= _WSNF_WORD; - } - - wordsplit_string_unquote_copy (wsp, p->flags & _WSNF_QUOTE, - p->v.word, str, slen); - } - } - return 0; -} - -static int -wsnode_coalesce (struct wordsplit *wsp) -{ - struct wordsplit_node *p; - - for (p = wsp->ws_head; p; p = p->next) - { - if (p->flags & _WSNF_JOIN) - if (coalesce_segment (wsp, p)) - return 1; - } - return 0; -} - -static int -wsnode_tail_coalesce (struct wordsplit *wsp, struct wordsplit_node *p) -{ - if (p->next) - { - struct wordsplit_node *np = p; - while (np && np->next) - { - np->flags |= _WSNF_JOIN; - np = np->next; - } - if (coalesce_segment (wsp, p)) - return 1; - } - return 0; -} - -static size_t skip_delim (struct wordsplit *wsp); - -static int -wordsplit_finish (struct wordsplit *wsp) -{ - struct wordsplit_node |