summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org>2019-07-10 17:05:14 (GMT)
committer Sergey Poznyakoff <gray@gnu.org>2019-07-10 17:05:14 (GMT)
commit45f329477f5bc8b2259cf11b489095ac94b0a813 (patch) (unidiff)
tree9dea96aa8cbb144a3c8f093fbaf0a7bc478af08e
parenta3e4314631601e98d63a9b702cb31931ec5222b3 (diff)
downloadvmod-dbrw-45f329477f5bc8b2259cf11b489095ac94b0a813.tar.gz
vmod-dbrw-45f329477f5bc8b2259cf11b489095ac94b0a813.tar.bz2
Use wordsplit for a submoduleHEADmaster
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--.gitmodules3
-rw-r--r--Makefile.am3
-rwxr-xr-xbootstrap2
-rw-r--r--src/Makefile.am12
-rw-r--r--src/wordsplit.c2546
-rw-r--r--src/wordsplit.h271
-rw-r--r--tests/Makefile.am6
m---------wordsplit0
8 files changed, 20 insertions, 2823 deletions
diff --git a/.gitmodules b/.gitmodules
index 00ae21c..6f6db00 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,6 @@
1[submodule "acvmod"] 1[submodule "acvmod"]
2 path = acvmod 2 path = acvmod
3 url = git://git.gnu.org.ua/acvmod.git 3 url = git://git.gnu.org.ua/acvmod.git
4[submodule "wordsplit"]
5 path = wordsplit
6 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,8 +1,8 @@
1# This file is part of vmod-dbrw 1# This file is part of vmod-dbrw
2# Copyright (C) 2013-2017 Sergey Poznyakoff 2# Copyright (C) 2013-2019 Sergey Poznyakoff
3# 3#
4# Vmod-dbrw is free software; you can redistribute it and/or modify 4# Vmod-dbrw is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by 5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 3, or (at your option) 6# the Free Software Foundation; either version 3, or (at your option)
7# any later version. 7# any later version.
8# 8#
@@ -16,6 +16,7 @@
16ACLOCAL_AMFLAGS = -I m4 -I acvmod 16ACLOCAL_AMFLAGS = -I m4 -I acvmod
17 17
18SUBDIRS = src doc tests 18SUBDIRS = src doc tests
19 19
20include acvmod/top.am 20include acvmod/top.am
21 21
22EXTRA_DIST += wordsplit/wordsplit.c wordsplit/wordsplit.h
diff --git a/bootstrap b/bootstrap
index 42bce68..2621e7a 100755
--- a/bootstrap
+++ b/bootstrap
@@ -1,9 +1,9 @@
1#!/bin/sh 1#!/bin/sh
2for dir in m4 build-aux 2for dir in m4 build-aux
3do 3do
4 test -d $dir || mkdir $dir 4 test -d $dir || mkdir $dir
5done 5done
6git submodule init 6git submodule init
7git submodule update 7git submodule update --init --recursive
8test -f ChangeLog || touch ChangeLog 8test -f ChangeLog || touch ChangeLog
9autoreconf -f -i -s 9autoreconf -f -i -s
diff --git a/src/Makefile.am b/src/Makefile.am
index 6cda3aa..2f23477 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,8 +1,8 @@
1# This file is part of vmod-dbrw 1# This file is part of vmod-dbrw
2# Copyright (C) 2013-2017 Sergey Poznyakoff 2# Copyright (C) 2013-2019 Sergey Poznyakoff
3# 3#
4# Vmod-dbrw is free software; you can redistribute it and/or modify 4# Vmod-dbrw is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by 5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 3, or (at your option) 6# the Free Software Foundation; either version 3, or (at your option)
7# any later version. 7# any later version.
8# 8#
@@ -11,20 +11,26 @@
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details. 12# GNU General Public License for more details.
13# 13#
14# You should have received a copy of the GNU General Public License 14# You should have received a copy of the GNU General Public License
15# along with vmod-dbrw. If not, see <http://www.gnu.org/licenses/>. 15# along with vmod-dbrw. If not, see <http://www.gnu.org/licenses/>.
16 16
17AM_CPPFLAGS=$(VARNISHAPI_CFLAGS) -I$(srcdir) -I$(builddir) 17AM_CPPFLAGS=\
18 $(VARNISHAPI_CFLAGS)\
19 -I$(srcdir)\
20 -I$(builddir)\
21 -I$(top_srcdir)/wordsplit
18noinst_LTLIBRARIES = libsql.la 22noinst_LTLIBRARIES = libsql.la
19libsql_la_SOURCES = \ 23libsql_la_SOURCES = \
20 be.c\ 24 be.c\
21 dbrw.h\ 25 dbrw.h\
22 sql.c\ 26 sql.c
27nodist_libsql_la_SOURCES = \
23 wordsplit.h\ 28 wordsplit.h\
24 wordsplit.c 29 wordsplit.c
30VPATH += $(top_srcdir)/wordsplit
25 31
26if USE_MYSQL 32if USE_MYSQL
27 libsql_la_SOURCES += mysql.c 33 libsql_la_SOURCES += mysql.c
28endif 34endif
29 35
30if USE_PGSQL 36if USE_PGSQL
diff --git a/src/wordsplit.c b/src/wordsplit.c
deleted file mode 100644
index bad59b1..0000000
--- a/src/wordsplit.c
+++ b/dev/null
@@ -1,2546 +0,0 @@
1/* wordsplit - a word splitter
2 Copyright (C) 2009-2018 Sergey Poznyakoff
3
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation; either version 3 of the License, or (at your
7 option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with this program. If not, see <http://www.gnu.org/licenses/>. */
16
17#ifdef HAVE_CONFIG_H
18# include <config.h>
19#endif
20
21#include <errno.h>
22#include <ctype.h>
23#include <unistd.h>
24#include <stdlib.h>
25#include <string.h>
26#include <stdio.h>
27#include <stdarg.h>
28#include <pwd.h>
29#include <glob.h>
30
31#if ENABLE_NLS
32# include <gettext.h>
33#else
34# define gettext(msgid) msgid
35#endif
36#define _(msgid) gettext (msgid)
37#define N_(msgid) msgid
38
39#include <wordsplit.h>
40
41#define ISWS(c) ((c)==' '||(c)=='\t'||(c)=='\n')
42#define ISDELIM(ws,c) \
43 (strchr ((ws)->ws_delim, (c)) != NULL)
44#define ISPUNCT(c) (strchr("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",(c))!=NULL)
45#define ISUPPER(c) ('A' <= ((unsigned) (c)) && ((unsigned) (c)) <= 'Z')
46#define ISLOWER(c) ('a' <= ((unsigned) (c)) && ((unsigned) (c)) <= 'z')
47#define ISALPHA(c) (ISUPPER(c) || ISLOWER(c))
48#define ISDIGIT(c) ('0' <= ((unsigned) (c)) && ((unsigned) (c)) <= '9')
49#define ISXDIGIT(c) (strchr("abcdefABCDEF", c)!=NULL)
50#define ISALNUM(c) (ISALPHA(c) || ISDIGIT(c))
51#define ISPRINT(c) (' ' <= ((unsigned) (c)) && ((unsigned) (c)) <= 127)
52
53#define ISVARBEG(c) (ISALPHA(c) || c == '_')
54#define ISVARCHR(c) (ISALNUM(c) || c == '_')
55
56#define WSP_RETURN_DELIMS(wsp) \
57 ((wsp)->ws_flags & WRDSF_RETURN_DELIMS || ((wsp)->ws_options & WRDSO_MAXWORDS))
58
59#define ALLOC_INIT 128
60#define ALLOC_INCR 128
61
62static void
63_wsplt_alloc_die (struct wordsplit *wsp)
64{
65 wsp->ws_error ("%s", _("memory exhausted"));
66 abort ();
67}
68
69static void
70_wsplt_error (const char *fmt, ...)
71{
72 va_list ap;
73
74 va_start (ap, fmt);
75 vfprintf (stderr, fmt, ap);
76 va_end (ap);
77 fputc ('\n', stderr);
78}
79
80static void wordsplit_free_nodes (struct wordsplit *);
81
82static int
83_wsplt_seterr (struct wordsplit *wsp, int ec)
84{
85 wsp->ws_errno = ec;
86 if (wsp->ws_flags & WRDSF_SHOWERR)
87 wordsplit_perror (wsp);
88 return ec;
89}
90
91static int
92_wsplt_nomem (struct wordsplit *wsp)
93{
94 errno = ENOMEM;
95 wsp->ws_errno = WRDSE_NOSPACE;
96 if (wsp->ws_flags & WRDSF_ENOMEMABRT)
97 wsp->ws_alloc_die (wsp);
98 if (wsp->ws_flags & WRDSF_SHOWERR)
99 wordsplit_perror (wsp);
100 if (!(wsp->ws_flags & WRDSF_REUSE))
101 wordsplit_free (wsp);
102 wordsplit_free_nodes (wsp);
103 return wsp->ws_errno;
104}
105
106static int wordsplit_run (const char *command, size_t length,
107 struct wordsplit *wsp,
108 int flags, int lvl);
109
110static int wordsplit_init (struct wordsplit *wsp, const char *input, size_t len,
111 int flags);
112static int wordsplit_process_list (struct wordsplit *wsp, size_t start);
113static int wordsplit_finish (struct wordsplit *wsp);
114
115static int
116_wsplt_subsplit (struct wordsplit *wsp, struct wordsplit *wss,
117 char const *str, int len,
118 int flags, int finalize)
119{
120 int rc;
121
122 wss->ws_delim = wsp->ws_delim;
123 wss->ws_debug = wsp->ws_debug;
124 wss->ws_error = wsp->ws_error;
125 wss->ws_alloc_die = wsp->ws_alloc_die;
126
127 if (!(flags & WRDSF_NOVAR))
128 {
129 wss->ws_env = wsp->ws_env;
130 wss->ws_getvar = wsp->ws_getvar;
131 flags |= wsp->ws_flags & (WRDSF_ENV | WRDSF_ENV_KV | WRDSF_GETVAR);
132 }
133 if (!(flags & WRDSF_NOCMD))
134 {
135 wss->ws_command = wsp->ws_command;
136 }
137
138 if ((flags & (WRDSF_NOVAR|WRDSF_NOCMD)) != (WRDSF_NOVAR|WRDSF_NOCMD))
139 {
140 wss->ws_closure = wsp->ws_closure;
141 flags |= wsp->ws_flags & WRDSF_CLOSURE;
142 }
143
144 wss->ws_options = wsp->ws_options;
145
146 flags |= WRDSF_DELIM
147 | WRDSF_ALLOC_DIE
148 | WRDSF_ERROR
149 | WRDSF_DEBUG
150 | (wsp->ws_flags & (WRDSF_SHOWDBG | WRDSF_SHOWERR | WRDSF_OPTIONS));
151
152 rc = wordsplit_init (wss, str, len, flags);
153 if (rc)
154 return rc;
155 wss->ws_lvl = wsp->ws_lvl + 1;
156 rc = wordsplit_process_list (wss, 0);
157 if (rc)
158 {
159 wordsplit_free_nodes (wss);
160 return rc;
161 }
162 if (finalize)
163 {
164 rc = wordsplit_finish (wss);
165 wordsplit_free_nodes (wss);
166 }
167 return rc;
168}
169
170static void
171_wsplt_seterr_sub (struct wordsplit *wsp, struct wordsplit *wss)
172{
173 if (wsp->ws_errno == WRDSE_USERERR)
174 free (wsp->ws_usererr);
175 wsp->ws_errno = wss->ws_errno;
176 if (wss->ws_errno == WRDSE_USERERR)
177 {
178 wsp->ws_usererr = wss->ws_usererr;
179 wss->ws_errno = WRDSE_EOF;
180 wss->ws_usererr = NULL;
181 }
182}
183
184static void
185wordsplit_init0 (struct wordsplit *wsp)
186{
187 if (wsp->ws_flags & WRDSF_REUSE)
188 {
189 if (!(wsp->ws_flags & WRDSF_APPEND))
190 wordsplit_free_words (wsp);
191 wordsplit_clearerr (wsp);
192 }
193 else
194 {
195 wsp->ws_wordv = NULL;
196 wsp->ws_wordc = 0;
197 wsp->ws_wordn = 0;
198 }
199
200 wsp->ws_errno = 0;
201}
202
203char wordsplit_c_escape_tab[] = "\\\\\"\"a\ab\bf\fn\nr\rt\tv\v";
204
205static int
206wordsplit_init (struct wordsplit *wsp, const char *input, size_t len,
207 int flags)
208{
209 wsp->ws_flags = flags;
210
211 if (!(wsp->ws_flags & WRDSF_ALLOC_DIE))
212 wsp->ws_alloc_die = _wsplt_alloc_die;
213 if (!(wsp->ws_flags & WRDSF_ERROR))
214 wsp->ws_error = _wsplt_error;
215
216 if (!(wsp->ws_flags & WRDSF_NOVAR))
217 {
218 /* These will be initialized on first variable assignment */
219 wsp->ws_envidx = wsp->ws_envsiz = 0;
220 wsp->ws_envbuf = NULL;
221 }
222
223 if (!(wsp->ws_flags & WRDSF_NOCMD))
224 {
225 if (!wsp->ws_command)
226 {
227 _wsplt_seterr (wsp, WRDSE_USAGE);
228 errno = EINVAL;
229 return wsp->ws_errno;
230 }
231 }
232
233 if (wsp->ws_flags & WRDSF_SHOWDBG)
234 {
235 if (!(wsp->ws_flags & WRDSF_DEBUG))
236 {
237 if (wsp->ws_flags & WRDSF_ERROR)
238 wsp->ws_debug = wsp->ws_error;
239 else if (wsp->ws_flags & WRDSF_SHOWERR)
240 wsp->ws_debug = _wsplt_error;
241 else
242 wsp->ws_flags &= ~WRDSF_SHOWDBG;
243 }
244 }
245
246 wsp->ws_input = input;
247 wsp->ws_len = len;
248
249 if (!(wsp->ws_flags & WRDSF_DOOFFS))
250 wsp->ws_offs = 0;
251
252 if (!(wsp->ws_flags & WRDSF_DELIM))
253 wsp->ws_delim = " \t\n";
254
255 if (!(wsp->ws_flags & WRDSF_COMMENT))
256 wsp->ws_comment = NULL;
257
258 if (!(wsp->ws_flags & WRDSF_CLOSURE))
259 wsp->ws_closure = NULL;
260
261 if (!(wsp->ws_flags & WRDSF_OPTIONS))
262 wsp->ws_options = 0;
263
264 if (wsp->ws_flags & WRDSF_ESCAPE)
265 {
266 if (!wsp->ws_escape[WRDSX_WORD])
267 wsp->ws_escape[WRDSX_WORD] = "";
268 if (!wsp->ws_escape[WRDSX_QUOTE])
269 wsp->ws_escape[WRDSX_QUOTE] = "";
270 }
271 else
272 {
273 if (wsp->ws_flags & WRDSF_CESCAPES)
274 {
275 wsp->ws_escape[WRDSX_WORD] = wordsplit_c_escape_tab;
276 wsp->ws_escape[WRDSX_QUOTE] = wordsplit_c_escape_tab;
277 wsp->ws_options |= WRDSO_OESC_QUOTE | WRDSO_OESC_WORD
278 | WRDSO_XESC_QUOTE | WRDSO_XESC_WORD;
279 }
280 else
281 {
282 wsp->ws_escape[WRDSX_WORD] = "";
283 wsp->ws_escape[WRDSX_QUOTE] = "\\\\\"\"";
284 wsp->ws_options |= WRDSO_BSKEEP_QUOTE;
285 }
286 }
287
288 wsp->ws_endp = 0;
289 wsp->ws_wordi = 0;
290
291 if (wsp->ws_flags & WRDSF_REUSE)
292 wordsplit_free_nodes (wsp);
293 wsp->ws_head = wsp->ws_tail = NULL;
294
295 wordsplit_init0 (wsp);
296
297 return 0;
298}
299
300static int
301alloc_space (struct wordsplit *wsp, size_t count)
302{
303 size_t offs = (wsp->ws_flags & WRDSF_DOOFFS) ? wsp->ws_offs : 0;
304 char **ptr;
305 size_t newalloc;
306
307 if (wsp->ws_wordv == NULL)
308 {
309 newalloc = offs + count > ALLOC_INIT ? count : ALLOC_INIT;
310 ptr = calloc (newalloc, sizeof (ptr[0]));
311 }
312 else if (wsp->ws_wordn < offs + wsp->ws_wordc + count)
313 {
314 newalloc = offs + wsp->ws_wordc +
315 (count > ALLOC_INCR ? count : ALLOC_INCR);
316 ptr = realloc (wsp->ws_wordv, newalloc * sizeof (ptr[0]));
317 }
318 else
319 return 0;
320
321 if (ptr)
322 {
323 wsp->ws_wordn = newalloc;
324 wsp->ws_wordv = ptr;
325 }
326 else
327 return _wsplt_nomem (wsp);
328 return 0;
329}
330
331
332/* Node state flags */
333 #define _WSNF_NULL 0x01/* null node (a noop) */
334 #define _WSNF_WORD 0x02/* node contains word in v.word */
335 #define _WSNF_QUOTE 0x04/* text is quoted */
336 #define _WSNF_NOEXPAND 0x08/* text is not subject to expansion */
337 #define _WSNF_JOIN 0x10/* node must be joined with the next node */
338 #define _WSNF_SEXP 0x20/* is a sed expression */
339#define _WSNF_DELIM 0x40 /* node is a delimiter */
340
341 #define _WSNF_EMPTYOK 0x0100/* special flag indicating that
342 wordsplit_add_segm must add the
343 segment even if it is empty */
344
345struct wordsplit_node
346{
347 struct wordsplit_node *prev;/* Previous element */
348 struct wordsplit_node *next;/* Next element */
349 int flags; /* Node flags */
350 union
351 {
352 struct
353 {
354 size_t beg; /* Start of word in ws_input */
355 size_t end; /* End of word in ws_input */
356 } segm;
357 char *word;
358 } v;
359};
360
361static const char *
362wsnode_flagstr (int flags)
363{
364 static char retbuf[7];
365 char *p = retbuf;
366
367 if (flags & _WSNF_WORD)
368 *p++ = 'w';
369 else if (flags & _WSNF_NULL)
370 *p++ = 'n';
371 else
372 *p++ = '-';
373 if (flags & _WSNF_QUOTE)
374 *p++ = 'q';
375 else
376 *p++ = '-';
377 if (flags & _WSNF_NOEXPAND)
378 *p++ = 'E';
379 else
380 *p++ = '-';
381 if (flags & _WSNF_JOIN)
382 *p++ = 'j';
383 else
384 *p++ = '-';
385 if (flags & _WSNF_SEXP)
386 *p++ = 's';
387 else
388 *p++ = '-';
389 if (flags & _WSNF_DELIM)
390 *p++ = 'd';
391 else
392 *p++ = '-';
393 *p = 0;
394 return retbuf;
395}
396
397static const char *
398wsnode_ptr (struct wordsplit *wsp, struct wordsplit_node *p)
399{
400 if (p->flags & _WSNF_NULL)
401 return "";
402 else if (p->flags & _WSNF_WORD)
403 return p->v.word;
404 else
405 return wsp->ws_input + p->v.segm.beg;
406}
407
408static size_t
409wsnode_len (struct wordsplit_node *p)
410{
411 if (p->flags & _WSNF_NULL)
412 return 0;
413 else if (p->flags & _WSNF_WORD)
414 return strlen (p->v.word);
415 else
416 return p->v.segm.end - p->v.segm.beg;
417}
418
419static int
420wsnode_new (struct wordsplit *wsp, struct wordsplit_node **pnode)
421{
422 struct wordsplit_node *node = calloc (1, sizeof (*node));
423 if (!node)
424 return _wsplt_nomem (wsp);
425 *pnode = node;
426 return 0;
427}
428
429static void
430wsnode_free (struct wordsplit_node *p)
431{
432 if (p->flags & _WSNF_WORD)
433 free (p->v.word);
434 free (p);
435}
436
437static void
438wsnode_append (struct wordsplit *wsp, struct wordsplit_node *node)
439{
440 node->next = NULL;
441 node->prev = wsp->ws_tail;
442 if (wsp->ws_tail)
443 wsp->ws_tail->next = node;
444 else
445 wsp->ws_head = node;
446 wsp->ws_tail = node;
447}
448
449static void
450wsnode_remove (struct wordsplit *wsp, struct wordsplit_node *node)
451{
452 struct wordsplit_node *p;
453
454 p = node->prev;
455 if (p)
456 {
457 p->next = node->next;
458 if (!node->next)
459 p->flags &= ~_WSNF_JOIN;
460 }
461 else
462 wsp->ws_head = node->next;
463
464 p = node->next;
465 if (p)
466 p->prev = node->prev;
467 else
468 wsp->ws_tail = node->prev;
469
470 node->next = node->prev = NULL;
471}
472
473static struct wordsplit_node *
474wsnode_tail (struct wordsplit_node *p)
475{
476 while (p && p->next)
477 p = p->next;
478 return p;
479}
480
481static void
482wsnode_insert (struct wordsplit *wsp, struct wordsplit_node *node,
483 struct wordsplit_node *anchor, int before)
484{
485 if (!wsp->ws_head)
486 {
487 node->next = node->prev = NULL;
488 wsp->ws_head = wsp->ws_tail = node;
489 }
490 else if (before)
491 {
492 if (anchor->prev)
493 wsnode_insert (wsp, node, anchor->prev, 0);
494 else
495 {
496 struct wordsplit_node *tail = wsnode_tail (node);
497 node->prev = NULL;
498 tail->next = anchor;
499 anchor->prev = tail;
500 wsp->ws_head = node;
501 }
502 }
503 else
504 {
505 struct wordsplit_node *p;
506 struct wordsplit_node *tail = wsnode_tail (node);
507
508 p = anchor->next;
509 if (p)
510 p->prev = tail;
511 else
512 wsp->ws_tail = tail;
513 tail->next = p;
514 node->prev = anchor;
515 anchor->next = node;
516 }
517}
518
519static int
520wordsplit_add_segm (struct wordsplit *wsp, size_t beg, size_t end, int flg)
521{
522 struct wordsplit_node *node;
523 int rc;
524
525 if (end == beg && !(flg & _WSNF_EMPTYOK))
526 return 0;
527 rc = wsnode_new (wsp, &node);
528 if (rc)
529 return rc;
530 node->flags = flg & ~(_WSNF_WORD | _WSNF_EMPTYOK);
531 node->v.segm.beg = beg;
532 node->v.segm.end = end;
533 wsnode_append (wsp, node);
534 return 0;
535}
536
537static void
538wordsplit_free_nodes (struct wordsplit *wsp)
539{
540 struct wordsplit_node *p;
541
542 for (p = wsp->ws_head; p;)
543 {
544 struct wordsplit_node *next = p->next;
545 wsnode_free (p);
546 p = next;
547 }
548 wsp->ws_head = wsp->ws_tail = NULL;
549}
550
551static void
552wordsplit_dump_nodes (struct wordsplit *wsp)
553{
554 struct wordsplit_node *p;
555 int n = 0;
556
557 for (p = wsp->ws_head, n = 0; p; p = p->next, n++)
558 {
559 if (p->flags & _WSNF_WORD)
560 wsp->ws_debug ("(%02d) %4d: %p: %#04x (%s):%s;",
561 wsp->ws_lvl,
562 n, p, p->flags, wsnode_flagstr (p->flags), p->v.word);
563 else
564 wsp->ws_debug ("(%02d) %4d: %p: %#04x (%s):%.*s;",
565 wsp->ws_lvl,
566 n, p, p->flags, wsnode_flagstr (p->flags),
567 (int) (p->v.segm.end - p->v.segm.beg),
568 wsp->ws_input + p->v.segm.beg);
569 }
570}
571
572static int
573coalesce_segment (struct wordsplit *wsp, struct wordsplit_node *node)
574{
575 struct wordsplit_node *p, *end;
576 size_t len = 0;
577 char *buf, *cur;
578 int stop;
579
580 if (!(node->flags & _WSNF_JOIN))
581 return 0;
582
583 for (p = node; p && (p->flags & _WSNF_JOIN); p = p->next)
584 {
585 len += wsnode_len (p);
586 }
587 if (p)
588 len += wsnode_len (p);
589 end = p;
590
591 buf = malloc (len + 1);
592 if (!buf)
593 return _wsplt_nomem (wsp);
594 cur = buf;
595
596 p = node;
597 for (stop = 0; !stop;)
598 {
599 struct wordsplit_node *next = p->next;
600 const char *str = wsnode_ptr (wsp, p);
601 size_t slen = wsnode_len (p);
602
603 memcpy (cur, str, slen);
604 cur += slen;
605 if (p != node)
606 {
607 node->flags |= p->flags & _WSNF_QUOTE;
608 wsnode_remove (wsp, p);
609 stop = p == end;
610 wsnode_free (p);
611 }
612 p = next;
613 }
614
615 *cur = 0;
616
617 node->flags &= ~_WSNF_JOIN;
618
619 if (node->flags & _WSNF_WORD)
620 free (node->v.word);
621 else
622 node->flags |= _WSNF_WORD;
623 node->v.word = buf;
624 return 0;
625}
626
627static void wordsplit_string_unquote_copy (struct wordsplit *ws, int inquote,
628 char *dst, const char *src,
629 size_t n);
630
631static int
632wsnode_quoteremoval (struct wordsplit *wsp)
633{
634 struct wordsplit_node *p;
635
636 for (p = wsp->ws_head; p; p = p->next)
637 {
638 const char *str = wsnode_ptr (wsp, p);
639 size_t slen = wsnode_len (p);
640 int unquote;
641
642 if (wsp->ws_flags & WRDSF_QUOTE)
643 unquote = !(p->flags & _WSNF_NOEXPAND);
644 else
645 unquote = 0;
646
647 if (unquote)
648 {
649 if (!(p->flags & _WSNF_WORD))
650 {
651 char *newstr = malloc (slen + 1);
652 if (!newstr)
653 return _wsplt_nomem (wsp);
654 memcpy (newstr, str, slen);
655 newstr[slen] = 0;
656 p->v.word = newstr;
657 p->flags |= _WSNF_WORD;
658 }
659
660 wordsplit_string_unquote_copy (wsp, p->flags & _WSNF_QUOTE,
661 p->v.word, str, slen);
662 }
663 }
664 return 0;
665}
666
667static int
668wsnode_coalesce (struct wordsplit *wsp)
669{
670 struct wordsplit_node *p;
671
672 for (p = wsp->ws_head; p; p = p->next)
673 {
674 if (p->flags & _WSNF_JOIN)
675 if (coalesce_segment (wsp, p))
676 return 1;
677 }
678 return 0;
679}
680
681static int
682wsnode_tail_coalesce (struct wordsplit *wsp, struct wordsplit_node *p)
683{
684 if (p->next)
685 {
686 struct wordsplit_node *np = p;
687 while (np && np->next)
688 {
689 np->flags |= _WSNF_JOIN;
690 np = np->next;
691 }
692 if (coalesce_segment (wsp, p))
693 return 1;
694 }
695 return 0;
696}
697
698static size_t skip_delim (struct wordsplit *wsp);
699
700static int
701wordsplit_finish (struct wordsplit *wsp)
702{
703 struct wordsplit_node *p;
704 size_t n;
705 int delim;
706
707 /* Postprocess delimiters. It would be rather simple, if it weren't for
708 the incremental operation.
709
710 Nodes of type _WSNF_DELIM get inserted to the node list if either
711 WRDSF_RETURN_DELIMS flag or WRDSO_MAXWORDS option is set.
712
713 The following cases should be distinguished:
714
715 1. If both WRDSF_SQUEEZE_DELIMS and WRDSF_RETURN_DELIMS are set, compress
716 any runs of similar delimiter nodes to a single node. The nodes are
717 'similar' if they point to the same delimiter character.
718
719 If WRDSO_MAXWORDS option is set, stop compressing when
720 ws_wordi + 1 == ws_maxwords, and coalesce the rest of nodes into
721 a single last node.
722
723 2. If WRDSO_MAXWORDS option is set, but WRDSF_RETURN_DELIMS is not,
724 remove any delimiter nodes. Stop operation when
725 ws_wordi + 1 == ws_maxwords, and coalesce the rest of nodes into
726 a single last node.
727
728 3. If incremental operation is in progress, restart the loop any time
729 a delimiter node is about to be returned, unless WRDSF_RETURN_DELIMS
730 is set.
731 */
732 again:
733 delim = 0; /* Delimiter being processed (if any) */
734 n = 0; /* Number of words processed so far */
735 p = wsp->ws_head; /* Current node */
736
737 while (p)
738 {
739 struct wordsplit_node *next = p->next;
740 if (p->flags & _WSNF_DELIM)
741 {
742 if (wsp->ws_flags & WRDSF_RETURN_DELIMS)
743 {
744 if (wsp->ws_flags & WRDSF_SQUEEZE_DELIMS)
745 {
746 char const *s = wsnode_ptr (wsp, p);
747 if (delim)
748 {
749 if (delim == *s)
750 {
751 wsnode_remove (wsp, p);
752 p = next;
753 continue;
754 }
755 else
756 {
757 delim = 0;
758 n++; /* Count this node; it will be returned */
759 }
760 }
761 else
762 {
763 delim = *s;
764 p = next;
765 continue;
766 }
767 }
768 }
769 else if (wsp->ws_options & WRDSO_MAXWORDS)
770 {
771 wsnode_remove (wsp, p);
772 p = next;
773 continue;
774 }
775 }
776 else
777 {
778 if (delim)
779 {
780 /* Last node was a delimiter or a compressed run of delimiters;
781 Count it, and clear the delimiter marker */
782 n++;
783 delim = 0;
784 }
785 if (wsp->ws_options & WRDSO_MAXWORDS)
786 {
787 if (wsp->ws_wordi + n + 1 == wsp->ws_maxwords)
788 break;
789 }
790 }
791 n++;
792 if (wsp->ws_flags & WRDSF_INCREMENTAL)
793 p = NULL; /* Break the loop */
794 else
795 p = next;
796 }
797
798 if (p)
799 {
800 /* We're here if WRDSO_MAXWORDS is in effect and wsp->ws_maxwords
801 words have already been collected. Reconstruct a single final
802 node from the remaining nodes. */
803 if (wsnode_tail_coalesce (wsp, p))
804 return wsp->ws_errno;
805 n++;
806 }
807
808 if (n == 0 && (wsp->ws_flags & WRDSF_INCREMENTAL))
809 {
810 /* The loop above have eliminated all nodes. Restart the
811 processing, if there's any input left. */
812 if (wsp->ws_endp < wsp->ws_len)
813 {
814 int rc;
815 if (wsp->ws_flags & WRDSF_SHOWDBG)
816 wsp->ws_debug (_("Restarting"));
817 rc = wordsplit_process_list (wsp, skip_delim (wsp));
818 if (rc)
819 return rc;
820 }
821 else
822 {
823 wsp->ws_error = WRDSE_EOF;
824 return WRDSE_EOF;
825 }
826 goto again;
827 }
828
829 if (alloc_space (wsp, n + 1))
830 return wsp->ws_errno;
831
832 while (wsp->ws_head)
833 {
834 const char *str = wsnode_ptr (wsp, wsp->ws_head);
835 size_t slen = wsnode_len (wsp->ws_head);
836 char *newstr = malloc (slen + 1);
837
838 /* Assign newstr first, even if it is NULL. This way
839 wordsplit_free will work even if we return
840 nomem later. */
841 wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc] = newstr;
842 if (!newstr)
843 return _wsplt_nomem (wsp);
844 memcpy (newstr, str, slen);
845 newstr[slen] = 0;
846
847 wsnode_remove (wsp, wsp->ws_head);
848
849 wsp->ws_wordc++;
850 wsp->ws_wordi++;
851
852 if (wsp->ws_flags & WRDSF_INCREMENTAL)
853 break;
854 }
855 wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc] = NULL;
856 return 0;
857}
858
859int
860wordsplit_append (wordsplit_t *wsp, int argc, char **argv)
861{
862 int rc;
863 size_t i;
864
865 rc = alloc_space (wsp, wsp->ws_wordc + argc + 1);
866 if (rc)
867 return rc;
868 for (i = 0; i < argc; i++)
869 {
870 char *newstr = strdup (argv[i]);
871 if (!newstr)
872 {
873 while (i > 0)
874 {
875 free (wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc + i - 1]);
876 wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc + i - 1] = NULL;
877 i--;
878 }
879 return _wsplt_nomem (wsp);
880 }
881 wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc + i] = newstr;
882 }
883 wsp->ws_wordc += i;
884 wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc] = NULL;
885 return 0;
886}
887
888/* Variable expansion */
889static int
890node_split_prefix (struct wordsplit *wsp,
891 struct wordsplit_node **ptail,
892 struct wordsplit_node *node,
893 size_t beg, size_t len, int flg)
894{
895 struct wordsplit_node *newnode;
896
897 if (len == 0)
898 return 0;
899 if (wsnode_new (wsp, &newnode))
900 return 1;
901 wsnode_insert (wsp, newnode, *ptail, 0);
902 if (node->flags & _WSNF_WORD)
903 {
904 const char *str = wsnode_ptr (wsp, node);
905 char *newstr = malloc (len + 1);
906 if (!newstr)
907 return _wsplt_nomem (wsp);
908 memcpy (newstr, str + beg, len);
909 newstr[len] = 0;
910 newnode->flags = _WSNF_WORD;
911 newnode->v.word = newstr;
912 }
913 else
914 {
915 newnode->v.segm.beg = node->v.segm.beg + beg;
916 newnode->v.segm.end = newnode->v.segm.beg + len;
917 }
918 newnode->flags |= flg;
919 *ptail = newnode;
920 return 0;
921}
922
923static int
924find_closing_paren (const char *str, size_t i, size_t len, size_t *poff,
925 char const *paren)
926{
927 enum { st_init, st_squote, st_dquote } state = st_init;
928 size_t level = 1;
929
930 for (; i < len; i++)
931 {
932 switch (state)
933 {
934 case st_init:
935 switch (str[i])
936 {
937 default:
938 if (str[i] == paren[0])
939 {
940 level++;
941 break;
942 }
943 else if (str[i] == paren[1])
944 {
945 if (--level == 0)
946 {
947 *poff = i;
948 return 0;
949 }
950 break;
951 }
952 break;
953
954 case '"':
955 state = st_dquote;
956 break;
957
958 case '\'':
959 state = st_squote;
960 break;
961 }
962 break;
963
964 case st_squote:
965 if (str[i] == '\'')
966 state = st_init;
967 break;
968
969 case st_dquote:
970 if (str[i] == '\\')
971 i++;
972 else if (str[i] == '"')
973 state = st_init;
974 break;
975 }
976 }
977 return 1;
978}
979
980static int
981wordsplit_find_env (struct wordsplit *wsp, const char *name, size_t len,
982 char const **ret)
983{
984 size_t i;
985
986 if (!(wsp->ws_flags & WRDSF_ENV))
987 return WRDSE_UNDEF;
988
989 if (wsp->ws_flags & WRDSF_ENV_KV)
990 {
991 /* A key-value pair environment */
992 for (i = 0; wsp->ws_env[i]; i++)
993 {
994 size_t elen = strlen (wsp->ws_env[i]);
995 if (elen == len && memcmp (wsp->ws_env[i], name, elen) == 0)
996 {
997 *ret = wsp->ws_env[i + 1];
998 return WRDSE_OK;
999 }
1000 /* Skip the value. Break the loop if it is NULL. */
1001 i++;
1002 if (wsp->ws_env[i] == NULL)
1003 break;
1004 }
1005 }
1006 else if (wsp->ws_env)
1007 {
1008 /* Usual (A=B) environment. */
1009 for (i = 0; wsp->ws_env[i]; i++)
1010 {
1011 size_t j;
1012 const char *var = wsp->ws_env[i];
1013
1014 for (j = 0; j < len; j++)
1015 if (name[j] != var[j])
1016 break;
1017 if (j == len && var[j] == '=')
1018 {
1019 *ret = var + j + 1;
1020 return WRDSE_OK;
1021 }
1022 }
1023 }
1024 return WRDSE_UNDEF;
1025}
1026
1027static int
1028wsplt_assign_var (struct wordsplit *wsp, const char *name, size_t namelen,
1029 char *value)
1030{
1031 int n = (wsp->ws_flags & WRDSF_ENV_KV) ? 2 : 1;
1032 char *v;
1033
1034 if (wsp->ws_envidx + n >= wsp->ws_envsiz)
1035 {
1036 size_t sz;
1037 char **newenv;
1038
1039 if (!wsp->ws_envbuf)
1040 {
1041 if (wsp->ws_flags & WRDSF_ENV)
1042 {
1043 size_t i = 0, j;
1044
1045 if (wsp->ws_env)
1046 {
1047 for (; wsp->ws_env[i]; i++)
1048 ;
1049 }
1050
1051 sz = i + n + 1;
1052
1053 newenv = calloc (sz, sizeof(newenv[0]));
1054 if (!newenv)
1055 return _wsplt_nomem (wsp);
1056
1057 for (j = 0; j < i; j++)
1058 {
1059 newenv[j] = strdup (wsp->ws_env[j]);
1060 if (!newenv[j])
1061 {
1062 for (; j > 1; j--)
1063 free (newenv[j-1]);
1064 free (newenv[j-1]);
1065 return _wsplt_nomem (wsp);
1066 }
1067 }
1068 newenv[j] = NULL;
1069
1070 wsp->ws_envbuf = newenv;
1071 wsp->ws_envidx = i;
1072 wsp->ws_envsiz = sz;
1073 wsp->ws_env = (const char**) wsp->ws_envbuf;
1074 }
1075 else
1076 {
1077 newenv = calloc (WORDSPLIT_ENV_INIT, sizeof(newenv[0]));
1078 if (!newenv)
1079 return _wsplt_nomem (wsp);
1080 wsp->ws_envbuf = newenv;
1081 wsp->ws_envidx = 0;
1082 wsp->ws_envsiz = WORDSPLIT_ENV_INIT;
1083 wsp->ws_env = (const char**) wsp->ws_envbuf;
1084 wsp->ws_flags |= WRDSF_ENV;
1085 }
1086 }
1087 else
1088 {
1089 wsp->ws_envsiz *= 2;
1090 newenv = realloc (wsp->ws_envbuf,
1091 wsp->ws_envsiz * sizeof (wsp->ws_envbuf[0]));
1092 if (!newenv)
1093 return _wsplt_nomem (wsp);
1094 wsp->ws_envbuf = newenv;
1095 wsp->ws_env = (const char**) wsp->ws_envbuf;
1096 }
1097 }
1098
1099 if (wsp->ws_flags & WRDSF_ENV_KV)
1100 {
1101 /* A key-value pair environment */
1102 char *p = malloc (namelen + 1);
1103 if (!p)
1104 return _wsplt_nomem (wsp);
1105 memcpy (p, name, namelen);
1106 p[namelen] = 0;
1107
1108 v = strdup (value);
1109 if (!v)
1110 {
1111 free (p);
1112 return _wsplt_nomem (wsp);
1113 }
1114 wsp->ws_env[wsp->ws_envidx++] = p;
1115 wsp->ws_env[wsp->ws_envidx++] = v;
1116 }
1117 else
1118 {
1119 v = malloc (namelen + strlen(value) + 2);
1120 if (!v)
1121 return _wsplt_nomem (wsp);
1122 memcpy (v, name, namelen);
1123 v[namelen++] = '=';
1124 strcpy(v + namelen, value);
1125 wsp->ws_env[wsp->ws_envidx++] = v;
1126 }
1127 wsp->ws_env[wsp->ws_envidx++] = NULL;
1128 return WRDSE_OK;
1129}
1130
1131static int
1132expvar (struct wordsplit *wsp, const char *str, size_t len,
1133 struct wordsplit_node **ptail, const char **pend, int flg)
1134{
1135 size_t i = 0;
1136 const char *defstr = NULL;
1137 char *value;
1138 const char *vptr;
1139 struct wordsplit_node *newnode;
1140 const char *start = str - 1;
1141 int rc;
1142 struct wordsplit ws;
1143
1144 if (ISVARBEG (str[0]))
1145 {
1146 for (i = 1; i < len; i++)
1147 if (!ISVARCHR (str[i]))
1148 break;
1149 *pend = str + i - 1;
1150 }
1151 else if (str[0] == '{')
1152 {
1153 str++;
1154 len--;
1155 for (i = 1; i < len; i++)
1156 {
1157 if (str[i] == ':')
1158 {
1159 size_t j;
1160
1161 defstr = str + i + 1;
1162 if (find_closing_paren (str, i + 1, len, &j, "{}"))
1163 return _wsplt_seterr (wsp, WRDSE_CBRACE);
1164 *pend = str + j;
1165 break;
1166 }
1167 else if (str[i] == '}')
1168 {
1169 defstr = NULL;
1170 *pend = str + i;
1171 break;
1172 }
1173 else if (strchr ("-+?=", str[i]))
1174 {
1175 size_t j;
1176
1177 defstr = str + i;
1178 if (find_closing_paren (str, i, len, &j, "{}"))
1179 return _wsplt_seterr (wsp, WRDSE_CBRACE);
1180 *pend = str + j;
1181 break;
1182 }
1183 }
1184 if (i == len)
1185 return _wsplt_seterr (wsp, WRDSE_CBRACE);
1186 }
1187 else
1188 {
1189 if (wsnode_new (wsp, &newnode))
1190 return 1;
1191 wsnode_insert (wsp, newnode, *ptail, 0);
1192 *ptail = newnode;
1193 newnode->flags = _WSNF_WORD | flg;
1194 newnode->v.word = malloc (3);
1195 if (!newnode->v.word)
1196 return _wsplt_nomem (wsp);
1197 newnode->v.word[0] = '$';
1198 newnode->v.word[1] = str[0];
1199 newnode->v.word[2] = 0;
1200 *pend = str;
1201 return 0;
1202 }
1203
1204 /* Actually expand the variable */
1205 /* str - start of the variable name
1206 i - its length
1207 defstr - default replacement str */
1208
1209 if (defstr && strchr("-+?=", defstr[0]) == 0)
1210 {
1211 rc = WRDSE_UNDEF;
1212 defstr = NULL;
1213 }
1214 else
1215 {
1216 rc = wordsplit_find_env (wsp, str, i, &vptr);
1217 if (rc == WRDSE_OK)
1218 {
1219 if (vptr)
1220 {
1221 value = strdup (vptr);
1222 if (!value)
1223 rc = WRDSE_NOSPACE;
1224 }
1225 else
1226 rc = WRDSE_UNDEF;
1227 }
1228 else if (wsp->ws_flags & WRDSF_GETVAR)
1229 rc = wsp->ws_getvar (&value, str, i, wsp->ws_closure);
1230 else
1231 rc = WRDSE_UNDEF;
1232
1233 if (rc == WRDSE_OK
1234 && (!value || value[0] == 0)
1235 && defstr && defstr[-1] == ':')
1236 {
1237 free (value);
1238 rc = WRDSE_UNDEF;
1239 }
1240 }
1241
1242 switch (rc)
1243 {
1244 case WRDSE_OK:
1245 if (defstr && *defstr == '+')
1246 {
1247 size_t size = *pend - ++defstr;
1248
1249 rc = _wsplt_subsplit (wsp, &ws, defstr, size,
1250 WRDSF_NOSPLIT | WRDSF_WS | WRDSF_QUOTE |
1251 (wsp->ws_flags &
1252 (WRDSF_NOVAR | WRDSF_NOCMD)), 1);
1253 if (rc)
1254 return rc;
1255 free (value);
1256 value = ws.ws_wordv[0];
1257 ws.ws_wordv[0] = NULL;
1258 wordsplit_free (&ws);
1259 }
1260 break;
1261
1262 case WRDSE_UNDEF:
1263 if (defstr)
1264 {
1265 size_t size;
1266 if (*defstr == '-' || *defstr == '=')
1267 {
1268 size = *pend - ++defstr;
1269
1270 rc = _wsplt_subsplit (wsp, &ws, defstr, size,
1271 WRDSF_NOSPLIT | WRDSF_WS | WRDSF_QUOTE |
1272 (wsp->ws_flags &
1273 (WRDSF_NOVAR | WRDSF_NOCMD)),
1274 1);
1275 if (rc)
1276 return rc;
1277
1278 value = ws.ws_wordv[0];
1279 ws.ws_wordv[0] = NULL;
1280 wordsplit_free (&ws);
1281
1282 if (defstr[-1] == '=')
1283 wsplt_assign_var (wsp, str, i, value);
1284 }
1285 else
1286 {
1287 if (*defstr == '?')
1288 {
1289 size = *pend - ++defstr;
1290 if (size == 0)
1291 wsp->ws_error (_("%.*s: variable null or not set"),
1292 (int) i, str);
1293 else
1294 {
1295 rc = _wsplt_subsplit (wsp, &ws, defstr, size,
1296 WRDSF_NOSPLIT | WRDSF_WS |
1297 WRDSF_QUOTE |
1298 (wsp->ws_flags &
1299 (WRDSF_NOVAR | WRDSF_NOCMD)),
1300 1);
1301 if (rc == 0)
1302 wsp->ws_error ("%.*s: %s",
1303 (int) i, str, ws.ws_wordv[0]);
1304 else
1305 wsp->ws_error ("%.*s: %.*s",
1306 (int) i, str, (int) size, defstr);
1307 wordsplit_free (&ws);
1308 }
1309 }
1310 value = NULL;
1311 }
1312 }
1313 else if (wsp->ws_flags & WRDSF_UNDEF)
1314 {
1315 _wsplt_seterr (wsp, WRDSE_UNDEF);
1316 return 1;
1317 }
1318 else
1319 {
1320 if (wsp->ws_flags & WRDSF_WARNUNDEF)
1321 wsp->ws_error (_("warning: undefined variable `%.*s'"),
1322 (int) i, str);
1323 if (wsp->ws_flags & WRDSF_KEEPUNDEF)
1324 value = NULL;
1325 else
1326 {
1327 value = strdup ("");
1328 if (!value)
1329 return _wsplt_nomem (wsp);
1330 }
1331 }
1332 break;
1333
1334 case WRDSE_NOSPACE:
1335 return _wsplt_nomem (wsp);
1336
1337 case WRDSE_USERERR:
1338 if (wsp->ws_errno == WRDSE_USERERR)
1339 free (wsp->ws_usererr);
1340 wsp->ws_usererr = value;
1341 /* fall through */
1342 default:
1343 _wsplt_seterr (wsp, rc);
1344 return 1;
1345 }
1346
1347 if (value)
1348 {
1349 if (flg & _WSNF_QUOTE)
1350 {
1351 if (wsnode_new (wsp, &newnode))
1352 return 1;
1353 wsnode_insert (wsp, newnode, *ptail, 0);
1354 *ptail = newnode;
1355 newnode->flags = _WSNF_WORD | _WSNF_NOEXPAND | flg;
1356 newnode->v.word = value;
1357 }
1358 else if (*value == 0)
1359 {
1360 free (value);
1361 /* Empty string is a special case */
1362 if (wsnode_new (wsp, &newnode))
1363 return 1;
1364 wsnode_insert (wsp, newnode, *ptail, 0);
1365 *ptail = newnode;
1366 newnode->flags = _WSNF_NULL;
1367 }
1368 else
1369 {
1370 struct wordsplit ws;
1371 int rc;
1372
1373 rc = _wsplt_subsplit (wsp, &ws, value, strlen (value),
1374 WRDSF_NOVAR | WRDSF_NOCMD |
1375 WRDSF_QUOTE
1376 | (WSP_RETURN_DELIMS (wsp) ? WRDSF_RETURN_DELIMS : 0) ,
1377 0);
1378 free (value);
1379 if (rc)
1380 {
1381 _wsplt_seterr_sub (wsp, &ws);
1382 wordsplit_free (&ws);
1383 return 1;
1384 }
1385 wsnode_insert (wsp, ws.ws_head, *ptail, 0);
1386 *ptail = ws.ws_tail;
1387 ws.ws_head = ws.ws_tail = NULL;
1388 wordsplit_free (&ws);
1389 }
1390 }
1391 else if (wsp->ws_flags & WRDSF_KEEPUNDEF)
1392 {
1393 size_t size = *pend - start + 1;
1394
1395 if (wsnode_new (wsp, &newnode))
1396 return 1;
1397 wsnode_insert (wsp, newnode, *ptail, 0);
1398 *ptail = newnode;
1399 newnode->flags = _WSNF_WORD | _WSNF_NOEXPAND | flg;
1400 newnode->v.word = malloc (size + 1);
1401 if (!newnode->v.word)
1402 return _wsplt_nomem (wsp);
1403 memcpy (newnode->v.word, start, size);
1404 newnode->v.word[size] = 0;
1405 }
1406 else
1407 {
1408 if (wsnode_new (wsp, &newnode))
1409 return 1;
1410 wsnode_insert (wsp, newnode, *ptail, 0);
1411 *ptail = newnode;
1412 newnode->flags = _WSNF_NULL;
1413 }
1414 return 0;
1415}
1416
1417static int
1418begin_var_p (int c)
1419{
1420 return c == '{' || ISVARBEG (c);
1421}
1422
1423static int
1424node_expand (struct wordsplit *wsp, struct wordsplit_node *node,
1425 int (*beg_p) (int),
1426 int (*ws_exp_fn) (struct wordsplit *wsp,
1427 const char *str, size_t len,
1428 struct wordsplit_node **ptail,
1429 const char **pend,
1430 int flg))
1431{
1432 const char *str = wsnode_ptr (wsp, node);
1433 size_t slen = wsnode_len (node);
1434 const char *end = str + slen;
1435 const char *p;
1436 size_t off = 0;
1437 struct wordsplit_node *tail = node;
1438
1439 for (p = str; p < end; p++)
1440 {
1441 if (*p == '\\')
1442 {
1443 p++;
1444 continue;
1445 }
1446 if (*p == '$' && beg_p (p[1]))
1447 {
1448 size_t n = p - str;
1449
1450 if (tail != node)
1451 tail->flags |= _WSNF_JOIN;
1452 if (node_split_prefix (wsp, &tail, node, off, n, _WSNF_JOIN))
1453 return 1;
1454 p++;
1455 if (ws_exp_fn (wsp, p, slen - n, &tail, &p,
1456 node->flags & (_WSNF_JOIN | _WSNF_QUOTE)))
1457 return 1;
1458 off += p - str + 1;
1459 str = p + 1;
1460 }
1461 }
1462 if (p > str)
1463 {
1464 if (tail != node)
1465 tail->flags |= _WSNF_JOIN;
1466 if (node_split_prefix (wsp, &tail, node, off, p - str,
1467 node->flags & (_WSNF_JOIN|_WSNF_QUOTE)))
1468 return 1;
1469 }
1470 if (tail != node)
1471 {
1472 wsnode_remove (wsp, node);
1473 wsnode_free (node);
1474 }
1475 return 0;
1476}
1477
1478/* Remove NULL nodes from the list */
1479static void
1480wsnode_nullelim (struct wordsplit *wsp)
1481{
1482 struct wordsplit_node *p;
1483
1484 for (p = wsp->ws_head; p;)
1485 {
1486 struct wordsplit_node *next = p->next;
1487 if (p->flags & _WSNF_DELIM && p->prev)
1488 p->prev->flags &= ~_WSNF_JOIN;
1489 if (p->flags & _WSNF_NULL)
1490 {
1491 wsnode_remove (wsp, p);
1492 wsnode_free (p);
1493 }
1494 p = next;
1495 }
1496}
1497
1498static int
1499wordsplit_varexp (struct wordsplit *wsp)
1500{
1501 struct wordsplit_node *p;
1502
1503 for (p = wsp->ws_head; p;)
1504 {
1505 struct wordsplit_node *next = p->next;
1506 if (!(p->flags & (_WSNF_NOEXPAND|_WSNF_DELIM)))
1507 if (node_expand (wsp, p, begin_var_p, expvar))
1508 return 1;
1509 p = next;
1510 }
1511
1512 wsnode_nullelim (wsp);
1513 return 0;
1514}
1515
1516static int
1517begin_cmd_p (int c)
1518{
1519 return c == '(';
1520}
1521
1522static int
1523expcmd (struct wordsplit *wsp, const char *str, size_t len,
1524 struct wordsplit_node **ptail, const char **pend, int flg)
1525{
1526 int rc;
1527 size_t j;
1528 char *value;
1529 struct wordsplit_node *newnode;
1530 struct wordsplit ws;
1531
1532 str++;
1533 len--;
1534
1535 if (find_closing_paren (str, 0, len, &j, "()"))
1536 {
1537 _wsplt_seterr (wsp, WRDSE_PAREN);
1538 return 1;
1539 }
1540
1541 *pend = str + j;
1542 rc = _wsplt_subsplit (wsp, &ws, str, j, WRDSF_WS | WRDSF_QUOTE, 1);
1543 if (rc)
1544 {
1545 _wsplt_seterr_sub (wsp, &ws);
1546 wordsplit_free (&ws);
1547 return 1;
1548 }
1549 rc = wsp->ws_command (&value, str, j, ws.ws_wordv, wsp->ws_closure);
1550 wordsplit_free (&ws);
1551
1552 if (rc == WRDSE_NOSPACE)
1553 return _wsplt_nomem (wsp);
1554 else if (rc)
1555 {
1556 if (rc == WRDSE_USERERR)
1557 {
1558 if (wsp->ws_errno == WRDSE_USERERR)
1559 free (wsp->ws_usererr);
1560 wsp->ws_usererr = value;
1561 }
1562 _wsplt_seterr (wsp, rc);
1563 return 1;
1564 }
1565
1566 if (value)
1567 {
1568 if (flg & _WSNF_QUOTE)
1569 {
1570 if (wsnode_new (wsp, &newnode))
1571 return 1;
1572 wsnode_insert (wsp, newnode, *ptail, 0);
1573 *ptail = newnode;
1574 newnode->flags = _WSNF_WORD | _WSNF_NOEXPAND | flg;
1575 newnode->v.word = value;
1576 }
1577 else if (*value == 0)
1578 {
1579 free (value);
1580 /* Empty string is a special case */
1581 if (wsnode_new (wsp, &newnode))
1582 return 1;
1583 wsnode_insert (wsp, newnode, *ptail, 0);
1584 *ptail = newnode;
1585 newnode->flags = _WSNF_NULL;
1586 }
1587 else
1588 {
1589 struct wordsplit ws;
1590 int rc;
1591
1592 rc = _wsplt_subsplit (wsp, &ws, value, strlen (value),
1593 WRDSF_NOVAR | WRDSF_NOCMD
1594 | WRDSF_WS | WRDSF_QUOTE
1595 | (WSP_RETURN_DELIMS (wsp) ? WRDSF_RETURN_DELIMS : 0),
1596 0);
1597 free (value);
1598 if (rc)
1599 {
1600 _wsplt_seterr_sub (wsp, &ws);
1601 wordsplit_free (&ws);
1602 return 1;
1603 }
1604 wsnode_insert (wsp, ws.ws_head, *ptail, 0);
1605 *ptail = ws.ws_tail;
1606 ws.ws_head = ws.ws_tail = NULL;
1607 wordsplit_free (&ws);
1608 }
1609 }
1610 else
1611 {
1612 if (wsnode_new (wsp, &newnode))
1613 return 1;
1614 wsnode_insert (wsp, newnode, *ptail, 0);
1615 *ptail = newnode;
1616 newnode->flags = _WSNF_NULL;
1617 }
1618 return 0;
1619}
1620
1621static int
1622wordsplit_cmdexp (struct wordsplit *wsp)
1623{
1624 struct wordsplit_node *p;
1625
1626 for (p = wsp->ws_head; p;)
1627 {
1628 struct wordsplit_node *next = p->next;
1629 if (!(p->flags & _WSNF_NOEXPAND))
1630 if (node_expand (wsp, p, begin_cmd_p, expcmd))
1631 return 1;
1632 p = next;
1633 }
1634
1635 wsnode_nullelim (wsp);
1636 return 0;
1637}
1638
1639/* Strip off any leading and trailing whitespace. This function is called
1640 right after the initial scanning, therefore it assumes that every
1641 node in the list is a text reference node. */
1642static int
1643wordsplit_trimws (struct wordsplit *wsp)
1644{
1645 struct wordsplit_node *p;
1646
1647 for (p = wsp->ws_head; p; p = p->next)
1648 {
1649 size_t n;
1650
1651 if (!(p->flags & _WSNF_QUOTE))
1652 {
1653 /* Skip leading whitespace: */
1654 for (n = p->v.segm.beg; n < p->v.segm.end && ISWS (wsp->ws_input[n]);
1655 n++)
1656 ;
1657 p->v.segm.beg = n;
1658 }
1659
1660 while (p->next && (p->flags & _WSNF_JOIN))
1661 p = p->next;
1662
1663 if (p->flags & _WSNF_QUOTE)
1664 continue;
1665
1666 /* Trim trailing whitespace */
1667 for (n = p->v.segm.end;
1668 n > p->v.segm.beg && ISWS (wsp->ws_input[n - 1]); n--);
1669 p->v.segm.end = n;
1670 if (p->v.segm.beg == p->v.segm.end)
1671 p->flags |= _WSNF_NULL;
1672 }
1673
1674 wsnode_nullelim (wsp);
1675 return 0;
1676}
1677
1678static int
1679wordsplit_tildexpand (struct wordsplit *wsp)
1680{
1681 struct wordsplit_node *p;
1682 char *uname = NULL;
1683 size_t usize = 0;
1684
1685 for (p = wsp->ws_head; p; p = p->next)
1686 {
1687 const char *str;
1688
1689 if (p->flags & _WSNF_QUOTE)
1690 continue;
1691
1692 str = wsnode_ptr (wsp, p);
1693 if (str[0] == '~')
1694 {
1695 size_t i, size, dlen;
1696 size_t slen = wsnode_len (p);
1697 struct passwd *pw;
1698 char *newstr;
1699
1700 for (i = 1; i < slen && str[i] != '/'; i++)
1701 ;
1702 if (i == slen)
1703 continue;
1704 if (i > 1)
1705 {
1706 if (i > usize)
1707 {
1708 char *p = realloc (uname, i);
1709 if (!p)
1710 {
1711 free (uname);
1712 return _wsplt_nomem (wsp);
1713 }
1714 uname = p;
1715 usize = i;
1716 }
1717 --i;
1718 memcpy (uname, str + 1, i);
1719 uname[i] = 0;
1720 pw = getpwnam (uname);
1721 }
1722 else
1723 pw = getpwuid (getuid ());
1724
1725 if (!pw)
1726 continue;
1727
1728 dlen = strlen (pw->pw_dir);
1729 size = slen - i + dlen;
1730 newstr = malloc (size);
1731 if (!newstr)
1732 {
1733 free (uname);
1734 return _wsplt_nomem (wsp);
1735 }
1736 --size;
1737
1738 memcpy (newstr, pw->pw_dir, dlen);
1739 memcpy (newstr + dlen, str + i + 1, slen - i - 1);
1740 newstr[size] = 0;
1741 if (p->flags & _WSNF_WORD)
1742 free (p->v.word);
1743 p->v.word = newstr;
1744 p->flags |= _WSNF_WORD;
1745 }
1746 }
1747 free (uname);
1748 return 0;
1749}
1750
1751static int
1752isglob (const char *s, int l)
1753{
1754 while (l--)
1755 {
1756 if (strchr ("*?[", *s++))
1757 return 1;
1758 }
1759 return 0;
1760}
1761
1762static int
1763wordsplit_pathexpand (struct wordsplit *wsp)
1764{
1765 struct wordsplit_node *p, *next;
1766 char *pattern = NULL;
1767 size_t patsize = 0;
1768 size_t slen;
1769 int flags = 0;
1770
1771#ifdef GLOB_PERIOD
1772 if (wsp->ws_options & WRDSO_DOTGLOB)
1773 flags = GLOB_PERIOD;
1774#endif
1775
1776 for (p = wsp->ws_head; p; p = next)
1777 {
1778 const char *str;
1779
1780 next = p->next;
1781
1782 if (p->flags & _WSNF_QUOTE)
1783 continue;
1784
1785 str = wsnode_ptr (wsp, p);
1786 slen = wsnode_len (p);
1787
1788 if (isglob (str, slen))
1789 {
1790 int i;
1791 glob_t g;
1792 struct wordsplit_node *prev;
1793
1794 if (slen + 1 > patsize)
1795 {
1796 char *p = realloc (pattern, slen + 1);
1797 if (!p)
1798 return _wsplt_nomem (wsp);
1799 pattern = p;
1800 patsize = slen + 1;
1801 }
1802 memcpy (pattern, str, slen);
1803 pattern[slen] = 0;
1804
1805 switch (glob (pattern, flags, NULL, &g))
1806 {
1807 case 0:
1808 break;
1809
1810 case GLOB_NOSPACE:
1811 free (pattern);
1812 return _wsplt_nomem (wsp);
1813
1814 case GLOB_NOMATCH:
1815 if (wsp->ws_options & WRDSO_NULLGLOB)
1816 {
1817 wsnode_remove (wsp, p);
1818 wsnode_free (p);
1819 }
1820 else if (wsp->ws_options & WRDSO_FAILGLOB)
1821 {
1822 char buf[128];
1823 if (wsp->ws_errno == WRDSE_USERERR)
1824 free (wsp->ws_usererr);
1825 snprintf (buf, sizeof (buf), _("no files match pattern %s"),
1826 pattern);
1827 free (pattern);
1828 wsp->ws_usererr = strdup (buf);
1829 if (!wsp->ws_usererr)
1830 return _wsplt_nomem (wsp);
1831 else
1832 return _wsplt_seterr (wsp, WRDSE_USERERR);
1833 }
1834 continue;
1835
1836 default:
1837 free (pattern);
1838 return _wsplt_seterr (wsp, WRDSE_GLOBERR);
1839 }
1840
1841 prev = p;
1842 for (i = 0; i < g.gl_pathc; i++)
1843 {
1844 struct wordsplit_node *newnode;
1845 char *newstr;
1846
1847 if (wsnode_new (wsp, &newnode))
1848 return 1;
1849 newstr = strdup (g.gl_pathv[i]);
1850 if (!newstr)
1851 return _wsplt_nomem (wsp);
1852 newnode->v.word = newstr;
1853 newnode->flags |= _WSNF_WORD|_WSNF_QUOTE;
1854 wsnode_insert (wsp, newnode, prev, 0);
1855 prev = newnode;
1856 }
1857 globfree (&g);
1858
1859 wsnode_remove (wsp, p);
1860 wsnode_free (p);
1861 }
1862 }
1863 free (pattern);
1864 return 0;
1865}
1866
1867static int
1868skip_sed_expr (const char *command, size_t i, size_t len)
1869{
1870 int state;
1871
1872 do
1873 {
1874 int delim;
1875
1876 if (command[i] == ';')
1877 i++;
1878 if (!(command[i] == 's' && i + 3 < len && ISPUNCT (command[i + 1])))
1879 break;
1880
1881 delim = command[++i];
1882 state = 1;
1883 for (i++; i < len; i++)
1884 {
1885 if (state == 3)
1886 {
1887 if (command[i] == delim || !ISALNUM (command[i]))
1888 break;
1889 }
1890 else if (command[i] == '\\')
1891 i++;
1892 else if (command[i] == delim)
1893 state++;
1894 }
1895 }
1896 while (state == 3 && i < len && command[i] == ';');
1897 return i;
1898}
1899
1900/* wsp->ws_endp points to a delimiter character. If RETURN_DELIMS
1901 is true, return its value, otherwise return the index past it. */
1902static inline size_t
1903skip_delim_internal (struct wordsplit *wsp, int return_delims)
1904{
1905 return return_delims ? wsp->ws_endp : wsp->ws_endp + 1;
1906}
1907
1908static inline size_t
1909skip_delim (struct wordsplit *wsp)
1910{
1911 return skip_delim_internal (wsp, WSP_RETURN_DELIMS (wsp));
1912}
1913
1914static inline size_t
1915skip_delim_real (struct wordsplit *wsp)
1916{
1917 return skip_delim_internal (wsp, wsp->ws_flags & WRDSF_RETURN_DELIMS);
1918}
1919
1920#define _WRDS_EOF 0
1921#define _WRDS_OK 1
1922#define _WRDS_ERR 2
1923
1924static int
1925scan_qstring (struct wordsplit *wsp, size_t start, size_t *end)
1926{
1927 size_t j;
1928 const char *command = wsp->ws_input;
1929 size_t len = wsp->ws_len;
1930 char q = command[start];
1931
1932 for (j = start + 1; j < len && command[j] != q; j++)
1933 if (q == '"' && command[j] == '\\')
1934 j++;
1935 if (j < len && command[j] == q)
1936 {
1937 int flags = _WSNF_QUOTE | _WSNF_EMPTYOK;
1938 if (q == '\'')
1939 flags |= _WSNF_NOEXPAND;
1940 if (wordsplit_add_segm (wsp, start + 1, j, flags))
1941 return _WRDS_ERR;
1942 *end = j;
1943 }
1944 else
1945 {
1946 wsp->ws_endp = start;
1947 _wsplt_seterr (wsp, WRDSE_QUOTE);
1948 return _WRDS_ERR;
1949 }
1950 return 0;
1951}
1952
1953static int
1954scan_word (struct wordsplit *wsp, size_t start, int consume_all)
1955{
1956 size_t len = wsp->ws_len;
1957 const char *command = wsp->ws_input;
1958 const char *comment = wsp->ws_comment;
1959 int join = 0;
1960 int flags = 0;
1961 struct wordsplit_node *np = wsp->ws_tail;
1962
1963 size_t i = start;
1964
1965 if (i >= len)
1966 {
1967 wsp->ws_errno = WRDSE_EOF;
1968 return _WRDS_EOF;
1969 }
1970
1971 start = i;
1972
1973 if (wsp->ws_flags & WRDSF_SED_EXPR
1974 && command[i] == 's' && i + 3 < len && ISPUNCT (command[i + 1]))
1975 {
1976 flags = _WSNF_SEXP;
1977 i = skip_sed_expr (command, i, len);
1978 }
1979 else if (consume_all || !ISDELIM (wsp, command[i]))
1980 {
1981 while (i < len)
1982 {
1983 if (comment && strchr (comment, command[i]) != NULL)
1984 {
1985 size_t j;
1986 for (j = i + 1; j < len && command[j] != '\n'; j++)
1987 ;
1988 if (wordsplit_add_segm (wsp, start, i, 0))
1989 return _WRDS_ERR;
1990 wsp->ws_endp = j;
1991 return _WRDS_OK;
1992 }
1993
1994 if (wsp->ws_flags & WRDSF_QUOTE)
1995 {
1996 if (command[i] == '\\')
1997 {
1998 if (++i == len)
1999 break;
2000 i++;
2001 continue;
2002 }
2003
2004 if (((wsp->ws_flags & WRDSF_SQUOTE) && command[i] == '\'') ||
2005 ((wsp->ws_flags & WRDSF_DQUOTE) && command[i] == '"'))
2006 {
2007 if (join && wsp->ws_tail)
2008 wsp->ws_tail->flags |= _WSNF_JOIN;
2009 if (wordsplit_add_segm (wsp, start, i, _WSNF_JOIN))
2010 return _WRDS_ERR;
2011 if (scan_qstring (wsp, i, &i))
2012 return _WRDS_ERR;
2013 start = i + 1;
2014 join = 1;
2015 }
2016 }
2017
2018 if (command[i] == '$')
2019 {
2020 if (!(wsp->ws_flags & WRDSF_NOVAR)
2021 && command[i+1] == '{'
2022 && find_closing_paren (command, i + 2, len, &i, "{}") == 0)
2023 continue;
2024 if (!(wsp->ws_flags & WRDSF_NOCMD)
2025 && command[i+1] == '('
2026 && find_closing_paren (command, i + 2, len, &i, "()") == 0)
2027 continue;
2028 }
2029
2030 if (!consume_all && ISDELIM (wsp, command[i]))
2031 break;
2032 else
2033 i++;
2034 }
2035 }
2036 else if (WSP_RETURN_DELIMS (wsp))
2037 {
2038 i++;
2039 flags |= _WSNF_DELIM;
2040 }
2041 else if (!(wsp->ws_flags & WRDSF_SQUEEZE_DELIMS))
2042 flags |= _WSNF_EMPTYOK;
2043
2044 if (join && i > start && wsp->ws_tail)
2045 wsp->ws_tail->flags |= _WSNF_JOIN;
2046 if (wordsplit_add_segm (wsp, start, i, flags))
2047 return _WRDS_ERR;
2048 wsp->ws_endp = i;
2049 if (wsp->ws_flags & WRDSF_INCREMENTAL)
2050 return _WRDS_EOF;
2051
2052 if (consume_all)
2053 {
2054 if (!np)
2055 np = wsp->ws_head;
2056 while (np)
2057 {
2058 np->flags |= _WSNF_QUOTE;
2059 np = np->next;
2060 }
2061 }
2062
2063 return _WRDS_OK;
2064}
2065
2066#define to_num(c) \
2067 (ISDIGIT(c) ? c - '0' : (ISXDIGIT(c) ? toupper(c) - 'A' + 10 : 255 ))
2068
2069static int
2070xtonum (int *pval, const char *src, int base, int cnt)
2071{
2072 int i, val;
2073
2074 for (i = 0, val = 0; i < cnt; i++, src++)
2075 {
2076 int n = *(unsigned char *) src;
2077 if (n > 127 || (n = to_num (n)) >= base)
2078 break;
2079 val = val * base + n;
2080 }
2081 *pval = val;
2082 return i;
2083}
2084
2085size_t
2086wordsplit_c_quoted_length (const char *str, int quote_hex, int *quote)
2087{
2088 size_t len = 0;
2089
2090 *quote = 0;
2091 for (; *str; str++)
2092 {
2093 if (strchr (" \"", *str))
2094 *quote = 1;
2095
2096 if (*str == ' ')
2097 len++;
2098 else if (*str == '"')
2099 len += 2;
2100 else if (*str != '\t' && *str != '\\' && ISPRINT (*str))
2101 len++;
2102 else if (quote_hex)
2103 len += 3;
2104 else
2105 {
2106 if (wordsplit_c_quote_char (*str))
2107 len += 2;
2108 else
2109 len += 4;
2110 }
2111 }
2112 return len;
2113}
2114
2115static int
2116wsplt_unquote_char (const char *transtab, int c)
2117{
2118 while (*transtab && transtab[1])
2119 {
2120 if (*transtab++ == c)
2121 return *transtab;
2122 ++transtab;
2123 }
2124 return 0;
2125}
2126
2127static int
2128wsplt_quote_char (const char *transtab, int c)
2129{
2130 for (; *transtab && transtab[1]; transtab += 2)
2131 {
2132 if (transtab[1] == c)
2133 return *transtab;
2134 }
2135 return 0;
2136}
2137
2138int
2139wordsplit_c_unquote_char (int c)
2140{
2141 return wsplt_unquote_char (wordsplit_c_escape_tab, c);
2142}
2143
2144int
2145wordsplit_c_quote_char (int c)
2146{
2147 return wsplt_quote_char (wordsplit_c_escape_tab, c);
2148}
2149
2150void
2151wordsplit_string_unquote_copy (struct wordsplit *ws, int inquote,
2152 char *dst, const char *src, size_t n)
2153{
2154 int i = 0;
2155 int c;
2156
2157 inquote = !!inquote;
2158 while (i < n)
2159 {
2160 if (src[i] == '\\')
2161 {
2162 ++i;
2163 if (WRDSO_ESC_TEST (ws, inquote, WRDSO_XESC)
2164 && (src[i] == 'x' || src[i] == 'X'))
2165 {
2166 if (n - i < 2)
2167 {
2168 *dst++ = '\\';
2169 *dst++ = src[i++];
2170 }
2171 else
2172 {
2173 int off = xtonum (&c, src + i + 1,
2174 16, 2);
2175 if (off == 0)
2176 {
2177 *dst++ = '\\';
2178 *dst++ = src[i++];
2179 }
2180 else
2181 {
2182 *dst++ = c;
2183 i += off + 1;
2184 }
2185 }
2186 }
2187 else if (WRDSO_ESC_TEST (ws, inquote, WRDSO_OESC)
2188 && (unsigned char) src[i] < 128 && ISDIGIT (src[i]))
2189 {
2190 if (n - i < 1)
2191 {
2192 *dst++ = '\\';
2193 *dst++ = src[i++];
2194 }
2195 else
2196 {
2197 int off = xtonum (&c, src + i, 8, 3);
2198 if (off == 0)
2199 {
2200 *dst++ = '\\';
2201 *dst++ = src[i++];
2202 }
2203 else
2204 {
2205 *dst++ = c;
2206 i += off;
2207 }
2208 }
2209 }
2210 else if ((c = wsplt_unquote_char (ws->ws_escape[inquote], src[i])))
2211 {
2212 *dst++ = c;
2213 ++i;
2214 }
2215 else
2216 {
2217 if (WRDSO_ESC_TEST (ws, inquote, WRDSO_BSKEEP))
2218 *dst++ = '\\';
2219 *dst++ = src[i++];
2220 }
2221 }
2222 else
2223 *dst++ = src[i++];
2224 }
2225 *dst = 0;
2226}
2227
2228void
2229wordsplit_c_quote_copy (char *dst, const char *src, int quote_hex)
2230{
2231 for (; *src; src++)
2232 {
2233 if (*src == '"')
2234 {
2235 *dst++ = '\\';
2236 *dst++ = *src;
2237 }
2238 else if (*src != '\t' && *src != '\\' && ISPRINT (*src))
2239 *dst++ = *src;
2240 else
2241 {
2242 char tmp[4];
2243
2244 if (quote_hex)
2245 {
2246 snprintf (tmp, sizeof tmp, "%%%02X", *(unsigned char *) src);
2247 memcpy (dst, tmp, 3);
2248 dst += 3;
2249 }
2250 else
2251 {
2252 int c = wordsplit_c_quote_char (*src);
2253 *dst++ = '\\';
2254 if (c)
2255 *dst++ = c;
2256 else
2257 {
2258 snprintf (tmp, sizeof tmp, "%03o", *(unsigned char *) src);
2259 memcpy (dst, tmp, 3);
2260 dst += 3;
2261 }
2262 }
2263 }
2264 }
2265}
2266
2267
2268/* This structure describes a single expansion phase */
2269struct exptab
2270{
2271 char const *descr; /* Textual description (for debugging) */
2272 int flag; /* WRDSF_ bit that controls this phase */
2273 int opt; /* Entry-specific options (see EXPOPT_ flags below */
2274 int (*expansion) (struct wordsplit *wsp); /* expansion function */
2275};
2276
2277/* The following options control expansions: */
2278/* Normally the exptab entry is run if its flag bit is set in struct
2279 wordsplit. The EXPOPT_NEG option negates this test so that expansion
2280 is performed if its associated flag bit is not set in struct wordsplit. */
2281#define EXPOPT_NEG 0x01
2282/* All bits in flag must be set in order for entry to match */
2283#define EXPORT_ALLOF 0x02
2284/* Coalesce the input list before running the expansion. */
2285#define EXPOPT_COALESCE 0x04
2286
2287static struct exptab exptab[] = {
2288 { N_("WS trimming"), WRDSF_WS, 0,
2289 wordsplit_trimws },
2290 { N_("command substitution"), WRDSF_NOCMD, EXPOPT_NEG|EXPOPT_COALESCE,
2291 wordsplit_cmdexp },
2292 { N_("coalesce list"), 0, EXPOPT_NEG|EXPOPT_COALESCE,
2293 NULL },
2294 { N_("tilde expansion"), WRDSF_PATHEXPAND, 0,
2295 wordsplit_tildexpand },
2296 { N_("variable expansion"), WRDSF_NOVAR, EXPOPT_NEG,
2297 wordsplit_varexp },
2298 { N_("quote removal"), 0, EXPOPT_NEG,
2299 wsnode_quoteremoval },
2300 { N_("coalesce list"), 0, EXPOPT_NEG|EXPOPT_COALESCE,
2301 NULL },
2302 { N_("path expansion"), WRDSF_PATHEXPAND, 0,
2303 wordsplit_pathexpand },
2304 { NULL }
2305};
2306
2307static inline int
2308exptab_matches(struct exptab *p, struct wordsplit *wsp)
2309{
2310 int result;
2311
2312 result = (wsp->ws_flags & p->flag);
2313 if (p->opt & EXPORT_ALLOF)
2314 result = result == p->flag;
2315 if (p->opt & EXPOPT_NEG)
2316 result = !result;
2317
2318 return result;
2319}
2320
2321static int
2322wordsplit_process_list (struct wordsplit *wsp, size_t start)
2323{
2324 struct exptab *p;
2325
2326 if (wsp->ws_flags & WRDSF_SHOWDBG)
2327 wsp->ws_debug (_("(%02d) Input:%.*s;"),
2328 wsp->ws_lvl, (int) wsp->ws_len, wsp->ws_input);
2329
2330 if ((wsp->ws_flags & WRDSF_NOSPLIT)
2331 || ((wsp->ws_options & WRDSO_MAXWORDS)
2332 && wsp->ws_wordi + 1 == wsp->ws_maxwords))
2333 {
2334 /* Treat entire input as a single word */
2335 if (scan_word (wsp, start, 1) == _WRDS_ERR)
2336 return wsp->ws_errno;
2337 }
2338 else
2339 {
2340 int rc;
2341
2342 while ((rc = scan_word (wsp, start, 0)) == _WRDS_OK)
2343 start = skip_delim (wsp);
2344 /* Make sure tail element is not joinable */
2345 if (wsp->ws_tail)
2346 wsp->ws_tail->flags &= ~_WSNF_JOIN;
2347 if (rc == _WRDS_ERR)
2348 return wsp->ws_errno;
2349 }
2350
2351 if (wsp->ws_flags & WRDSF_SHOWDBG)
2352 {
2353 wsp->ws_debug ("(%02d) %s", wsp->ws_lvl, _("Initial list:"));
2354 wordsplit_dump_nodes (wsp);
2355 }
2356
2357 for (p = exptab; p->descr; p++)
2358 {
2359 if (exptab_matches(p, wsp))
2360 {
2361 if (p->opt & EXPOPT_COALESCE)
2362 {
2363 if (wsnode_coalesce (wsp))
2364 break;
2365 if (wsp->ws_flags & WRDSF_SHOWDBG)
2366 {
2367 wsp->ws_debug ("(%02d) %s", wsp->ws_lvl,
2368 _("Coalesced list:"));
2369 wordsplit_dump_nodes (wsp);
2370 }
2371 }
2372 if (p->expansion)
2373 {
2374 if (p->expansion (wsp))
2375 break;
2376 if (wsp->ws_flags & WRDSF_SHOWDBG)
2377 {
2378 wsp->ws_debug ("(%02d) %s", wsp->ws_lvl, _(p->descr));
2379 wordsplit_dump_nodes (wsp);
2380 }
2381 }
2382 }
2383 }
2384 return wsp->ws_errno;
2385}
2386
2387static int
2388wordsplit_run (const char *command, size_t length, struct wordsplit *wsp,
2389 int flags, int lvl)
2390{
2391 int rc;
2392 size_t start;
2393
2394 if (!command)
2395 {
2396 if (!(flags & WRDSF_INCREMENTAL))
2397 return _wsplt_seterr (wsp, WRDSE_USAGE);
2398
2399 if (wsp->ws_head)
2400 return wordsplit_finish (wsp);
2401
2402 start = skip_delim_real (wsp);
2403 if (wsp->ws_endp == wsp->ws_len)
2404 return _wsplt_seterr (wsp, WRDSE_NOINPUT);
2405
2406 wsp->ws_flags |= WRDSF_REUSE;
2407 wordsplit_init0 (wsp);
2408 }
2409 else
2410 {
2411 start = 0;
2412 rc = wordsplit_init (wsp, command, length, flags);
2413 if (rc)
2414 return rc;
2415 wsp->ws_lvl = lvl;
2416 }
2417
2418 rc = wordsplit_process_list (wsp, start);
2419 if (rc)
2420 return rc;
2421 return wordsplit_finish (wsp);
2422}
2423
2424int
2425wordsplit_len (const char *command, size_t length, struct wordsplit *wsp,
2426 int flags)
2427{
2428 return wordsplit_run (command, length, wsp, flags, 0);
2429}
2430
2431int
2432wordsplit (const char *command, struct wordsplit *ws, int flags)
2433{
2434 return wordsplit_len (command, command ? strlen (command) : 0, ws, flags);
2435}
2436
2437void
2438wordsplit_free_words (struct wordsplit *ws)
2439{
2440 size_t i;
2441
2442 for (i = 0; i < ws->ws_wordc; i++)
2443 {
2444 char *p = ws->ws_wordv[ws->ws_offs + i];
2445 if (p)
2446 {
2447 free (p);
2448 ws->ws_wordv[ws->ws_offs + i] = NULL;
2449 }
2450 }
2451 ws->ws_wordc = 0;
2452}
2453
2454void
2455wordsplit_free_envbuf (struct wordsplit *ws)
2456{
2457 if (ws->ws_flags & WRDSF_NOCMD)
2458 return;
2459 if (ws->ws_envbuf)
2460 {
2461 size_t i;
2462
2463 for (i = 0; ws->ws_envbuf[i]; i++)
2464 free (ws->ws_envbuf[i]);
2465 free (ws->ws_envbuf);
2466 ws->ws_envidx = ws->ws_envsiz = 0;
2467 ws->ws_envbuf = NULL;
2468 }
2469}
2470
2471void
2472wordsplit_clearerr (struct wordsplit *ws)
2473{
2474 if (ws->ws_errno == WRDSE_USERERR)
2475 free (ws->ws_usererr);
2476 ws->ws_usererr = NULL;
2477 ws->ws_errno = WRDSE_OK;
2478}
2479
2480void
2481wordsplit_free (struct wordsplit *ws)
2482{
2483 wordsplit_free_nodes (ws);
2484 wordsplit_free_words (ws);
2485 free (ws->ws_wordv);
2486 ws->ws_wordv = NULL;
2487 wordsplit_free_envbuf (ws);
2488}
2489
2490int
2491wordsplit_get_words (struct wordsplit *ws, size_t *wordc, char ***wordv)
2492{
2493 char **p = realloc (ws->ws_wordv,
2494 (ws->ws_wordc + 1) * sizeof (ws->ws_wordv[0]));
2495 if (!p)
2496 return -1;
2497 *wordv = p;
2498 *wordc = ws->ws_wordc;
2499
2500 ws->ws_wordv = NULL;
2501 ws->ws_wordc = 0;
2502 ws->ws_wordn = 0;
2503
2504 return 0;
2505}
2506
2507const char *_wordsplit_errstr[] = {
2508 N_("no error"),
2509 N_("missing closing quote"),
2510 N_("memory exhausted"),
2511 N_("invalid wordsplit usage"),
2512 N_("unbalanced curly brace"),
2513 N_("undefined variable"),
2514 N_("input exhausted"),
2515 N_("unbalanced parenthesis"),
2516 N_("globbing error")
2517};
2518int _wordsplit_nerrs =
2519 sizeof (_wordsplit_errstr) / sizeof (_wordsplit_errstr[0]);
2520
2521const char *
2522wordsplit_strerror (struct wordsplit *ws)
2523{
2524 if (ws->ws_errno == WRDSE_USERERR)
2525 return ws->ws_usererr;
2526 if (ws->ws_errno < _wordsplit_nerrs)
2527 return _wordsplit_errstr[ws->ws_errno];
2528 return N_("unknown error");
2529}
2530
2531void
2532wordsplit_perror (struct wordsplit *wsp)
2533{
2534 switch (wsp->ws_errno)
2535 {
2536 case WRDSE_QUOTE:
2537 wsp->ws_error (_("missing closing %c (start near #%lu)"),
2538 wsp->ws_input[wsp->ws_endp],
2539 (unsigned long) wsp->ws_endp);
2540 break;
2541
2542 default:
2543 wsp->ws_error ("%s", wordsplit_strerror (wsp));
2544 }
2545}
2546
diff --git a/src/wordsplit.h b/src/wordsplit.h
deleted file mode 100644
index 1a047f7..0000000
--- a/src/wordsplit.h
+++ b/dev/null
@@ -1,271 +0,0 @@
1/* wordsplit - a word splitter
2 Copyright (C) 2009-2018 Sergey Poznyakoff
3
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation; either version 3 of the License, or (at your
7 option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with this program. If not, see <http://www.gnu.org/licenses/>. */
16
17#ifndef __WORDSPLIT_H
18#define __WORDSPLIT_H
19
20#include <stddef.h>
21
22typedef struct wordsplit wordsplit_t;
23
24/* Structure used to direct the splitting. Members marked with [Input]
25 can be defined before calling wordsplit(), those marked with [Output]
26 provide return values when the function returns. If neither mark is
27 used, the member is internal and must not be used by the caller.
28
29 In the comments below, the identifiers in parentheses indicate bits that
30 must be set (or unset, if starting with !) in ws_flags (if starting with
31 WRDSF_) or ws_options (if starting with WRDSO_) to initialize or use the
32 given member.
33
34 If not redefined explicitly, most of them are set to some reasonable
35 default value upon entry to wordsplit(). */
36struct wordsplit
37{
38 size_t ws_wordc; /* [Output] Number of words in ws_wordv. */
39 char **ws_wordv; /* [Output] Array of parsed out words. */
40 size_t ws_offs; /* [Input] (WRDSF_DOOFFS) Number of initial
41 elements in ws_wordv to fill with NULLs. */
42 size_t ws_wordn; /* Number of elements ws_wordv can accomodate. */
43 int ws_flags; /* [Input] Flags passed to wordsplit. */
44 int ws_options; /* [Input] (WRDSF_OPTIONS)
45 Additional options. */
46 size_t ws_maxwords; /* [Input] (WRDSO_MAXWORDS) Return at most that
47 many words */
48 size_t ws_wordi; /* [Output] (WRDSF_INCREMENTAL) Total number of
49 words returned so far */
50
51 const char *ws_delim; /* [Input] (WRDSF_DELIM) Word delimiters. */
52 const char *ws_comment; /* [Input] (WRDSF_COMMENT) Comment characters. */
53 const char *ws_escape[2]; /* [Input] (WRDSF_ESCAPE) Characters to be escaped
54 with backslash. */
55 void (*ws_alloc_die) (wordsplit_t *wsp);
56 /* [Input] (WRDSF_ALLOC_DIE) Function called when
57 out of memory. Must not return. */
58 void (*ws_error) (const char *, ...)
59 __attribute__ ((__format__ (__printf__, 1, 2)));
60 /* [Input] (WRDSF_ERROR) Function used for error
61 reporting */
62 void (*ws_debug) (const char *, ...)
63 __attribute__ ((__format__ (__printf__, 1, 2)));
64 /* [Input] (WRDSF_DEBUG) Function used for debug
65 output. */
66 const char **ws_env; /* [Input] (WRDSF_ENV, !WRDSF_NOVAR) Array of
67 environment variables. */
68
69 char **ws_envbuf;
70 size_t ws_envidx;
71 size_t ws_envsiz;
72
73 int (*ws_getvar) (char **ret, const char *var, size_t len, void *clos);
74 /* [Input] (WRDSF_GETVAR, !WRDSF_NOVAR) Looks up
75 the name VAR (LEN bytes long) in the table of
76 variables and if found returns in memory
77 location pointed to by RET the value of that
78 variable. Returns WRDSE_OK (0) on success,
79 and an error code (see WRDSE_* defines below)
80 on error. User-specific errors can be returned
81 by storing the error diagnostic string in RET
82 and returning WRDSE_USERERR.
83 Whatever is stored in RET, it must be allocated
84 using malloc(3). */
85 void *ws_closure; /* [Input] (WRDSF_CLOSURE) Passed as the CLOS
86 argument to ws_getvar and ws_command. */
87 int (*ws_command) (char **ret, const char *cmd, size_t len, char **argv,
88 void *clos);
89 /* [Input] (!WRDSF_NOCMD) Returns in the memory
90 location pointed to by RET the expansion of
91 the command CMD (LEN bytes long). On input,
92 ARGV contains CMD split out to words.
93
94 See ws_getvar for a discussion of possible
95 return values. */
96
97 const char *ws_input; /* Input string (the S argument to wordsplit. */
98 size_t ws_len; /* Length of ws_input. */
99 size_t ws_endp; /* Points past the last processed byte in
100 ws_input. */
101 int ws_errno; /* [Output] Error code, if an error occurred. */
102 char *ws_usererr; /* Points to textual description of
103 the error, if ws_errno is WRDSE_USERERR. Must
104 be allocated with malloc(3). */
105 struct wordsplit_node *ws_head, *ws_tail;
106 /* Doubly-linked list of parsed out nodes. */
107 int ws_lvl; /* Invocation nesting level. */
108};
109
110/* Initial size for ws_env, if allocated automatically */
111#define WORDSPLIT_ENV_INIT 16
112
113/* Wordsplit flags. */
114/* Append the words found to the array resulting from a previous
115 call. */
116#define WRDSF_APPEND 0x00000001
117/* Insert ws_offs initial NULLs in the array ws_wordv.
118 (These are not counted in the returned ws_wordc.) */
119#define WRDSF_DOOFFS 0x00000002
120/* Don't do command substitution. */
121#define WRDSF_NOCMD 0x00000004
122/* The parameter p resulted from a previous call to
123 wordsplit(), and wordsplit_free() was not called. Reuse the
124 allocated storage. */
125#define WRDSF_REUSE 0x00000008
126/* Print errors */
127#define WRDSF_SHOWERR 0x00000010
128/* Consider it an error if an undefined variable is expanded. */
129#define WRDSF_UNDEF 0x00000020
130/* Don't do variable expansion. */
131#define WRDSF_NOVAR 0x00000040
132/* Abort on ENOMEM error */
133#define WRDSF_ENOMEMABRT 0x00000080
134/* Trim off any leading and trailind whitespace */
135#define WRDSF_WS 0x00000100
136/* Handle single quotes */
137#define WRDSF_SQUOTE 0x00000200
138/* Handle double quotes */
139#define WRDSF_DQUOTE 0x00000400
140/* Handle single and double quotes */
141#define WRDSF_QUOTE (WRDSF_SQUOTE|WRDSF_DQUOTE)
142/* Replace each input sequence of repeated delimiters with a single
143 delimiter */
144#define WRDSF_SQUEEZE_DELIMS 0x00000800
145/* Return delimiters */
146#define WRDSF_RETURN_DELIMS 0x00001000
147/* Treat sed expressions as words */
148#define WRDSF_SED_EXPR 0x00002000
149/* ws_delim field is initialized */
150#define WRDSF_DELIM 0x00004000
151/* ws_comment field is initialized */
152#define WRDSF_COMMENT 0x00008000
153/* ws_alloc_die field is initialized */
154#define WRDSF_ALLOC_DIE 0x00010000
155/* ws_error field is initialized */
156#define WRDSF_ERROR 0x00020000
157/* ws_debug field is initialized */
158#define WRDSF_DEBUG 0x00040000
159/* ws_env field is initialized */
160#define WRDSF_ENV 0x00080000
161/* ws_getvar field is initialized */
162#define WRDSF_GETVAR 0x00100000
163/* enable debugging */
164#define WRDSF_SHOWDBG 0x00200000
165/* Don't split input into words. Useful for side effects. */
166#define WRDSF_NOSPLIT 0x00400000
167/* Keep undefined variables in place, instead of expanding them to
168 empty strings. */
169#define WRDSF_KEEPUNDEF 0x00800000
170/* Warn about undefined variables */
171#define WRDSF_WARNUNDEF 0x01000000
172/* Handle C escapes */
173#define WRDSF_CESCAPES 0x02000000
174/* ws_closure is set */
175#define WRDSF_CLOSURE 0x04000000
176/* ws_env is a Key/Value environment, i.e. the value of a variable is
177 stored in the element that follows its name. */
178#define WRDSF_ENV_KV 0x08000000
179/* ws_escape is set */
180#define WRDSF_ESCAPE 0x10000000
181/* Incremental mode */
182#define WRDSF_INCREMENTAL 0x20000000
183/* Perform pathname and tilde expansion */
184#define WRDSF_PATHEXPAND 0x40000000
185/* ws_options is initialized */
186#define WRDSF_OPTIONS 0x80000000
187
188 #define WRDSF_DEFFLAGS \
189 (WRDSF_NOVAR | WRDSF_NOCMD | \
190 WRDSF_QUOTE | WRDSF_SQUEEZE_DELIMS | WRDSF_CESCAPES)
191
192/* Remove the word that produces empty string after path expansion */
193#define WRDSO_NULLGLOB 0x00000001
194/* Print error message if path expansion produces empty string */
195#define WRDSO_FAILGLOB 0x00000002
196/* Allow a leading period to be matched by metacharacters. */
197#define WRDSO_DOTGLOB 0x00000004
198#if 0 /* Unused value */
199#define WRDSO_ARGV 0x00000008
200/* Keep backslash in unrecognized escape sequences in words */
201#endif
202#define WRDSO_BSKEEP_WORD 0x00000010
203/* Handle octal escapes in words */
204#define WRDSO_OESC_WORD 0x00000020
205/* Handle hex escapes in words */
206#define WRDSO_XESC_WORD 0x00000040
207
208/* ws_maxwords field is initialized */
209#define WRDSO_MAXWORDS 0x00000080
210
211/* Keep backslash in unrecognized escape sequences in quoted strings */
212#define WRDSO_BSKEEP_QUOTE 0x00000100
213/* Handle octal escapes in quoted strings */
214#define WRDSO_OESC_QUOTE 0x00000200
215/* Handle hex escapes in quoted strings */
216#define WRDSO_XESC_QUOTE 0x00000400
217
218#define WRDSO_BSKEEP WRDSO_BSKEEP_WORD
219#define WRDSO_OESC WRDSO_OESC_WORD
220#define WRDSO_XESC WRDSO_XESC_WORD
221
222/* Indices into ws_escape */
223#define WRDSX_WORD 0
224#define WRDSX_QUOTE 1
225
226/* Set escape option F in WS for words (Q==0) or quoted strings (Q==1) */
227#define WRDSO_ESC_SET(ws,q,f) ((ws)->ws_options |= ((f) << 4*(q)))
228/* Test WS for escape option F for words (Q==0) or quoted strings (Q==1) */
229#define WRDSO_ESC_TEST(ws,q,f) ((ws)->ws_options & ((f) << 4*(q)))
230
231#define WRDSE_OK 0
232#define WRDSE_EOF WRDSE_OK
233#define WRDSE_QUOTE 1
234#define WRDSE_NOSPACE 2
235#define WRDSE_USAGE 3
236#define WRDSE_CBRACE 4
237#define WRDSE_UNDEF 5
238#define WRDSE_NOINPUT 6
239#define WRDSE_PAREN 7
240#define WRDSE_GLOBERR 8
241#define WRDSE_USERERR 9
242
243int wordsplit (const char *s, wordsplit_t *ws, int flags);
244int wordsplit_len (const char *s, size_t len, wordsplit_t *ws, int flags);
245void wordsplit_free (wordsplit_t *ws);
246void wordsplit_free_words (wordsplit_t *ws);
247void wordsplit_free_envbuf (wordsplit_t *ws);
248int wordsplit_get_words (wordsplit_t *ws, size_t *wordc, char ***wordv);
249
250static inline void wordsplit_getwords (wordsplit_t *ws, size_t *wordc, char ***wordv)
251 __attribute__ ((deprecated));
252
253static inline void
254wordsplit_getwords (wordsplit_t *ws, size_t *wordc, char ***wordv)
255{
256 wordsplit_get_words (ws, wordc, wordv);
257}
258
259int wordsplit_append (wordsplit_t *wsp, int argc, char **argv);
260
261int wordsplit_c_unquote_char (int c);
262int wordsplit_c_quote_char (int c);
263size_t wordsplit_c_quoted_length (const char *str, int quote_hex, int *quote);
264void wordsplit_c_quote_copy (char *dst, const char *src, int quote_hex);
265
266void wordsplit_perror (wordsplit_t *ws);
267const char *wordsplit_strerror (wordsplit_t *ws);
268
269void wordsplit_clearerr (wordsplit_t *ws);
270
271#endif
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 28b795f..19126ab 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -75,10 +75,14 @@ check-local: atconfig atlocal $(TESTSUITE)
75 75
76 76
77check_PROGRAMS = initdb 77check_PROGRAMS = initdb
78initdb_SOURCES = initdb.c 78initdb_SOURCES = initdb.c
79initdb_LDADD = ../src/libsql.la 79initdb_LDADD = ../src/libsql.la
80initdb_CFLAGS = $(AM_CFLAGS) 80initdb_CFLAGS = $(AM_CFLAGS)
81AM_CPPFLAGS = $(VARNISHAPI_CFLAGS) -I$(top_srcdir)/src -I$(top_builddir)/src 81AM_CPPFLAGS = \
82 $(VARNISHAPI_CFLAGS)\
83 -I$(top_srcdir)/src\
84 -I$(top_builddir)/src\
85 -I$(top_srcdir)/wordsplit
82 86
83 87
84 88
diff --git a/wordsplit b/wordsplit
new file mode 160000
Subproject 6ccb9ad200f6fa0b59a3d17e7e069badb2d39e5

Return to:

Send suggestions and report system problems to the System administrator.