doc: mention timestamp limits
[tar.git] / lib / wordsplit.c
blob289f7c10c2559d30dd333007c88a6076997cf2b8
1 /* wordsplit - a word splitter
2 Copyright (C) 2009-2018 Sergey Poznyakoff
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.
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.
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/>. */
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
21 #include <wordsplit.h>
23 #include <errno.h>
24 #include <glob.h>
25 #include <limits.h>
26 #include <pwd.h>
27 #include <stdarg.h>
28 #include <stdckdint.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
34 #include <attribute.h>
35 #include <c-ctype.h>
36 #include <ialloc.h>
38 #if ENABLE_NLS
39 # include <gettext.h>
40 #else
41 # define gettext(msgid) msgid
42 #endif
43 #define _(msgid) gettext (msgid)
44 #define N_(msgid) msgid
46 #define ISWS(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
47 #define ISDELIM(ws, c) ((c) && strchr ((ws)->ws_delim, c))
49 #define ISVARBEG(c) (c_isalpha (c) || c == '_')
50 #define ISVARCHR(c) (c_isalnum (c) || c == '_')
52 #define WSP_RETURN_DELIMS(wsp) \
53 ((wsp)->ws_flags & WRDSF_RETURN_DELIMS || ((wsp)->ws_options & WRDSO_MAXWORDS))
55 /* Set escape option F in WS for words (Q==0) or quoted strings (Q==1). */
56 #define WRDSO_ESC_SET(ws,q,f) ((ws)->ws_options |= ((f) << 4*(q)))
57 /* Test WS for escape option F for words (Q==0) or quoted strings (Q==1). */
58 #define WRDSO_ESC_TEST(ws,q,f) ((ws)->ws_options & ((f) << 4*(q)))
60 /* When printing diagnostics with %.*s, output at most this many bytes.
61 Users typically don't want super-long strings in diagnostics,
62 and anyway printf fails if it outputs more than INT_MAX bytes.
63 printflen (LEN) returns LEN as an int, but at most PRINTMAX;
64 printfdots (LEN) returns "..." if LEN exceeds PRINTMAX, "" otherwise. */
66 enum { PRINTMAX = 10 * 1024 };
68 static int
69 printflen (idx_t len)
71 return len <= PRINTMAX ? len : PRINTMAX;
74 static char const *
75 printfdots (idx_t len)
77 return &"..."[3 * (len <= PRINTMAX)];
81 static void
82 _wsplt_alloc_die (struct wordsplit *wsp)
84 wsp->ws_error ("%s", _("memory exhausted"));
85 abort ();
88 static void ATTRIBUTE_FORMAT ((__printf__, 1, 2))
89 _wsplt_error (const char *fmt, ...)
91 va_list ap;
93 va_start (ap, fmt);
94 vfprintf (stderr, fmt, ap);
95 va_end (ap);
96 fputc ('\n', stderr);
99 #ifdef _WORDSPLIT_EXTRAS
100 # define WORDSPLIT_EXTRAS_extern extern
101 #else
102 # define WORDSPLIT_EXTRAS_extern static
103 static void wordsplit_clearerr (struct wordsplit *);
104 static void wordsplit_free_envbuf (struct wordsplit *);
105 static void wordsplit_free_words (struct wordsplit *);
106 static void wordsplit_perror (struct wordsplit *);
107 #endif
109 static void wordsplit_free_nodes (struct wordsplit *);
111 static int
112 _wsplt_seterr (struct wordsplit *wsp, int ec)
114 wsp->ws_errno = ec;
115 if (wsp->ws_flags & WRDSF_SHOWERR)
116 wordsplit_perror (wsp);
117 return ec;
120 static int
121 _wsplt_nomem (struct wordsplit *wsp)
123 errno = ENOMEM;
124 wsp->ws_errno = WRDSE_NOSPACE;
125 if (wsp->ws_flags & WRDSF_ENOMEMABRT)
126 wsp->ws_alloc_die (wsp);
127 if (wsp->ws_flags & WRDSF_SHOWERR)
128 wordsplit_perror (wsp);
129 if (!(wsp->ws_flags & WRDSF_REUSE))
130 wordsplit_free (wsp);
131 wordsplit_free_nodes (wsp);
132 return wsp->ws_errno;
135 static int wordsplit_init (struct wordsplit *wsp, char const *input,
136 idx_t len, unsigned flags);
137 static int wordsplit_process_list (struct wordsplit *wsp, idx_t start);
138 static int wordsplit_finish (struct wordsplit *wsp);
140 static int
141 _wsplt_subsplit (struct wordsplit *wsp, struct wordsplit *wss,
142 char const *str, idx_t len,
143 unsigned flags, bool finalize)
145 int rc;
147 wss->ws_delim = wsp->ws_delim;
148 wss->ws_debug = wsp->ws_debug;
149 wss->ws_error = wsp->ws_error;
150 wss->ws_alloc_die = wsp->ws_alloc_die;
152 if (!(flags & WRDSF_NOVAR))
154 wss->ws_env = wsp->ws_env;
155 wss->ws_getvar = wsp->ws_getvar;
156 flags |= wsp->ws_flags & (WRDSF_ENV | WRDSF_ENV_KV | WRDSF_GETVAR);
158 if (!(flags & WRDSF_NOCMD))
160 wss->ws_command = wsp->ws_command;
163 if ((flags & (WRDSF_NOVAR|WRDSF_NOCMD)) != (WRDSF_NOVAR|WRDSF_NOCMD))
165 wss->ws_closure = wsp->ws_closure;
166 flags |= wsp->ws_flags & WRDSF_CLOSURE;
169 wss->ws_options = wsp->ws_options;
171 flags |= WRDSF_DELIM
172 | WRDSF_ALLOC_DIE
173 | WRDSF_ERROR
174 | WRDSF_DEBUG
175 | (wsp->ws_flags & (WRDSF_SHOWDBG | WRDSF_SHOWERR | WRDSF_OPTIONS));
177 rc = wordsplit_init (wss, str, len, flags);
178 if (rc)
179 return rc;
180 wss->ws_lvl++;
181 rc = wordsplit_process_list (wss, 0);
182 if (rc)
184 wordsplit_free_nodes (wss);
185 return rc;
187 if (finalize)
189 rc = wordsplit_finish (wss);
190 wordsplit_free_nodes (wss);
192 return rc;
195 static void
196 _wsplt_seterr_sub (struct wordsplit *wsp, struct wordsplit *wss)
198 if (wsp->ws_errno == WRDSE_USERERR)
199 free (wsp->ws_usererr);
200 wsp->ws_errno = wss->ws_errno;
201 if (wss->ws_errno == WRDSE_USERERR)
203 wsp->ws_usererr = wss->ws_usererr;
204 wss->ws_errno = WRDSE_EOF;
205 wss->ws_usererr = NULL;
209 static void
210 wordsplit_init0 (struct wordsplit *wsp)
212 if (wsp->ws_flags & WRDSF_REUSE)
214 if (!(wsp->ws_flags & WRDSF_APPEND))
215 wordsplit_free_words (wsp);
216 wordsplit_clearerr (wsp);
218 else
220 wsp->ws_wordv = NULL;
221 wsp->ws_wordc = 0;
222 wsp->ws_wordn = 0;
225 wsp->ws_errno = 0;
228 static char wordsplit_c_escape_tab[] = "\\\\\"\"a\ab\bf\fn\nr\rt\tv\v";
230 static int
231 wordsplit_init (struct wordsplit *wsp, char const *input, idx_t len,
232 unsigned flags)
234 wsp->ws_flags = flags;
236 if (!(wsp->ws_flags & WRDSF_ALLOC_DIE))
237 wsp->ws_alloc_die = _wsplt_alloc_die;
238 if (!(wsp->ws_flags & WRDSF_ERROR))
239 wsp->ws_error = _wsplt_error;
241 if (!(wsp->ws_flags & WRDSF_NOVAR))
243 /* These will be initialized on first variable assignment */
244 wsp->ws_envidx = wsp->ws_envsiz = 0;
245 wsp->ws_envbuf = NULL;
248 if (!(wsp->ws_flags & WRDSF_NOCMD))
250 if (!wsp->ws_command)
252 _wsplt_seterr (wsp, WRDSE_USAGE);
253 errno = EINVAL;
254 return wsp->ws_errno;
258 if (wsp->ws_flags & WRDSF_SHOWDBG)
260 if (!(wsp->ws_flags & WRDSF_DEBUG))
262 if (wsp->ws_flags & WRDSF_ERROR)
263 wsp->ws_debug = wsp->ws_error;
264 else if (wsp->ws_flags & WRDSF_SHOWERR)
265 wsp->ws_debug = _wsplt_error;
266 else
267 wsp->ws_flags &= ~WRDSF_SHOWDBG;
271 wsp->ws_input = input;
272 wsp->ws_len = len;
274 if (!(wsp->ws_flags & WRDSF_DOOFFS))
275 wsp->ws_offs = 0;
277 if (!(wsp->ws_flags & WRDSF_DELIM))
278 wsp->ws_delim = " \t\n";
280 if (!(wsp->ws_flags & WRDSF_COMMENT))
281 wsp->ws_comment = NULL;
283 if (!(wsp->ws_flags & WRDSF_CLOSURE))
284 wsp->ws_closure = NULL;
286 if (!(wsp->ws_flags & WRDSF_OPTIONS))
287 wsp->ws_options = 0;
289 if (wsp->ws_flags & WRDSF_ESCAPE)
291 if (!wsp->ws_escape[WRDSX_WORD])
292 wsp->ws_escape[WRDSX_WORD] = "";
293 if (!wsp->ws_escape[WRDSX_QUOTE])
294 wsp->ws_escape[WRDSX_QUOTE] = "";
296 else
298 if (wsp->ws_flags & WRDSF_CESCAPES)
300 wsp->ws_escape[WRDSX_WORD] = wordsplit_c_escape_tab;
301 wsp->ws_escape[WRDSX_QUOTE] = wordsplit_c_escape_tab;
302 wsp->ws_options |= WRDSO_OESC_QUOTE | WRDSO_OESC_WORD
303 | WRDSO_XESC_QUOTE | WRDSO_XESC_WORD;
305 else
307 wsp->ws_escape[WRDSX_WORD] = "";
308 wsp->ws_escape[WRDSX_QUOTE] = "\\\\\"\"";
309 wsp->ws_options |= WRDSO_BSKEEP_QUOTE;
313 wsp->ws_endp = 0;
314 wsp->ws_wordi = 0;
316 if (wsp->ws_flags & WRDSF_REUSE)
317 wordsplit_free_nodes (wsp);
318 wsp->ws_head = wsp->ws_tail = NULL;
320 wordsplit_init0 (wsp);
322 return WRDSE_OK;
325 static int
326 alloc_space (struct wordsplit *wsp, idx_t count)
328 idx_t offs_plus_count;
329 if (ckd_add (&offs_plus_count, count,
330 wsp->ws_flags & WRDSF_DOOFFS ? wsp->ws_offs : 0))
331 return _wsplt_nomem (wsp);
333 idx_t wordn;
334 char **wordv;
336 if (!wsp->ws_wordv)
338 /* The default largest "small, fast" request for glibc malloc. */
339 enum { DEFAULT_MXFAST = 64 * sizeof (size_t) / 4 };
341 enum { ALLOC_INIT = DEFAULT_MXFAST / sizeof *wordv };
342 wordn = offs_plus_count <= ALLOC_INIT ? ALLOC_INIT : offs_plus_count;
344 /* Use calloc so that the initial ws_offs words are zero. */
345 wordv = icalloc (wordn, sizeof *wordv);
347 else
349 idx_t wordroom = wsp->ws_wordn - wsp->ws_wordc;
350 if (offs_plus_count <= wordroom)
351 return WRDSE_OK;
353 /* Grow the allocation by at least MININCR. To avoid quadratic
354 behavior, also grow it by at least 50% if possible. */
355 idx_t minincr = offs_plus_count - wordroom;
356 idx_t halfn = wsp->ws_wordn >> 1;
357 wordv = (halfn <= minincr || ckd_add (&wordn, wsp->ws_wordn, halfn)
358 ? nullptr
359 : ireallocarray (wsp->ws_wordv, wordn, sizeof *wordv));
360 if (!wordv)
361 wordv = (ckd_add (&wordn, wsp->ws_wordn, minincr)
362 ? nullptr
363 : ireallocarray (wsp->ws_wordv, wordn, sizeof *wordv));
366 if (!wordv)
367 return _wsplt_nomem (wsp);
368 wsp->ws_wordn = wordn;
369 wsp->ws_wordv = wordv;
370 return WRDSE_OK;
374 /* Node state flags */
375 #define _WSNF_NULL 0x01 /* null node (a noop) */
376 #define _WSNF_WORD 0x02 /* node contains word in v.word */
377 #define _WSNF_QUOTE 0x04 /* text is quoted */
378 #define _WSNF_NOEXPAND 0x08 /* text is not subject to expansion */
379 #define _WSNF_JOIN 0x10 /* node must be joined with the next node */
380 #define _WSNF_SEXP 0x20 /* is a sed expression */
381 #define _WSNF_DELIM 0x40 /* node is a delimiter */
383 #define _WSNF_EMPTYOK 0x0100 /* special flag indicating that
384 wordsplit_add_segm must add the
385 segment even if it is empty */
387 struct wordsplit_node
389 struct wordsplit_node *prev; /* Previous element */
390 struct wordsplit_node *next; /* Next element */
391 unsigned flags; /* Node flags */
392 union
394 struct
396 idx_t beg; /* Start of word in ws_input */
397 idx_t end; /* End of word in ws_input */
398 } segm;
399 char *word;
400 } v;
403 static const char *
404 wsnode_flagstr (unsigned flags)
406 static char retbuf[7];
407 char *p = retbuf;
409 if (flags & _WSNF_WORD)
410 *p++ = 'w';
411 else if (flags & _WSNF_NULL)
412 *p++ = 'n';
413 else
414 *p++ = '-';
415 if (flags & _WSNF_QUOTE)
416 *p++ = 'q';
417 else
418 *p++ = '-';
419 if (flags & _WSNF_NOEXPAND)
420 *p++ = 'E';
421 else
422 *p++ = '-';
423 if (flags & _WSNF_JOIN)
424 *p++ = 'j';
425 else
426 *p++ = '-';
427 if (flags & _WSNF_SEXP)
428 *p++ = 's';
429 else
430 *p++ = '-';
431 if (flags & _WSNF_DELIM)
432 *p++ = 'd';
433 else
434 *p++ = '-';
435 *p = '\0';
436 return retbuf;
439 static const char *
440 wsnode_ptr (struct wordsplit *wsp, struct wordsplit_node *p)
442 if (p->flags & _WSNF_NULL)
443 return "";
444 else if (p->flags & _WSNF_WORD)
445 return p->v.word;
446 else
447 return wsp->ws_input + p->v.segm.beg;
450 static idx_t
451 wsnode_len (struct wordsplit_node *p)
453 if (p->flags & _WSNF_NULL)
454 return 0;
455 else if (p->flags & _WSNF_WORD)
456 return strlen (p->v.word);
457 else
458 return p->v.segm.end - p->v.segm.beg;
461 static struct wordsplit_node *
462 wsnode_new (struct wordsplit *wsp)
464 struct wordsplit_node *node = calloc (1, sizeof *node);
465 if (!node)
466 _wsplt_nomem (wsp);
467 return node;
470 static void
471 wsnode_free (struct wordsplit_node *p)
473 if (p->flags & _WSNF_WORD)
474 free (p->v.word);
475 free (p);
478 static void
479 wsnode_append (struct wordsplit *wsp, struct wordsplit_node *node)
481 node->next = NULL;
482 node->prev = wsp->ws_tail;
483 if (wsp->ws_tail)
484 wsp->ws_tail->next = node;
485 else
486 wsp->ws_head = node;
487 wsp->ws_tail = node;
490 static void
491 wsnode_remove (struct wordsplit *wsp, struct wordsplit_node *node)
493 struct wordsplit_node *p;
495 p = node->prev;
496 if (p)
498 p->next = node->next;
499 if (!node->next)
500 p->flags &= ~_WSNF_JOIN;
502 else
503 wsp->ws_head = node->next;
505 p = node->next;
506 if (p)
507 p->prev = node->prev;
508 else
509 wsp->ws_tail = node->prev;
511 node->next = node->prev = NULL;
514 static struct wordsplit_node *
515 wsnode_tail (struct wordsplit_node *p)
517 while (p && p->next)
518 p = p->next;
519 return p;
522 static void
523 wsnode_insert (struct wordsplit *wsp, struct wordsplit_node *node,
524 struct wordsplit_node *anchor)
526 if (!wsp->ws_head)
528 node->next = node->prev = NULL;
529 wsp->ws_head = wsp->ws_tail = node;
531 else
533 struct wordsplit_node *p;
534 struct wordsplit_node *tail = wsnode_tail (node);
536 p = anchor->next;
537 if (p)
538 p->prev = tail;
539 else
540 wsp->ws_tail = tail;
541 tail->next = p;
542 node->prev = anchor;
543 anchor->next = node;
547 static bool
548 wordsplit_add_segm (struct wordsplit *wsp, idx_t beg, idx_t end, unsigned flg)
550 if (end == beg && !(flg & _WSNF_EMPTYOK))
551 return true;
552 struct wordsplit_node *node = wsnode_new (wsp);
553 if (!node)
554 return false;
555 node->flags = flg & ~(_WSNF_WORD | _WSNF_EMPTYOK);
556 node->v.segm.beg = beg;
557 node->v.segm.end = end;
558 wsnode_append (wsp, node);
559 return true;
562 static void
563 wordsplit_free_nodes (struct wordsplit *wsp)
565 struct wordsplit_node *p;
567 for (p = wsp->ws_head; p;)
569 struct wordsplit_node *next = p->next;
570 wsnode_free (p);
571 p = next;
573 wsp->ws_head = wsp->ws_tail = NULL;
576 static void
577 wordsplit_dump_nodes (struct wordsplit *wsp)
579 intmax_t n = 0;
580 for (struct wordsplit_node *p = wsp->ws_head; p; p = p->next, n++)
582 if (p->flags & _WSNF_WORD)
583 wsp->ws_debug ("(%02td) %4jd: %p: %#04x (%s):%s;",
584 wsp->ws_lvl,
585 n, p, p->flags, wsnode_flagstr (p->flags), p->v.word);
586 else
588 idx_t seglen = p->v.segm.end - p->v.segm.beg;
589 wsp->ws_debug ("(%02td) %4jd: %p: %#04x (%s):%.*s%s;",
590 wsp->ws_lvl,
591 n, p, p->flags, wsnode_flagstr (p->flags),
592 printflen (seglen), wsp->ws_input + p->v.segm.beg,
593 printfdots (seglen));
598 static int
599 coalesce_segment (struct wordsplit *wsp, struct wordsplit_node *node)
601 struct wordsplit_node *p, *end;
602 idx_t len = 0;
603 char *buf, *cur;
605 if (!(node->flags & _WSNF_JOIN))
606 return WRDSE_OK;
608 for (p = node; p && (p->flags & _WSNF_JOIN); p = p->next)
610 len += wsnode_len (p);
612 if (p)
613 len += wsnode_len (p);
614 end = p;
616 buf = imalloc (len + 1);
617 if (!buf)
618 return _wsplt_nomem (wsp);
619 cur = buf;
621 p = node;
622 for (;;)
624 struct wordsplit_node *next = p->next;
625 const char *str = wsnode_ptr (wsp, p);
626 idx_t slen = wsnode_len (p);
628 memcpy (cur, str, slen);
629 cur += slen;
630 if (p != node)
632 node->flags |= p->flags & _WSNF_QUOTE;
633 wsnode_remove (wsp, p);
634 if (p == end)
636 /* Call wsnode_free separately to work around GCC bug 106427. */
637 wsnode_free (p);
638 break;
640 wsnode_free (p);
642 p = next;
645 *cur = '\0';
647 node->flags &= ~_WSNF_JOIN;
649 if (node->flags & _WSNF_WORD)
650 free (node->v.word);
651 else
652 node->flags |= _WSNF_WORD;
653 node->v.word = buf;
654 return WRDSE_OK;
657 static void wordsplit_string_unquote_copy (struct wordsplit *ws, bool inquote,
658 char *dst, const char *src,
659 idx_t n);
661 static int
662 wsnode_quoteremoval (struct wordsplit *wsp)
664 struct wordsplit_node *p;
666 for (p = wsp->ws_head; p; p = p->next)
668 bool unquote = (wsp->ws_flags & WRDSF_QUOTE
669 && !(p->flags & _WSNF_NOEXPAND));
671 if (unquote)
673 const char *str = wsnode_ptr (wsp, p);
674 idx_t slen = wsnode_len (p);
676 if (!(p->flags & _WSNF_WORD))
678 char *newstr = imalloc (slen + 1);
679 if (!newstr)
680 return _wsplt_nomem (wsp);
681 memcpy (newstr, str, slen);
682 newstr[slen] = '\0';
683 p->v.word = newstr;
684 p->flags |= _WSNF_WORD;
687 wordsplit_string_unquote_copy (wsp, !!(p->flags & _WSNF_QUOTE),
688 p->v.word, str, slen);
691 return WRDSE_OK;
694 static bool
695 wsnode_coalesce (struct wordsplit *wsp)
697 struct wordsplit_node *p;
699 for (p = wsp->ws_head; p; p = p->next)
701 if (p->flags & _WSNF_JOIN)
702 if (coalesce_segment (wsp, p))
703 return true;
705 return false;
708 static bool
709 wsnode_tail_coalesce (struct wordsplit *wsp, struct wordsplit_node *p)
711 if (p->next)
713 struct wordsplit_node *np = p;
714 while (np && np->next)
716 np->flags |= _WSNF_JOIN;
717 np = np->next;
719 if (coalesce_segment (wsp, p))
720 return true;
722 return false;
725 static idx_t skip_delim (struct wordsplit *wsp);
727 static int
728 wordsplit_finish (struct wordsplit *wsp)
730 struct wordsplit_node *p;
731 idx_t n;
732 char delim;
734 /* Postprocess delimiters. It would be rather simple, if it weren't for
735 the incremental operation.
737 Nodes of type _WSNF_DELIM get inserted to the node list if either
738 WRDSF_RETURN_DELIMS flag or WRDSO_MAXWORDS option is set.
740 The following cases should be distinguished:
742 1. If both WRDSF_SQUEEZE_DELIMS and WRDSF_RETURN_DELIMS are set, compress
743 any runs of similar delimiter nodes to a single node. The nodes are
744 'similar' if they point to the same delimiter character.
746 If WRDSO_MAXWORDS option is set, stop compressing when
747 ws_wordi + 1 == ws_maxwords, and coalesce the rest of nodes into
748 a single last node.
750 2. If WRDSO_MAXWORDS option is set, but WRDSF_RETURN_DELIMS is not,
751 remove any delimiter nodes. Stop operation when
752 ws_wordi + 1 == ws_maxwords, and coalesce the rest of nodes into
753 a single last node.
755 3. If incremental operation is in progress, restart the loop any time
756 a delimiter node is about to be returned, unless WRDSF_RETURN_DELIMS
757 is set.
759 again:
760 delim = '\0'; /* Delimiter being processed (if any) */
761 n = 0; /* Number of words processed so far */
762 p = wsp->ws_head; /* Current node */
764 while (p)
766 struct wordsplit_node *next = p->next;
767 if (p->flags & _WSNF_DELIM)
769 if (wsp->ws_flags & WRDSF_RETURN_DELIMS)
771 if (wsp->ws_flags & WRDSF_SQUEEZE_DELIMS)
773 char const *s = wsnode_ptr (wsp, p);
774 if (delim)
776 if (delim == *s)
778 wsnode_remove (wsp, p);
779 p = next;
780 continue;
782 else
784 delim = '\0';
785 n++; /* Count this node; it will be returned */
788 else
790 delim = *s;
791 p = next;
792 continue;
796 else if (wsp->ws_options & WRDSO_MAXWORDS)
798 wsnode_remove (wsp, p);
799 p = next;
800 continue;
803 else
805 if (delim)
807 /* Last node was a delimiter or a compressed run of delimiters;
808 Count it, and clear the delimiter marker */
809 n++;
810 delim = '\0';
812 if (wsp->ws_options & WRDSO_MAXWORDS)
814 if (wsp->ws_wordi + n + 1 == wsp->ws_maxwords)
815 break;
818 n++;
819 if (wsp->ws_flags & WRDSF_INCREMENTAL)
820 p = NULL; /* Break the loop */
821 else
822 p = next;
825 if (p)
827 /* We're here if WRDSO_MAXWORDS is in effect and wsp->ws_maxwords
828 words have already been collected. Reconstruct a single final
829 node from the remaining nodes. */
830 if (wsnode_tail_coalesce (wsp, p))
831 return wsp->ws_errno;
832 n++;
835 if (n == 0 && (wsp->ws_flags & WRDSF_INCREMENTAL))
837 /* The loop above have eliminated all nodes. Restart the
838 processing, if there's any input left. */
839 if (wsp->ws_endp < wsp->ws_len)
841 int rc;
842 if (wsp->ws_flags & WRDSF_SHOWDBG)
843 wsp->ws_debug (_("Restarting"));
844 rc = wordsplit_process_list (wsp, skip_delim (wsp));
845 if (rc)
846 return rc;
848 else
850 wsp->ws_error = WRDSE_EOF;
851 return WRDSE_EOF;
853 goto again;
856 if (alloc_space (wsp, n + 1))
857 return wsp->ws_errno;
859 while (wsp->ws_head)
861 const char *str = wsnode_ptr (wsp, wsp->ws_head);
862 idx_t slen = wsnode_len (wsp->ws_head);
863 char *newstr = imalloc (slen + 1);
865 /* Assign newstr first, even if it is NULL. This way
866 wordsplit_free will work even if we return
867 nomem later. */
868 wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc] = newstr;
869 if (!newstr)
870 return _wsplt_nomem (wsp);
871 memcpy (newstr, str, slen);
872 newstr[slen] = '\0';
874 wsnode_remove (wsp, wsp->ws_head);
876 wsp->ws_wordc++;
877 wsp->ws_wordi++;
879 if (wsp->ws_flags & WRDSF_INCREMENTAL)
880 break;
882 wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc] = NULL;
883 return WRDSE_OK;
886 #ifdef _WORDSPLIT_EXTRAS
888 wordsplit_append (wordsplit_t *wsp, int argc, char **argv)
890 int rc;
891 idx_t i;
893 rc = alloc_space (wsp, wsp->ws_wordc + argc + 1);
894 if (rc)
895 return rc;
896 for (i = 0; i < argc; i++)
898 char *newstr = strdup (argv[i]);
899 if (!newstr)
901 while (i > 0)
903 free (wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc + i - 1]);
904 wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc + i - 1] = NULL;
905 i--;
907 return _wsplt_nomem (wsp);
909 wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc + i] = newstr;
911 wsp->ws_wordc += i;
912 wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc] = NULL;
913 return WRDSE_OK;
915 #endif
917 /* Variable expansion */
918 static int
919 node_split_prefix (struct wordsplit *wsp,
920 struct wordsplit_node **ptail,
921 struct wordsplit_node *node,
922 idx_t beg, idx_t len, unsigned flg)
924 if (len == 0)
925 return 0;
926 struct wordsplit_node *newnode = wsnode_new (wsp);
927 if (!newnode)
928 return 1;
929 wsnode_insert (wsp, newnode, *ptail);
930 if (node->flags & _WSNF_WORD)
932 const char *str = wsnode_ptr (wsp, node);
933 char *newstr = imalloc (len + 1);
934 if (!newstr)
935 return _wsplt_nomem (wsp);
936 memcpy (newstr, str + beg, len);
937 newstr[len] = '\0';
938 newnode->flags = _WSNF_WORD;
939 newnode->v.word = newstr;
941 else
943 newnode->v.segm.beg = node->v.segm.beg + beg;
944 newnode->v.segm.end = newnode->v.segm.beg + len;
946 newnode->flags |= flg;
947 *ptail = newnode;
948 return 0;
951 static bool
952 find_closing_paren (char const *str, idx_t i, idx_t len, idx_t *poff,
953 char const *paren)
955 enum { st_init, st_squote, st_dquote } state = st_init;
956 idx_t level = 1;
958 for (; i < len; i++)
960 switch (state)
962 case st_init:
963 switch (str[i])
965 default:
966 if (str[i] == paren[0])
968 level++;
969 break;
971 else if (str[i] == paren[1])
973 if (--level == 0)
975 *poff = i;
976 return false;
978 break;
980 break;
982 case '"':
983 state = st_dquote;
984 break;
986 case '\'':
987 state = st_squote;
988 break;
990 break;
992 case st_squote:
993 if (str[i] == '\'')
994 state = st_init;
995 break;
997 case st_dquote:
998 if (str[i] == '\\')
999 i++;
1000 else if (str[i] == '"')
1001 state = st_init;
1002 break;
1005 return true;
1008 static int
1009 wordsplit_find_env (struct wordsplit *wsp, char const *name, idx_t len,
1010 char const **ret)
1012 idx_t i;
1014 if (!(wsp->ws_flags & WRDSF_ENV))
1015 return WRDSE_UNDEF;
1017 if (wsp->ws_flags & WRDSF_ENV_KV)
1019 /* A key-value pair environment */
1020 for (i = 0; wsp->ws_env[i]; i++)
1022 idx_t elen = strlen (wsp->ws_env[i]);
1023 if (elen == len && memcmp (wsp->ws_env[i], name, elen) == 0)
1025 *ret = wsp->ws_env[i + 1];
1026 return WRDSE_OK;
1028 /* Skip the value. Break the loop if it is NULL. */
1029 i++;
1030 if (wsp->ws_env[i] == NULL)
1031 break;
1034 else if (wsp->ws_env)
1036 /* Usual (A=B) environment. */
1037 for (i = 0; wsp->ws_env[i]; i++)
1039 idx_t j;
1040 const char *var = wsp->ws_env[i];
1042 for (j = 0; j < len; j++)
1043 if (name[j] != var[j])
1044 break;
1045 if (j == len && var[j] == '=')
1047 *ret = var + j + 1;
1048 return WRDSE_OK;
1052 return WRDSE_UNDEF;
1055 /* Initial size for ws_env, if allocated automatically */
1056 enum { WORDSPLIT_ENV_INIT = 16 };
1058 static int
1059 wsplt_assign_var (struct wordsplit *wsp, char const *name, idx_t namelen,
1060 char *value)
1062 int n = (wsp->ws_flags & WRDSF_ENV_KV) ? 2 : 1;
1063 char *v;
1065 if (wsp->ws_envsiz - wsp->ws_envidx <= n)
1067 idx_t sz;
1068 char **newenv;
1070 if (!wsp->ws_envbuf)
1072 if (wsp->ws_flags & WRDSF_ENV)
1074 idx_t i = 0, j;
1076 if (wsp->ws_env)
1078 for (; wsp->ws_env[i]; i++)
1082 sz = i + n + 1;
1084 newenv = icalloc (sz, sizeof *newenv);
1085 if (!newenv)
1086 return _wsplt_nomem (wsp);
1088 for (j = 0; j < i; j++)
1090 newenv[j] = strdup (wsp->ws_env[j]);
1091 if (!newenv[j])
1093 for (; j > 1; j--)
1094 free (newenv[j-1]);
1095 free (newenv[j-1]);
1096 free (newenv);
1097 return _wsplt_nomem (wsp);
1100 newenv[j] = NULL;
1102 wsp->ws_envbuf = newenv;
1103 wsp->ws_envidx = i;
1104 wsp->ws_envsiz = sz;
1105 wsp->ws_env = (char const **) newenv;
1107 else
1109 newenv = calloc (WORDSPLIT_ENV_INIT, sizeof *newenv);
1110 if (!newenv)
1111 return _wsplt_nomem (wsp);
1112 wsp->ws_envbuf = newenv;
1113 wsp->ws_envidx = 0;
1114 wsp->ws_envsiz = WORDSPLIT_ENV_INIT;
1115 wsp->ws_env = (char const **) newenv;
1116 wsp->ws_flags |= WRDSF_ENV;
1119 else
1121 idx_t envsiz, envsiz2;
1122 if (ckd_add (&envsiz, wsp->ws_envidx, n + 1))
1123 return _wsplt_nomem (wsp);
1124 newenv = ((!ckd_add (&envsiz2, wsp->ws_envsiz, wsp->ws_envsiz >> 1)
1125 && envsiz < envsiz2)
1126 ? ireallocarray (wsp->ws_envbuf, envsiz2, sizeof *newenv)
1127 : NULL);
1128 if (newenv)
1129 envsiz = envsiz2;
1130 else
1132 newenv = ireallocarray (wsp->ws_envbuf, envsiz, sizeof *newenv);
1133 if (!newenv)
1134 return _wsplt_nomem (wsp);
1137 wsp->ws_envbuf = newenv;
1138 wsp->ws_envsiz = envsiz;
1139 wsp->ws_env = (char const **) wsp->ws_envbuf;
1143 if (wsp->ws_flags & WRDSF_ENV_KV)
1145 /* A key-value pair environment */
1146 char *p = imalloc (namelen + 1);
1147 if (!p)
1148 return _wsplt_nomem (wsp);
1149 memcpy (p, name, namelen);
1150 p[namelen] = '\0';
1152 v = strdup (value);
1153 if (!v)
1155 free (p);
1156 return _wsplt_nomem (wsp);
1158 wsp->ws_env[wsp->ws_envidx++] = p;
1159 wsp->ws_env[wsp->ws_envidx++] = v;
1161 else
1163 v = imalloc (namelen + strlen (value) + 2);
1164 if (!v)
1165 return _wsplt_nomem (wsp);
1166 memcpy (v, name, namelen);
1167 v[namelen++] = '=';
1168 strcpy (v + namelen, value);
1169 wsp->ws_env[wsp->ws_envidx++] = v;
1171 wsp->ws_env[wsp->ws_envidx++] = NULL;
1172 return WRDSE_OK;
1175 static int
1176 expvar (struct wordsplit *wsp, char const *str, idx_t len,
1177 struct wordsplit_node **ptail, const char **pend, unsigned flg)
1179 idx_t i = 0;
1180 const char *defstr = NULL;
1181 char *value;
1182 const char *vptr;
1183 struct wordsplit_node *newnode;
1184 const char *start = str - 1;
1185 int rc;
1186 struct wordsplit ws;
1188 if (ISVARBEG (str[0]))
1190 for (i = 1; i < len; i++)
1191 if (!ISVARCHR (str[i]))
1192 break;
1193 *pend = str + i - 1;
1195 else if (str[0] == '{')
1197 str++;
1198 len--;
1199 for (i = 1; i < len; i++)
1201 if (str[i] == ':')
1203 idx_t j;
1205 defstr = str + i + 1;
1206 if (find_closing_paren (str, i + 1, len, &j, "{}"))
1207 return _wsplt_seterr (wsp, WRDSE_CBRACE);
1208 *pend = str + j;
1209 break;
1211 else if (str[i] == '}')
1213 defstr = NULL;
1214 *pend = str + i;
1215 break;
1217 else if (str[i] && strchr ("-+?=", str[i]))
1219 idx_t j;
1221 defstr = str + i;
1222 if (find_closing_paren (str, i, len, &j, "{}"))
1223 return _wsplt_seterr (wsp, WRDSE_CBRACE);
1224 *pend = str + j;
1225 break;
1228 if (i == len)
1229 return _wsplt_seterr (wsp, WRDSE_CBRACE);
1231 else
1233 newnode = wsnode_new (wsp);
1234 if (!newnode)
1235 return 1;
1236 wsnode_insert (wsp, newnode, *ptail);
1237 *ptail = newnode;
1238 newnode->flags = _WSNF_WORD | flg;
1239 newnode->v.word = malloc (3);
1240 if (!newnode->v.word)
1241 return _wsplt_nomem (wsp);
1242 newnode->v.word[0] = '$';
1243 newnode->v.word[1] = str[0];
1244 newnode->v.word[2] = '\0';
1245 *pend = str;
1246 return 0;
1249 /* Actually expand the variable */
1250 /* str - start of the variable name
1251 i - its length
1252 defstr - default replacement str */
1254 if (defstr && ! (defstr[0] && strchr ("-+?=", defstr[0])))
1256 rc = WRDSE_UNDEF;
1257 defstr = NULL;
1259 else
1261 rc = wordsplit_find_env (wsp, str, i, &vptr);
1262 if (rc == WRDSE_OK)
1264 if (vptr)
1266 value = strdup (vptr);
1267 if (!value)
1268 rc = WRDSE_NOSPACE;
1270 else
1271 rc = WRDSE_UNDEF;
1273 else if (wsp->ws_flags & WRDSF_GETVAR)
1274 rc = wsp->ws_getvar (&value, str, i, wsp->ws_closure);
1275 else
1276 rc = WRDSE_UNDEF;
1278 if (rc == WRDSE_OK
1279 && !(value && *value)
1280 && defstr && defstr[-1] == ':')
1282 free (value);
1283 rc = WRDSE_UNDEF;
1287 switch (rc)
1289 case WRDSE_OK:
1290 if (defstr && *defstr == '+')
1292 idx_t size = *pend - ++defstr;
1294 rc = _wsplt_subsplit (wsp, &ws, defstr, size,
1295 WRDSF_NOSPLIT | WRDSF_WS | WRDSF_QUOTE |
1296 (wsp->ws_flags &
1297 (WRDSF_NOVAR | WRDSF_NOCMD)),
1298 true);
1299 if (rc)
1300 return rc;
1301 free (value);
1302 value = ws.ws_wordv[0];
1303 ws.ws_wordv[0] = NULL;
1304 wordsplit_free (&ws);
1306 break;
1308 case WRDSE_UNDEF:
1309 if (defstr)
1311 idx_t size;
1312 if (*defstr == '-' || *defstr == '=')
1314 size = *pend - ++defstr;
1316 rc = _wsplt_subsplit (wsp, &ws, defstr, size,
1317 WRDSF_NOSPLIT | WRDSF_WS | WRDSF_QUOTE |
1318 (wsp->ws_flags &
1319 (WRDSF_NOVAR | WRDSF_NOCMD)),
1320 true);
1321 if (rc)
1322 return rc;
1324 value = ws.ws_wordv[0];
1325 ws.ws_wordv[0] = NULL;
1326 wordsplit_free (&ws);
1328 if (defstr[-1] == '=')
1329 wsplt_assign_var (wsp, str, i, value);
1331 else
1333 if (*defstr == '?')
1335 size = *pend - ++defstr;
1336 if (size == 0)
1337 wsp->ws_error (_("%.*s%s: variable null or not set"),
1338 printflen (i), str, printfdots (i));
1339 else
1341 rc = _wsplt_subsplit (wsp, &ws, defstr, size,
1342 WRDSF_NOSPLIT | WRDSF_WS |
1343 WRDSF_QUOTE |
1344 (wsp->ws_flags &
1345 (WRDSF_NOVAR | WRDSF_NOCMD)),
1346 true);
1347 if (rc == 0)
1348 wsp->ws_error ("%.*s%s: %s",
1349 printflen (i), str, printfdots (i),
1350 ws.ws_wordv[0]);
1351 else
1352 wsp->ws_error ("%.*s%s: %.*s%s",
1353 printflen (i), str, printfdots (i),
1354 printflen (size), defstr,
1355 printfdots (size));
1356 wordsplit_free (&ws);
1359 value = NULL;
1362 else if (wsp->ws_flags & WRDSF_UNDEF)
1364 _wsplt_seterr (wsp, WRDSE_UNDEF);
1365 return 1;
1367 else
1369 if (wsp->ws_flags & WRDSF_WARNUNDEF)
1370 wsp->ws_error (_("warning: undefined variable '%.*s%s'"),
1371 printflen (i), str, printfdots (i));
1372 if (wsp->ws_flags & WRDSF_KEEPUNDEF)
1373 value = NULL;
1374 else
1376 value = strdup ("");
1377 if (!value)
1378 return _wsplt_nomem (wsp);
1381 break;
1383 case WRDSE_NOSPACE:
1384 return _wsplt_nomem (wsp);
1386 case WRDSE_USERERR:
1387 if (wsp->ws_errno == WRDSE_USERERR)
1388 free (wsp->ws_usererr);
1389 wsp->ws_usererr = value;
1390 FALLTHROUGH;
1391 default:
1392 _wsplt_seterr (wsp, rc);
1393 return 1;
1396 if (value)
1398 if (flg & _WSNF_QUOTE)
1400 newnode = wsnode_new (wsp);
1401 if (!newnode)
1403 free (value);
1404 return 1;
1406 wsnode_insert (wsp, newnode, *ptail);
1407 *ptail = newnode;
1408 newnode->flags = _WSNF_WORD | _WSNF_NOEXPAND | flg;
1409 newnode->v.word = value;
1411 else if (!*value)
1413 free (value);
1414 /* Empty string is a special case */
1415 newnode = wsnode_new (wsp);
1416 if (!newnode)
1417 return 1;
1418 wsnode_insert (wsp, newnode, *ptail);
1419 *ptail = newnode;
1420 newnode->flags = _WSNF_NULL;
1422 else
1424 struct wordsplit ws;
1425 int rc;
1427 rc = _wsplt_subsplit (wsp, &ws, value, strlen (value),
1428 (WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_QUOTE
1429 | (WSP_RETURN_DELIMS (wsp)
1430 ? WRDSF_RETURN_DELIMS : 0)),
1431 false);
1432 free (value);
1433 if (rc)
1435 _wsplt_seterr_sub (wsp, &ws);
1436 wordsplit_free (&ws);
1437 return 1;
1439 wsnode_insert (wsp, ws.ws_head, *ptail);
1440 *ptail = ws.ws_tail;
1441 ws.ws_head = ws.ws_tail = NULL;
1442 wordsplit_free (&ws);
1445 else if (wsp->ws_flags & WRDSF_KEEPUNDEF)
1447 idx_t size = *pend - start + 1;
1449 newnode = wsnode_new (wsp);
1450 if (!newnode)
1451 return 1;
1452 wsnode_insert (wsp, newnode, *ptail);
1453 *ptail = newnode;
1454 newnode->flags = _WSNF_WORD | _WSNF_NOEXPAND | flg;
1455 newnode->v.word = imalloc (size + 1);
1456 if (!newnode->v.word)
1457 return _wsplt_nomem (wsp);
1458 memcpy (newnode->v.word, start, size);
1459 newnode->v.word[size] = '\0';
1461 else
1463 newnode = wsnode_new (wsp);
1464 if (!newnode)
1465 return 1;
1466 wsnode_insert (wsp, newnode, *ptail);
1467 *ptail = newnode;
1468 newnode->flags = _WSNF_NULL;
1470 return 0;
1473 static bool
1474 begin_var_p (char c)
1476 return c == '{' || ISVARBEG (c);
1479 static bool
1480 node_expand (struct wordsplit *wsp, struct wordsplit_node *node,
1481 bool (*beg_p) (char),
1482 int (*ws_exp_fn) (struct wordsplit *wsp,
1483 char const *str, idx_t len,
1484 struct wordsplit_node **ptail,
1485 const char **pend,
1486 unsigned flg))
1488 const char *str = wsnode_ptr (wsp, node);
1489 idx_t slen = wsnode_len (node);
1490 const char *end = str + slen;
1491 const char *p;
1492 idx_t off = 0;
1493 struct wordsplit_node *tail = node;
1495 for (p = str; p < end; p++)
1497 if (*p == '\\')
1499 p++;
1500 continue;
1502 if (*p == '$' && beg_p (p[1]))
1504 idx_t n = p - str;
1506 if (tail != node)
1507 tail->flags |= _WSNF_JOIN;
1508 if (node_split_prefix (wsp, &tail, node, off, n, _WSNF_JOIN))
1509 return false;
1510 p++;
1511 if (ws_exp_fn (wsp, p, slen - n, &tail, &p,
1512 node->flags & (_WSNF_JOIN | _WSNF_QUOTE)))
1513 return false;
1514 off += p - str + 1;
1515 str = p + 1;
1518 if (p > str)
1520 if (tail != node)
1521 tail->flags |= _WSNF_JOIN;
1522 if (node_split_prefix (wsp, &tail, node, off, p - str,
1523 node->flags & (_WSNF_JOIN|_WSNF_QUOTE)))
1524 return false;
1526 if (tail != node)
1528 wsnode_remove (wsp, node);
1529 wsnode_free (node);
1531 return true;
1534 /* Remove NULL nodes from the list */
1535 static void
1536 wsnode_nullelim (struct wordsplit *wsp)
1538 struct wordsplit_node *p;
1540 for (p = wsp->ws_head; p;)
1542 struct wordsplit_node *next = p->next;
1543 if (p->flags & _WSNF_DELIM && p->prev)
1544 p->prev->flags &= ~_WSNF_JOIN;
1545 if (p->flags & _WSNF_NULL)
1547 wsnode_remove (wsp, p);
1548 wsnode_free (p);
1550 p = next;
1554 static int
1555 wordsplit_varexp (struct wordsplit *wsp)
1557 struct wordsplit_node *p;
1559 for (p = wsp->ws_head; p;)
1561 struct wordsplit_node *next = p->next;
1562 if (!(p->flags & (_WSNF_NOEXPAND|_WSNF_DELIM)))
1563 if (!node_expand (wsp, p, begin_var_p, expvar))
1564 return 1;
1565 p = next;
1568 wsnode_nullelim (wsp);
1569 return 0;
1572 static bool
1573 begin_cmd_p (char c)
1575 return c == '(';
1578 static int
1579 expcmd (struct wordsplit *wsp, char const *str, idx_t len,
1580 struct wordsplit_node **ptail, const char **pend, unsigned flg)
1582 int rc;
1583 idx_t j;
1584 char *value;
1585 struct wordsplit_node *newnode;
1587 str++;
1588 len--;
1590 if (find_closing_paren (str, 0, len, &j, "()"))
1592 _wsplt_seterr (wsp, WRDSE_PAREN);
1593 return 1;
1596 *pend = str + j;
1597 if (wsp->ws_options & WRDSO_ARGV)
1599 struct wordsplit ws;
1601 rc = _wsplt_subsplit (wsp, &ws, str, j, WRDSF_WS | WRDSF_QUOTE, true);
1602 if (rc)
1604 _wsplt_seterr_sub (wsp, &ws);
1605 wordsplit_free (&ws);
1606 return 1;
1608 rc = wsp->ws_command (&value, str, j, ws.ws_wordv, wsp->ws_closure);
1609 wordsplit_free (&ws);
1611 else
1612 rc = wsp->ws_command (&value, str, j, NULL, wsp->ws_closure);
1614 if (rc == WRDSE_NOSPACE)
1615 return _wsplt_nomem (wsp);
1616 else if (rc)
1618 if (rc == WRDSE_USERERR)
1620 if (wsp->ws_errno == WRDSE_USERERR)
1621 free (wsp->ws_usererr);
1622 wsp->ws_usererr = value;
1624 _wsplt_seterr (wsp, rc);
1625 return 1;
1628 if (value)
1630 if (flg & _WSNF_QUOTE)
1632 newnode = wsnode_new (wsp);
1633 if (!newnode)
1634 return 1;
1635 wsnode_insert (wsp, newnode, *ptail);
1636 *ptail = newnode;
1637 newnode->flags = _WSNF_WORD | _WSNF_NOEXPAND | flg;
1638 newnode->v.word = value;
1640 else if (!*value)
1642 free (value);
1643 /* Empty string is a special case */
1644 newnode = wsnode_new (wsp);
1645 if (!newnode)
1646 return 1;
1647 wsnode_insert (wsp, newnode, *ptail);
1648 *ptail = newnode;
1649 newnode->flags = _WSNF_NULL;
1651 else
1653 struct wordsplit ws;
1654 int rc;
1656 rc = _wsplt_subsplit (wsp, &ws, value, strlen (value),
1657 (WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_WS
1658 | WRDSF_QUOTE
1659 | (WSP_RETURN_DELIMS (wsp)
1660 ? WRDSF_RETURN_DELIMS : 0)),
1661 false);
1662 free (value);
1663 if (rc)
1665 _wsplt_seterr_sub (wsp, &ws);
1666 wordsplit_free (&ws);
1667 return 1;
1669 wsnode_insert (wsp, ws.ws_head, *ptail);
1670 *ptail = ws.ws_tail;
1671 ws.ws_head = ws.ws_tail = NULL;
1672 wordsplit_free (&ws);
1675 else
1677 newnode = wsnode_new (wsp);
1678 if (!newnode)
1679 return 1;
1680 wsnode_insert (wsp, newnode, *ptail);
1681 *ptail = newnode;
1682 newnode->flags = _WSNF_NULL;
1684 return 0;
1687 static int
1688 wordsplit_cmdexp (struct wordsplit *wsp)
1690 struct wordsplit_node *p;
1692 for (p = wsp->ws_head; p;)
1694 struct wordsplit_node *next = p->next;
1695 if (!(p->flags & _WSNF_NOEXPAND))
1696 if (!node_expand (wsp, p, begin_cmd_p, expcmd))
1697 return 1;
1698 p = next;
1701 wsnode_nullelim (wsp);
1702 return 0;
1705 /* Strip off any leading and trailing whitespace. This function is called
1706 right after the initial scanning, therefore it assumes that every
1707 node in the list is a text reference node. */
1708 static int
1709 wordsplit_trimws (struct wordsplit *wsp)
1711 struct wordsplit_node *p;
1713 for (p = wsp->ws_head; p; p = p->next)
1715 idx_t n;
1717 if (!(p->flags & _WSNF_QUOTE))
1719 /* Skip leading whitespace: */
1720 for (n = p->v.segm.beg; n < p->v.segm.end && ISWS (wsp->ws_input[n]);
1721 n++)
1723 p->v.segm.beg = n;
1726 while (p->next && (p->flags & _WSNF_JOIN))
1727 p = p->next;
1729 if (p->flags & _WSNF_QUOTE)
1730 continue;
1732 /* Trim trailing whitespace */
1733 for (n = p->v.segm.end;
1734 n > p->v.segm.beg && ISWS (wsp->ws_input[n - 1]); n--);
1735 p->v.segm.end = n;
1736 if (p->v.segm.beg == p->v.segm.end)
1737 p->flags |= _WSNF_NULL;
1740 wsnode_nullelim (wsp);
1741 return 0;
1744 static int
1745 wordsplit_tildexpand (struct wordsplit *wsp)
1747 struct wordsplit_node *p;
1748 char *uname = NULL;
1749 idx_t usize = 0;
1751 for (p = wsp->ws_head; p; p = p->next)
1753 const char *str;
1755 if (p->flags & _WSNF_QUOTE)
1756 continue;
1758 str = wsnode_ptr (wsp, p);
1759 if (str[0] == '~')
1761 idx_t i;
1762 idx_t slen = wsnode_len (p);
1763 struct passwd *pw;
1765 for (i = 1; i < slen && str[i] != '/'; i++)
1767 if (i == slen)
1768 continue;
1769 if (i > 1)
1771 if (i > usize)
1773 if (ckd_add (&usize, usize, usize >> 1) || usize < i)
1774 usize = i;
1775 char *p = irealloc (uname, usize);
1776 if (!p)
1778 free (uname);
1779 return _wsplt_nomem (wsp);
1781 uname = p;
1783 --i;
1784 memcpy (uname, str + 1, i);
1785 uname[i] = '\0';
1786 pw = getpwnam (uname);
1788 else
1789 pw = getpwuid (getuid ());
1791 if (!pw)
1792 continue;
1794 idx_t dlen = strlen (pw->pw_dir);
1795 idx_t size = slen - i + dlen;
1796 char *newstr = imalloc (size);
1797 if (!newstr)
1799 free (uname);
1800 return _wsplt_nomem (wsp);
1802 --size;
1804 memcpy (newstr, pw->pw_dir, dlen);
1805 memcpy (newstr + dlen, str + i + 1, slen - i - 1);
1806 newstr[size] = '\0';
1807 if (p->flags & _WSNF_WORD)
1808 free (p->v.word);
1809 p->v.word = newstr;
1810 p->flags |= _WSNF_WORD;
1813 free (uname);
1814 return 0;
1817 static bool
1818 isglob (char const *s, idx_t l)
1820 for (ptrdiff_t i = l; i--; )
1822 char c = *s++;
1823 if (c && strchr ("*?[", c))
1824 return true;
1826 return false;
1829 static int
1830 wordsplit_pathexpand (struct wordsplit *wsp)
1832 struct wordsplit_node *p, *next;
1833 idx_t slen;
1834 int flags = 0;
1836 #ifdef GLOB_PERIOD
1837 if (wsp->ws_options & WRDSO_DOTGLOB)
1838 flags = GLOB_PERIOD;
1839 #endif
1841 for (p = wsp->ws_head; p; p = next)
1843 const char *str;
1845 next = p->next;
1847 if (p->flags & _WSNF_QUOTE)
1848 continue;
1850 str = wsnode_ptr (wsp, p);
1851 slen = wsnode_len (p);
1853 if (isglob (str, slen))
1855 int i;
1856 glob_t g;
1857 struct wordsplit_node *prev;
1859 char *pattern = imalloc (slen + 1);
1860 if (!pattern)
1861 return _wsplt_nomem (wsp);
1862 memcpy (pattern, str, slen);
1863 pattern[slen] = '\0';
1865 switch (glob (pattern, flags, NULL, &g))
1867 case 0:
1868 free (pattern);
1869 break;
1871 case GLOB_NOSPACE:
1872 free (pattern);
1873 return _wsplt_nomem (wsp);
1875 case GLOB_NOMATCH:
1876 if (wsp->ws_options & WRDSO_NULLGLOB)
1878 wsnode_remove (wsp, p);
1879 wsnode_free (p);
1881 else if (wsp->ws_options & WRDSO_FAILGLOB)
1883 if (wsp->ws_errno == WRDSE_USERERR)
1884 free (wsp->ws_usererr);
1885 char const *msg = _("no files match pattern ");
1886 idx_t msglen = strlen (msg);
1887 char *usererr = irealloc (pattern, msglen + slen + 1);
1888 if (!usererr)
1890 free (pattern);
1891 return _wsplt_nomem (wsp);
1893 memmove (usererr + msglen, usererr, slen + 1);
1894 wsp->ws_usererr = memcpy (usererr, msg, msglen);
1895 return _wsplt_seterr (wsp, WRDSE_USERERR);
1897 free (pattern);
1898 continue;
1900 default:
1901 free (pattern);
1902 return _wsplt_seterr (wsp, WRDSE_GLOBERR);
1905 prev = p;
1906 for (i = 0; i < g.gl_pathc; i++)
1908 struct wordsplit_node *newnode = wsnode_new (wsp);
1909 char *newstr;
1911 if (!newnode)
1912 return 1;
1913 newstr = strdup (g.gl_pathv[i]);
1914 if (!newstr)
1916 wsnode_free (newnode);
1917 return _wsplt_nomem (wsp);
1919 newnode->v.word = newstr;
1920 newnode->flags |= _WSNF_WORD|_WSNF_QUOTE;
1921 wsnode_insert (wsp, newnode, prev);
1922 prev = newnode;
1924 globfree (&g);
1926 wsnode_remove (wsp, p);
1927 wsnode_free (p);
1930 return 0;
1933 static int
1934 skip_sed_expr (char const *command, idx_t i, idx_t len)
1936 int state;
1940 char delim;
1942 if (command[i] == ';')
1943 i++;
1944 if (!(command[i] == 's' && i + 3 < len && c_ispunct (command[i + 1])))
1945 break;
1947 delim = command[++i];
1948 state = 1;
1949 for (i++; i < len; i++)
1951 if (state == 3)
1953 if (command[i] == delim || !c_isalnum (command[i]))
1954 break;
1956 else if (command[i] == '\\')
1957 i++;
1958 else if (command[i] == delim)
1959 state++;
1962 while (state == 3 && i < len && command[i] == ';');
1963 return i;
1966 /* wsp->ws_endp points to a delimiter character. If RETURN_DELIMS
1967 is true, return its value, otherwise return the index past it. */
1968 static idx_t
1969 skip_delim_internal (struct wordsplit *wsp, bool return_delims)
1971 return wsp->ws_endp + !return_delims;
1974 static idx_t
1975 skip_delim (struct wordsplit *wsp)
1977 return skip_delim_internal (wsp, WSP_RETURN_DELIMS (wsp));
1980 static idx_t
1981 skip_delim_real (struct wordsplit *wsp)
1983 return skip_delim_internal (wsp, !!(wsp->ws_flags & WRDSF_RETURN_DELIMS));
1986 #define _WRDS_EOF 0
1987 #define _WRDS_OK 1
1988 #define _WRDS_ERR 2
1990 static bool
1991 scan_qstring (struct wordsplit *wsp, idx_t start, idx_t *end)
1993 idx_t j;
1994 const char *command = wsp->ws_input;
1995 idx_t len = wsp->ws_len;
1996 char q = command[start];
1998 for (j = start + 1; j < len && command[j] != q; j++)
1999 if (q == '"' && command[j] == '\\')
2000 j++;
2001 if (j < len && command[j] == q)
2003 unsigned flags = _WSNF_QUOTE | _WSNF_EMPTYOK;
2004 if (q == '\'')
2005 flags |= _WSNF_NOEXPAND;
2006 if (!wordsplit_add_segm (wsp, start + 1, j, flags))
2007 return false;
2008 *end = j;
2010 else
2012 wsp->ws_endp = start;
2013 _wsplt_seterr (wsp, WRDSE_QUOTE);
2014 return false;
2016 return true;
2019 static int
2020 scan_word (struct wordsplit *wsp, idx_t start, bool consume_all)
2022 idx_t len = wsp->ws_len;
2023 const char *command = wsp->ws_input;
2024 const char *comment = wsp->ws_comment;
2025 bool join = false;
2026 unsigned flags = 0;
2027 struct wordsplit_node *np = wsp->ws_tail;
2029 idx_t i = start;
2031 if (i >= len)
2033 wsp->ws_errno = WRDSE_EOF;
2034 return _WRDS_EOF;
2037 start = i;
2039 if (wsp->ws_flags & WRDSF_SED_EXPR
2040 && command[i] == 's' && i + 3 < len && c_ispunct (command[i + 1]))
2042 flags = _WSNF_SEXP;
2043 i = skip_sed_expr (command, i, len);
2045 else if (consume_all || !ISDELIM (wsp, command[i]))
2047 while (i < len)
2049 if (comment && command[i] && strchr (comment, command[i]) != NULL)
2051 idx_t j;
2052 for (j = i + 1; j < len && command[j] != '\n'; j++)
2054 if (!wordsplit_add_segm (wsp, start, i, 0))
2055 return _WRDS_ERR;
2056 wsp->ws_endp = j;
2057 return _WRDS_OK;
2060 if (wsp->ws_flags & WRDSF_QUOTE)
2062 if (command[i] == '\\')
2064 if (++i == len)
2065 break;
2066 i++;
2067 continue;
2070 if (((wsp->ws_flags & WRDSF_SQUOTE) && command[i] == '\'') ||
2071 ((wsp->ws_flags & WRDSF_DQUOTE) && command[i] == '"'))
2073 if (join && wsp->ws_tail)
2074 wsp->ws_tail->flags |= _WSNF_JOIN;
2075 if (!wordsplit_add_segm (wsp, start, i, _WSNF_JOIN))
2076 return _WRDS_ERR;
2077 if (!scan_qstring (wsp, i, &i))
2078 return _WRDS_ERR;
2079 start = i + 1;
2080 join = true;
2084 if (command[i] == '$')
2086 if (!(wsp->ws_flags & WRDSF_NOVAR)
2087 && command[i+1] == '{'
2088 && !find_closing_paren (command, i + 2, len, &i, "{}"))
2089 continue;
2090 if (!(wsp->ws_flags & WRDSF_NOCMD)
2091 && command[i+1] == '('
2092 && !find_closing_paren (command, i + 2, len, &i, "()"))
2093 continue;
2096 if (!consume_all && ISDELIM (wsp, command[i]))
2097 break;
2098 else
2099 i++;
2102 else if (WSP_RETURN_DELIMS (wsp))
2104 i++;
2105 flags |= _WSNF_DELIM;
2107 else if (!(wsp->ws_flags & WRDSF_SQUEEZE_DELIMS))
2108 flags |= _WSNF_EMPTYOK;
2110 if (join && i > start && wsp->ws_tail)
2111 wsp->ws_tail->flags |= _WSNF_JOIN;
2112 if (!wordsplit_add_segm (wsp, start, i, flags))
2113 return _WRDS_ERR;
2114 wsp->ws_endp = i;
2115 if (wsp->ws_flags & WRDSF_INCREMENTAL)
2116 return _WRDS_EOF;
2118 if (consume_all)
2120 if (!np)
2121 np = wsp->ws_head;
2122 while (np)
2124 np->flags |= _WSNF_QUOTE;
2125 np = np->next;
2129 return _WRDS_OK;
2132 static int
2133 xtonum (char *pval, char const *src, int base, int cnt)
2135 int i;
2136 unsigned char val = 0;
2138 /* The maximum value that a prefix of a number can represent.
2139 This is 31 if base is 8 and UCHAR_MAX == 255,
2140 so that "\400" is treated as "\40" followed by "0", not as "\000". */
2141 unsigned char max_prefix = UCHAR_MAX / base;
2143 for (i = 0; i < cnt && val <= max_prefix; i++)
2145 unsigned char c = src[i];
2146 unsigned char digit;
2148 if (c_isdigit (c))
2149 digit = c - '0';
2150 else if (c_isxdigit (c))
2151 digit = c_toupper (c) - 'A' + 10;
2152 else
2153 break;
2155 if (base <= digit)
2156 break;
2157 val = val * base + digit;
2159 *pval = val;
2160 return i;
2163 #ifdef _WORDSPLIT_EXTRAS
2164 idx_t
2165 wordsplit_c_quoted_length (const char *str, bool quote_hex, bool *quote)
2167 idx_t len = 0;
2169 *quote = false;
2170 for (; *str; str++)
2172 if (strchr (" \"", *str))
2173 *quote = true;
2175 if (*str == ' ')
2176 len++;
2177 else if (*str == '"')
2178 len += 2;
2179 else if (*str != '\t' && *str != '\\' && c_isprint (*str))
2180 len++;
2181 else if (quote_hex)
2182 len += 3;
2183 else
2185 if (wordsplit_c_quote_char (*str))
2186 len += 2;
2187 else
2188 len += 4;
2191 return len;
2193 #endif
2195 static char
2196 wsplt_unquote_char (const char *transtab, char c)
2198 while (*transtab && transtab[1])
2200 if (*transtab++ == c)
2201 return *transtab;
2202 ++transtab;
2204 return '\0';
2207 #ifdef _WORDSPLIT_EXTRAS
2208 static char
2209 wsplt_quote_char (const char *transtab, char c)
2211 for (; *transtab && transtab[1]; transtab += 2)
2213 if (transtab[1] == c)
2214 return *transtab;
2216 return '\0';
2219 char
2220 wordsplit_c_unquote_char (char c)
2222 return wsplt_unquote_char (wordsplit_c_escape_tab, c);
2225 char
2226 wordsplit_c_quote_char (char c)
2228 return wsplt_quote_char (wordsplit_c_escape_tab, c);
2230 #endif
2232 void
2233 wordsplit_string_unquote_copy (struct wordsplit *ws, bool inquote,
2234 char *dst, char const *src, idx_t n)
2236 for (idx_t i = 0; i < n; )
2238 if (src[i] == '\\')
2240 char c;
2241 ++i;
2242 if (WRDSO_ESC_TEST (ws, inquote, WRDSO_XESC)
2243 && (src[i] == 'x' || src[i] == 'X'))
2245 if (n - i < 2)
2247 *dst++ = '\\';
2248 *dst++ = src[i++];
2250 else
2252 int off = xtonum (&c, src + i + 1, 16, 2);
2253 if (off == 0)
2255 *dst++ = '\\';
2256 *dst++ = src[i++];
2258 else
2260 *dst++ = c;
2261 i += off + 1;
2265 else if (WRDSO_ESC_TEST (ws, inquote, WRDSO_OESC)
2266 && c_isdigit (src[i]))
2268 if (n - i < 1)
2270 *dst++ = '\\';
2271 *dst++ = src[i++];
2273 else
2275 int off = xtonum (&c, src + i, 8, 3);
2276 if (off == 0)
2278 *dst++ = '\\';
2279 *dst++ = src[i++];
2281 else
2283 *dst++ = c;
2284 i += off;
2288 else if ((c = wsplt_unquote_char (ws->ws_escape[inquote], src[i])))
2290 *dst++ = c;
2291 ++i;
2293 else
2295 if (WRDSO_ESC_TEST (ws, inquote, WRDSO_BSKEEP))
2296 *dst++ = '\\';
2297 *dst++ = src[i++];
2300 else
2301 *dst++ = src[i++];
2303 *dst = '\0';
2306 #ifdef _WORDSPLIT_EXTRAS
2307 void
2308 wordsplit_c_quote_copy (char *dst, const char *src, bool quote_hex)
2310 for (; *src; src++)
2312 if (*src == '"')
2314 *dst++ = '\\';
2315 *dst++ = *src;
2317 else if (*src != '\t' && *src != '\\' && c_isprint (*src))
2318 *dst++ = *src;
2319 else
2321 unsigned char uc = *src;
2323 if (quote_hex)
2325 static char const hexdigit[16] = "0123456789ABCDEF";
2326 *dst++ = '%';
2327 for (int i = 4; 0 <= i; i -= 4)
2328 *dst++ = hexdigit[(uc >> i) & 0xf];
2330 else
2332 char c = wordsplit_c_quote_char (*src);
2333 *dst++ = '\\';
2334 if (c)
2335 *dst++ = c;
2336 else
2337 for (int i = 6; 0 <= i; i -= 3)
2338 *dst++ = '0' + ((uc >> i) & 7);
2343 #endif
2346 /* This structure describes a single expansion phase */
2347 struct exptab
2349 char const *descr; /* Textual description (for debugging) */
2350 int flag; /* WRDSF_ bit that controls this phase */
2351 int opt; /* Entry-specific options (see EXPOPT_ flags below */
2352 int (*expansion) (struct wordsplit *wsp); /* expansion function */
2355 /* The following options control expansions: */
2356 /* Normally the exptab entry is run if its flag bit is set in struct
2357 wordsplit. The EXPOPT_NEG option negates this test so that expansion
2358 is performed if its associated flag bit is not set in struct wordsplit. */
2359 #define EXPOPT_NEG 0x01
2360 /* All bits in flag must be set in order for entry to match */
2361 #define EXPORT_ALLOF 0x02
2362 /* Coalesce the input list before running the expansion. */
2363 #define EXPOPT_COALESCE 0x04
2365 static struct exptab exptab[] = {
2366 { N_("WS trimming"), WRDSF_WS, 0,
2367 wordsplit_trimws },
2368 { N_("command substitution"), WRDSF_NOCMD, EXPOPT_NEG|EXPOPT_COALESCE,
2369 wordsplit_cmdexp },
2370 { N_("coalesce list"), 0, EXPOPT_NEG|EXPOPT_COALESCE,
2371 NULL },
2372 { N_("tilde expansion"), WRDSF_PATHEXPAND, 0,
2373 wordsplit_tildexpand },
2374 { N_("variable expansion"), WRDSF_NOVAR, EXPOPT_NEG,
2375 wordsplit_varexp },
2376 { N_("quote removal"), 0, EXPOPT_NEG,
2377 wsnode_quoteremoval },
2378 { N_("coalesce list"), 0, EXPOPT_NEG|EXPOPT_COALESCE,
2379 NULL },
2380 { N_("path expansion"), WRDSF_PATHEXPAND, 0,
2381 wordsplit_pathexpand },
2382 { NULL }
2385 static bool
2386 exptab_matches (struct exptab *p, struct wordsplit *wsp)
2388 int result;
2390 result = (wsp->ws_flags & p->flag);
2391 if (p->opt & EXPORT_ALLOF)
2392 result = result == p->flag;
2393 if (p->opt & EXPOPT_NEG)
2394 result = !result;
2396 return !!result;
2399 static int
2400 wordsplit_process_list (struct wordsplit *wsp, idx_t start)
2402 struct exptab *p;
2404 if (wsp->ws_flags & WRDSF_SHOWDBG)
2405 wsp->ws_debug (_("(%02td) Input:%.*s%s;"),
2406 wsp->ws_lvl, printflen (wsp->ws_len), wsp->ws_input,
2407 printfdots (wsp->ws_len));
2409 if ((wsp->ws_flags & WRDSF_NOSPLIT)
2410 || ((wsp->ws_options & WRDSO_MAXWORDS)
2411 && wsp->ws_wordi + 1 == wsp->ws_maxwords))
2413 /* Treat entire input as a single word */
2414 if (scan_word (wsp, start, true) == _WRDS_ERR)
2415 return wsp->ws_errno;
2417 else
2419 int rc;
2421 while ((rc = scan_word (wsp, start, false)) == _WRDS_OK)
2422 start = skip_delim (wsp);
2423 /* Make sure tail element is not joinable */
2424 if (wsp->ws_tail)
2425 wsp->ws_tail->flags &= ~_WSNF_JOIN;
2426 if (rc == _WRDS_ERR)
2427 return wsp->ws_errno;
2430 if (wsp->ws_flags & WRDSF_SHOWDBG)
2432 wsp->ws_debug ("(%02td) %s", wsp->ws_lvl, _("Initial list:"));
2433 wordsplit_dump_nodes (wsp);
2436 for (p = exptab; p->descr; p++)
2438 if (exptab_matches (p, wsp))
2440 if (p->opt & EXPOPT_COALESCE)
2442 if (wsnode_coalesce (wsp))
2443 break;
2444 if (wsp->ws_flags & WRDSF_SHOWDBG)
2446 wsp->ws_debug ("(%02td) %s", wsp->ws_lvl,
2447 _("Coalesced list:"));
2448 wordsplit_dump_nodes (wsp);
2451 if (p->expansion)
2453 if (p->expansion (wsp))
2454 break;
2455 if (wsp->ws_flags & WRDSF_SHOWDBG)
2457 wsp->ws_debug ("(%02td) %s", wsp->ws_lvl, _(p->descr));
2458 wordsplit_dump_nodes (wsp);
2463 return wsp->ws_errno;
2466 WORDSPLIT_EXTRAS_extern
2468 wordsplit_len (char const *command, idx_t length, struct wordsplit *wsp,
2469 unsigned flags)
2471 int rc;
2472 idx_t start;
2474 if (!command)
2476 if (!(flags & WRDSF_INCREMENTAL))
2477 return _wsplt_seterr (wsp, WRDSE_USAGE);
2479 if (wsp->ws_head)
2480 return wordsplit_finish (wsp);
2482 start = skip_delim_real (wsp);
2483 if (wsp->ws_endp == wsp->ws_len)
2484 return _wsplt_seterr (wsp, WRDSE_NOINPUT);
2486 wsp->ws_flags |= WRDSF_REUSE;
2487 wordsplit_init0 (wsp);
2489 else
2491 start = 0;
2492 rc = wordsplit_init (wsp, command, length, flags);
2493 if (rc)
2494 return rc;
2495 wsp->ws_lvl = 0;
2498 rc = wordsplit_process_list (wsp, start);
2499 if (rc)
2500 return rc;
2501 return wordsplit_finish (wsp);
2505 wordsplit (const char *command, struct wordsplit *ws, unsigned flags)
2507 return wordsplit_len (command, command ? strlen (command) : 0, ws, flags);
2510 WORDSPLIT_EXTRAS_extern
2511 void
2512 wordsplit_free_words (struct wordsplit *ws)
2514 idx_t i;
2516 for (i = 0; i < ws->ws_wordc; i++)
2518 char *p = ws->ws_wordv[ws->ws_offs + i];
2519 if (p)
2521 free (p);
2522 ws->ws_wordv[ws->ws_offs + i] = NULL;
2525 ws->ws_wordc = 0;
2528 WORDSPLIT_EXTRAS_extern
2529 void
2530 wordsplit_free_envbuf (struct wordsplit *ws)
2532 if (ws->ws_flags & WRDSF_NOCMD)
2533 return;
2534 if (ws->ws_envbuf)
2536 idx_t i;
2538 for (i = 0; ws->ws_envbuf[i]; i++)
2539 free (ws->ws_envbuf[i]);
2540 free (ws->ws_envbuf);
2541 ws->ws_envidx = ws->ws_envsiz = 0;
2542 ws->ws_envbuf = NULL;
2546 WORDSPLIT_EXTRAS_extern
2547 void
2548 wordsplit_clearerr (struct wordsplit *ws)
2550 if (ws->ws_errno == WRDSE_USERERR)
2551 free (ws->ws_usererr);
2552 ws->ws_usererr = NULL;
2553 ws->ws_errno = WRDSE_OK;
2556 void
2557 wordsplit_free (struct wordsplit *ws)
2559 wordsplit_free_nodes (ws);
2560 wordsplit_free_words (ws);
2561 free (ws->ws_wordv);
2562 ws->ws_wordv = NULL;
2563 wordsplit_free_envbuf (ws);
2566 #ifdef _WORDSPLIT_EXTRAS
2567 void
2568 wordsplit_get_words (struct wordsplit *ws, idx_t *wordc, char ***wordv)
2570 /* Tell the memory manager that ws->ws_wordv can be shrunk. */
2571 char **p = irealloc (ws->ws_wordv,
2572 (ws->ws_wordc + 1) * sizeof (ws->ws_wordv[0]));
2573 *wordv = p ? p : ws->ws_wordv;
2574 *wordc = ws->ws_wordc;
2576 ws->ws_wordv = NULL;
2577 ws->ws_wordc = 0;
2578 ws->ws_wordn = 0;
2580 #endif
2582 static char const *const wordsplit_errstr[] = {
2583 N_("no error"),
2584 N_("missing closing quote"),
2585 N_("memory exhausted"),
2586 N_("invalid wordsplit usage"),
2587 N_("unbalanced curly brace"),
2588 N_("undefined variable"),
2589 N_("input exhausted"),
2590 N_("unbalanced parenthesis"),
2591 N_("globbing error")
2593 enum { wordsplit_nerrs = sizeof wordsplit_errstr / sizeof *wordsplit_errstr };
2595 const char *
2596 wordsplit_strerror (struct wordsplit const *ws)
2598 if (ws->ws_errno == WRDSE_USERERR)
2599 return ws->ws_usererr;
2600 if (ws->ws_errno < wordsplit_nerrs)
2601 return wordsplit_errstr[ws->ws_errno];
2602 return N_("unknown error");
2605 WORDSPLIT_EXTRAS_extern
2606 void
2607 wordsplit_perror (struct wordsplit *wsp)
2609 switch (wsp->ws_errno)
2611 case WRDSE_QUOTE:
2612 wsp->ws_error (_("missing closing %c (start near #%td)"),
2613 wsp->ws_input[wsp->ws_endp],
2614 wsp->ws_endp);
2615 break;
2617 default:
2618 wsp->ws_error ("%s", wordsplit_strerror (wsp));