1 /* wordsplit - a word splitter
2    Copyright (C) 2009-2019 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 #include <limits.h>
31 
32 #if ENABLE_NLS
33 # include <gettext.h>
34 #else
35 # define gettext(msgid) msgid
36 #endif
37 #define _(msgid) gettext (msgid)
38 #define N_(msgid) msgid
39 
40 #include <wordsplit.h>
41 
42 #define ISWS(c) ((c)==' '||(c)=='\t'||(c)=='\n')
43 #define ISDELIM(ws,c) \
44   (strchr ((ws)->ws_delim, (c)) != NULL)
45 #define ISPUNCT(c) (strchr("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",(c))!=NULL)
46 #define ISUPPER(c) ('A' <= ((unsigned) (c)) && ((unsigned) (c)) <= 'Z')
47 #define ISLOWER(c) ('a' <= ((unsigned) (c)) && ((unsigned) (c)) <= 'z')
48 #define ISALPHA(c) (ISUPPER(c) || ISLOWER(c))
49 #define ISDIGIT(c) ('0' <= ((unsigned) (c)) && ((unsigned) (c)) <= '9')
50 #define ISXDIGIT(c) (strchr("abcdefABCDEF", c)!=NULL)
51 #define ISALNUM(c) (ISALPHA(c) || ISDIGIT(c))
52 #define ISPRINT(c) (' ' <= ((unsigned) (c)) && ((unsigned) (c)) <= 127)
53 
54 #define ISVARBEG(c) (ISALPHA(c) || c == '_')
55 static inline int
is_name_char(struct wordsplit * wsp,int c)56 is_name_char (struct wordsplit *wsp, int c)
57 {
58 	return ISALNUM (c)
59 		|| c == '_'
60 		|| ((wsp->ws_options & WRDSO_NAMECHAR)
61 		    && strchr (wsp->ws_namechar, c));
62 }
63 
64 #define WSP_RETURN_DELIMS(wsp) \
65  ((wsp)->ws_flags & WRDSF_RETURN_DELIMS || ((wsp)->ws_options & WRDSO_MAXWORDS))
66 
67 #define to_num(c) \
68   (ISDIGIT(c) ? c - '0' : (ISXDIGIT(c) ? toupper(c) - 'A' + 10 : 255 ))
69 
70 #define ALLOC_INIT 128
71 #define ALLOC_INCR 128
72 
73 static void
_wsplt_alloc_die(struct wordsplit * wsp)74 _wsplt_alloc_die (struct wordsplit *wsp)
75 {
76   wsp->ws_error ("%s", _("memory exhausted"));
77   abort ();
78 }
79 
80 static void _wsplt_error (const char *fmt, ...)
81    __attribute__ ((format (printf, 1, 2)));
82 
83 static void
_wsplt_error(const char * fmt,...)84 _wsplt_error (const char *fmt, ...)
85 {
86   va_list ap;
87 
88   va_start (ap, fmt);
89   vfprintf (stderr, fmt, ap);
90   va_end (ap);
91   fputc ('\n', stderr);
92 }
93 
94 static void wordsplit_free_nodes (struct wordsplit *);
95 
96 static int
_wsplt_seterr(struct wordsplit * wsp,int ec)97 _wsplt_seterr (struct wordsplit *wsp, int ec)
98 {
99   wsp->ws_errno = ec;
100   if (wsp->ws_flags & WRDSF_SHOWERR)
101     wordsplit_perror (wsp);
102   if (ec == WRDSE_USAGE)
103     errno = EINVAL;
104   return ec;
105 }
106 
107 static int
_wsplt_nomem(struct wordsplit * wsp)108 _wsplt_nomem (struct wordsplit *wsp)
109 {
110   errno = ENOMEM;
111   wsp->ws_errno = WRDSE_NOSPACE;
112   if (wsp->ws_flags & WRDSF_ENOMEMABRT)
113     wsp->ws_alloc_die (wsp);
114   if (wsp->ws_flags & WRDSF_SHOWERR)
115     wordsplit_perror (wsp);
116   if (!(wsp->ws_flags & WRDSF_REUSE))
117     wordsplit_free (wsp);
118   wordsplit_free_nodes (wsp);
119   return wsp->ws_errno;
120 }
121 
122 static void
_wsplt_store_errctx(struct wordsplit * wsp,char const * str,size_t len)123 _wsplt_store_errctx (struct wordsplit *wsp, char const *str, size_t len)
124 {
125   free (wsp->ws_errctx);
126   wsp->ws_errctx = malloc (len + 1);
127   if (!wsp->ws_errctx)
128     {
129       wsp->ws_error ("%s",
130 		     _("memory exhausted while trying to store error context"));
131     }
132   else
133     {
134       memcpy (wsp->ws_errctx, str, len);
135       wsp->ws_errctx[len] = 0;
136     }
137 }
138 
139 static inline int
_wsplt_setctxerr(struct wordsplit * wsp,int ec,char const * str,size_t len)140 _wsplt_setctxerr (struct wordsplit *wsp, int ec, char const *str, size_t len)
141 {
142   _wsplt_store_errctx (wsp, str, len);
143   return _wsplt_seterr (wsp, ec);
144 }
145 
146 static int wordsplit_run (const char *command, size_t length,
147 			  struct wordsplit *wsp,
148 			  int flags, int lvl);
149 
150 static int wordsplit_init (struct wordsplit *wsp, const char *input, size_t len,
151 			   int flags);
152 static int wordsplit_process_list (struct wordsplit *wsp, size_t start);
153 static int wordsplit_finish (struct wordsplit *wsp);
154 
155 static int
_wsplt_subsplit(struct wordsplit * wsp,struct wordsplit * wss,char const * str,int len,int flags,int finalize)156 _wsplt_subsplit (struct wordsplit *wsp, struct wordsplit *wss,
157 		 char const *str, int len,
158 		 int flags, int finalize)
159 {
160   int rc;
161 
162   wss->ws_delim = wsp->ws_delim;
163   wss->ws_debug = wsp->ws_debug;
164   wss->ws_error = wsp->ws_error;
165   wss->ws_alloc_die = wsp->ws_alloc_die;
166 
167   if (!(flags & WRDSF_NOVAR))
168     {
169       wss->ws_env = wsp->ws_env;
170       wss->ws_getvar = wsp->ws_getvar;
171       flags |= wsp->ws_flags & (WRDSF_ENV | WRDSF_ENV_KV | WRDSF_GETVAR);
172     }
173   if (!(flags & WRDSF_NOCMD))
174     {
175       wss->ws_command = wsp->ws_command;
176     }
177 
178   if ((flags & (WRDSF_NOVAR|WRDSF_NOCMD)) != (WRDSF_NOVAR|WRDSF_NOCMD))
179     {
180       wss->ws_closure = wsp->ws_closure;
181       flags |= wsp->ws_flags & WRDSF_CLOSURE;
182     }
183 
184   wss->ws_options = wsp->ws_options & ~WRDSO_MAXWORDS;
185   wss->ws_namechar = wsp->ws_namechar;
186 
187   flags |= WRDSF_DELIM
188 	 | WRDSF_ALLOC_DIE
189 	 | WRDSF_ERROR
190 	 | WRDSF_DEBUG
191 	 | (wsp->ws_flags & (WRDSF_SHOWDBG | WRDSF_SHOWERR | WRDSF_OPTIONS));
192 
193   rc = wordsplit_init (wss, str, len, flags);
194   if (rc)
195     return rc;
196   wss->ws_lvl = wsp->ws_lvl + 1;
197   rc = wordsplit_process_list (wss, 0);
198   if (rc)
199     {
200       wordsplit_free_nodes (wss);
201       return rc;
202     }
203   if (finalize)
204     {
205       rc = wordsplit_finish (wss);
206       wordsplit_free_nodes (wss);
207     }
208   return rc;
209 }
210 
211 static void
_wsplt_seterr_sub(struct wordsplit * wsp,struct wordsplit * wss)212 _wsplt_seterr_sub (struct wordsplit *wsp, struct wordsplit *wss)
213 {
214   /* Clear user-defined error */
215   if (wsp->ws_errno == WRDSE_USERERR)
216     free (wsp->ws_usererr);
217   /* Copy error state */
218   wsp->ws_errno = wss->ws_errno;
219   if (wss->ws_errno == WRDSE_USERERR)
220     {
221       wsp->ws_usererr = wss->ws_usererr;
222       wss->ws_errno = WRDSE_EOF;
223       wss->ws_usererr = NULL;
224     }
225   /* Copy error context */
226   free (wsp->ws_errctx);
227   wsp->ws_errctx = wss->ws_errctx;
228   wss->ws_errctx = NULL;
229 }
230 
231 static void
wordsplit_init0(struct wordsplit * wsp)232 wordsplit_init0 (struct wordsplit *wsp)
233 {
234   if (wsp->ws_flags & WRDSF_REUSE)
235     {
236       if (!(wsp->ws_flags & WRDSF_APPEND))
237 	wordsplit_free_words (wsp);
238       wordsplit_clearerr (wsp);
239     }
240   else
241     {
242       wsp->ws_wordv = NULL;
243       wsp->ws_wordc = 0;
244       wsp->ws_wordn = 0;
245     }
246 
247   wsp->ws_errno = 0;
248 }
249 
250 char wordsplit_c_escape_tab[] = "\\\\\"\"a\ab\bf\fn\nr\rt\tv\v";
251 
252 static int
wordsplit_init(struct wordsplit * wsp,const char * input,size_t len,int flags)253 wordsplit_init (struct wordsplit *wsp, const char *input, size_t len,
254 		int flags)
255 {
256   wsp->ws_flags = flags;
257 
258   if (!(wsp->ws_flags & WRDSF_ALLOC_DIE))
259     wsp->ws_alloc_die = _wsplt_alloc_die;
260   if (!(wsp->ws_flags & WRDSF_ERROR))
261     wsp->ws_error = _wsplt_error;
262 
263   if (!(wsp->ws_flags & WRDSF_NOVAR))
264     {
265       /* These will be initialized on first variable assignment */
266       wsp->ws_envidx = wsp->ws_envsiz = 0;
267       wsp->ws_envbuf = NULL;
268     }
269 
270   if (!(wsp->ws_flags & WRDSF_NOCMD))
271     {
272       if (!wsp->ws_command)
273 	return _wsplt_seterr (wsp, WRDSE_USAGE);
274     }
275 
276   if (wsp->ws_flags & WRDSF_SHOWDBG)
277     {
278       if (!(wsp->ws_flags & WRDSF_DEBUG))
279 	{
280 	  if (wsp->ws_flags & WRDSF_ERROR)
281 	    wsp->ws_debug = wsp->ws_error;
282 	  else if (wsp->ws_flags & WRDSF_SHOWERR)
283 	    wsp->ws_debug = _wsplt_error;
284 	  else
285 	    wsp->ws_flags &= ~WRDSF_SHOWDBG;
286 	}
287     }
288 
289   wsp->ws_input = input;
290   wsp->ws_len = len;
291 
292   if (!(wsp->ws_flags & WRDSF_DOOFFS))
293     wsp->ws_offs = 0;
294 
295   if (!(wsp->ws_flags & WRDSF_DELIM))
296     wsp->ws_delim = " \t\n";
297 
298   wsp->ws_sep[0] = wsp->ws_delim[0];
299   wsp->ws_sep[1] = 0;
300 
301   if (!(wsp->ws_flags & WRDSF_COMMENT))
302     wsp->ws_comment = NULL;
303 
304   if (!(wsp->ws_flags & WRDSF_CLOSURE))
305     wsp->ws_closure = NULL;
306 
307   if (!(wsp->ws_flags & WRDSF_OPTIONS))
308     wsp->ws_options = 0;
309 
310   if (wsp->ws_flags & WRDSF_ESCAPE)
311     {
312       if (!wsp->ws_escape[WRDSX_WORD])
313 	wsp->ws_escape[WRDSX_WORD] = "";
314       if (!wsp->ws_escape[WRDSX_QUOTE])
315 	wsp->ws_escape[WRDSX_QUOTE] = "";
316     }
317   else
318     {
319       if (wsp->ws_flags & WRDSF_CESCAPES)
320 	{
321 	  wsp->ws_escape[WRDSX_WORD] = wordsplit_c_escape_tab;
322 	  wsp->ws_escape[WRDSX_QUOTE] = wordsplit_c_escape_tab;
323 	  wsp->ws_options |= WRDSO_OESC_QUOTE | WRDSO_OESC_WORD
324 			     | WRDSO_XESC_QUOTE | WRDSO_XESC_WORD;
325 	}
326       else
327 	{
328 	  wsp->ws_escape[WRDSX_WORD] = "";
329 	  wsp->ws_escape[WRDSX_QUOTE] = "\\\\\"\"";
330 	  wsp->ws_options |= WRDSO_BSKEEP_QUOTE;
331 	}
332     }
333 
334   if (!(wsp->ws_options & WRDSO_PARAMV))
335     {
336       wsp->ws_paramv = NULL;
337       wsp->ws_paramc = 0;
338     }
339   wsp->ws_paramidx = wsp->ws_paramsiz = 0;
340   wsp->ws_parambuf = NULL;
341 
342   if (wsp->ws_options & WRDSO_NAMECHAR)
343     {
344       if (wsp->ws_namechar[strcspn(wsp->ws_namechar, "${}*@-+?=")])
345 	return _wsplt_seterr (wsp, WRDSE_USAGE);
346     }
347   else
348     wsp->ws_namechar = NULL;
349 
350   wsp->ws_endp = 0;
351   wsp->ws_wordi = 0;
352 
353   if (wsp->ws_flags & WRDSF_REUSE)
354     wordsplit_free_nodes (wsp);
355   wsp->ws_head = wsp->ws_tail = NULL;
356 
357   wsp->ws_errctx = NULL;
358 
359   wordsplit_init0 (wsp);
360 
361   return 0;
362 }
363 
364 static int
alloc_space(struct wordsplit * wsp,size_t count)365 alloc_space (struct wordsplit *wsp, size_t count)
366 {
367   size_t offs = (wsp->ws_flags & WRDSF_DOOFFS) ? wsp->ws_offs : 0;
368   char **ptr;
369   size_t newalloc;
370 
371   if (wsp->ws_wordv == NULL)
372     {
373       newalloc = offs + count > ALLOC_INIT ? count : ALLOC_INIT;
374       ptr = calloc (newalloc, sizeof (ptr[0]));
375     }
376   else if (wsp->ws_wordn < offs + wsp->ws_wordc + count)
377     {
378       newalloc = offs + wsp->ws_wordc +
379 	(count > ALLOC_INCR ? count : ALLOC_INCR);
380       ptr = realloc (wsp->ws_wordv, newalloc * sizeof (ptr[0]));
381     }
382   else
383     return 0;
384 
385   if (ptr)
386     {
387       wsp->ws_wordn = newalloc;
388       wsp->ws_wordv = ptr;
389     }
390   else
391     return _wsplt_nomem (wsp);
392   return 0;
393 }
394 
395 
396 /* Node state flags */
397 #define _WSNF_NULL     0x01	/* null node (a noop) */
398 #define _WSNF_WORD     0x02	/* node contains word in v.word */
399 #define _WSNF_QUOTE    0x04	/* text is quoted */
400 #define _WSNF_NOEXPAND 0x08	/* text is not subject to expansion */
401 #define _WSNF_JOIN     0x10	/* node must be joined with the next node */
402 #define _WSNF_SEXP     0x20	/* is a sed expression */
403 #define _WSNF_DELIM    0x40     /* node is a delimiter */
404 #define _WSNF_CONST    0x80     /* with _WSNF_WORD: v.word is constant */
405 #define _WSNF_EMPTYOK  0x0100	/* special flag indicating that
406 				   wordsplit_add_segm must add the
407 				   segment even if it is empty */
408 
409 struct wordsplit_node
410 {
411   struct wordsplit_node *prev;	/* Previous element */
412   struct wordsplit_node *next;	/* Next element */
413   int flags;			/* Node flags */
414   union
415   {
416     struct
417     {
418       size_t beg;		/* Start of word in ws_input */
419       size_t end;		/* End of word in ws_input */
420     } segm;
421     char *word;
422   } v;
423 };
424 
425 static const char *
wsnode_flagstr(int flags)426 wsnode_flagstr (int flags)
427 {
428   static char retbuf[7];
429   char *p = retbuf;
430 
431   if (flags & _WSNF_WORD)
432     *p++ = 'w';
433   else if (flags & _WSNF_NULL)
434     *p++ = 'n';
435   else
436     *p++ = '-';
437   if (flags & _WSNF_QUOTE)
438     *p++ = 'q';
439   else
440     *p++ = '-';
441   if (flags & _WSNF_NOEXPAND)
442     *p++ = 'E';
443   else
444     *p++ = '-';
445   if (flags & _WSNF_JOIN)
446     *p++ = 'j';
447   else
448     *p++ = '-';
449   if (flags & _WSNF_SEXP)
450     *p++ = 's';
451   else
452     *p++ = '-';
453   if (flags & _WSNF_DELIM)
454     *p++ = 'd';
455   else
456     *p++ = '-';
457   *p = 0;
458   return retbuf;
459 }
460 
461 static const char *
wsnode_ptr(struct wordsplit * wsp,struct wordsplit_node * p)462 wsnode_ptr (struct wordsplit *wsp, struct wordsplit_node *p)
463 {
464   if (p->flags & _WSNF_NULL)
465     return "";
466   else if (p->flags & _WSNF_WORD)
467     return p->v.word;
468   else
469     return wsp->ws_input + p->v.segm.beg;
470 }
471 
472 static size_t
wsnode_len(struct wordsplit_node * p)473 wsnode_len (struct wordsplit_node *p)
474 {
475   if (p->flags & _WSNF_NULL)
476     return 0;
477   else if (p->flags & _WSNF_WORD)
478     return strlen (p->v.word);
479   else
480     return p->v.segm.end - p->v.segm.beg;
481 }
482 
483 static int
wsnode_new(struct wordsplit * wsp,struct wordsplit_node ** pnode)484 wsnode_new (struct wordsplit *wsp, struct wordsplit_node **pnode)
485 {
486   struct wordsplit_node *node = calloc (1, sizeof (*node));
487   if (!node)
488     return _wsplt_nomem (wsp);
489   *pnode = node;
490   return 0;
491 }
492 
493 static void
wsnode_free(struct wordsplit_node * p)494 wsnode_free (struct wordsplit_node *p)
495 {
496   if ((p->flags & (_WSNF_WORD|_WSNF_CONST)) == _WSNF_WORD)
497     free (p->v.word);
498   free (p);
499 }
500 
501 static void
wsnode_append(struct wordsplit * wsp,struct wordsplit_node * node)502 wsnode_append (struct wordsplit *wsp, struct wordsplit_node *node)
503 {
504   node->next = NULL;
505   node->prev = wsp->ws_tail;
506   if (wsp->ws_tail)
507     wsp->ws_tail->next = node;
508   else
509     wsp->ws_head = node;
510   wsp->ws_tail = node;
511 }
512 
513 static void
wsnode_remove(struct wordsplit * wsp,struct wordsplit_node * node)514 wsnode_remove (struct wordsplit *wsp, struct wordsplit_node *node)
515 {
516   struct wordsplit_node *p;
517 
518   p = node->prev;
519   if (p)
520     {
521       p->next = node->next;
522       if (!node->next)
523 	p->flags &= ~_WSNF_JOIN;
524     }
525   else
526     wsp->ws_head = node->next;
527 
528   p = node->next;
529   if (p)
530     p->prev = node->prev;
531   else
532     wsp->ws_tail = node->prev;
533 
534   wsnode_free (node);
535 }
536 
537 static struct wordsplit_node *
wsnode_tail(struct wordsplit_node * p)538 wsnode_tail (struct wordsplit_node *p)
539 {
540   while (p && p->next)
541     p = p->next;
542   return p;
543 }
544 
545 static void
wsnode_insert(struct wordsplit * wsp,struct wordsplit_node * node,struct wordsplit_node * anchor,int before)546 wsnode_insert (struct wordsplit *wsp, struct wordsplit_node *node,
547 	       struct wordsplit_node *anchor, int before)
548 {
549   if (!wsp->ws_head)
550     {
551       node->next = node->prev = NULL;
552       wsp->ws_head = wsp->ws_tail = node;
553     }
554   else if (before)
555     {
556       if (anchor->prev)
557 	wsnode_insert (wsp, node, anchor->prev, 0);
558       else
559 	{
560 	  struct wordsplit_node *tail = wsnode_tail (node);
561 	  node->prev = NULL;
562 	  tail->next = anchor;
563 	  anchor->prev = tail;
564 	  wsp->ws_head = node;
565 	}
566     }
567   else
568     {
569       struct wordsplit_node *p;
570       struct wordsplit_node *tail = wsnode_tail (node);
571 
572       p = anchor->next;
573       if (p)
574 	p->prev = tail;
575       else
576 	wsp->ws_tail = tail;
577       tail->next = p;
578       node->prev = anchor;
579       anchor->next = node;
580     }
581 }
582 
583 static int
wordsplit_add_segm(struct wordsplit * wsp,size_t beg,size_t end,int flg)584 wordsplit_add_segm (struct wordsplit *wsp, size_t beg, size_t end, int flg)
585 {
586   struct wordsplit_node *node;
587   int rc;
588 
589   if (end == beg && !(flg & _WSNF_EMPTYOK))
590     return 0;
591   rc = wsnode_new (wsp, &node);
592   if (rc)
593     return rc;
594   node->flags = flg & ~(_WSNF_WORD | _WSNF_EMPTYOK);
595   node->v.segm.beg = beg;
596   node->v.segm.end = end;
597   wsnode_append (wsp, node);
598   return 0;
599 }
600 
601 static void
wordsplit_free_nodes(struct wordsplit * wsp)602 wordsplit_free_nodes (struct wordsplit *wsp)
603 {
604   struct wordsplit_node *p;
605 
606   for (p = wsp->ws_head; p;)
607     {
608       struct wordsplit_node *next = p->next;
609       wsnode_free (p);
610       p = next;
611     }
612   wsp->ws_head = wsp->ws_tail = NULL;
613 }
614 
615 static void
wordsplit_dump_nodes(struct wordsplit * wsp)616 wordsplit_dump_nodes (struct wordsplit *wsp)
617 {
618   struct wordsplit_node *p;
619   int n = 0;
620 
621   for (p = wsp->ws_head, n = 0; p; p = p->next, n++)
622     {
623       if (p->flags & _WSNF_WORD)
624 	wsp->ws_debug ("(%02d) %4d: %p: %#04x (%s):%s;",
625 		       wsp->ws_lvl,
626 		       n, p, p->flags, wsnode_flagstr (p->flags), p->v.word);
627       else
628 	wsp->ws_debug ("(%02d) %4d: %p: %#04x (%s):%.*s;",
629 		       wsp->ws_lvl,
630 		       n, p, p->flags, wsnode_flagstr (p->flags),
631 		       (int) (p->v.segm.end - p->v.segm.beg),
632 		       wsp->ws_input + p->v.segm.beg);
633     }
634 }
635 
636 static int
coalesce_segment(struct wordsplit * wsp,struct wordsplit_node * node)637 coalesce_segment (struct wordsplit *wsp, struct wordsplit_node *node)
638 {
639   struct wordsplit_node *p, *end;
640   size_t len = 0;
641   char *buf, *cur;
642   int stop;
643 
644   if (!(node->flags & _WSNF_JOIN))
645     return 0;
646 
647   for (p = node; p && (p->flags & _WSNF_JOIN); p = p->next)
648     {
649       len += wsnode_len (p);
650     }
651   if (p)
652     len += wsnode_len (p);
653   end = p;
654 
655   buf = malloc (len + 1);
656   if (!buf)
657     return _wsplt_nomem (wsp);
658   cur = buf;
659 
660   p = node;
661   for (stop = 0; !stop;)
662     {
663       struct wordsplit_node *next = p->next;
664       const char *str = wsnode_ptr (wsp, p);
665       size_t slen = wsnode_len (p);
666 
667       memcpy (cur, str, slen);
668       cur += slen;
669       if (p != node)
670 	{
671 	  node->flags |= p->flags & _WSNF_QUOTE;
672 	  stop = p == end;
673 	  wsnode_remove (wsp, p);
674 	}
675       p = next;
676     }
677 
678   *cur = 0;
679 
680   node->flags &= ~_WSNF_JOIN;
681 
682   if (node->flags & _WSNF_WORD)
683     free (node->v.word);
684   else
685     node->flags |= _WSNF_WORD;
686   node->v.word = buf;
687   return 0;
688 }
689 
690 static void wordsplit_string_unquote_copy (struct wordsplit *ws, int inquote,
691 					   char *dst, const char *src,
692 					   size_t n);
693 
694 static int
wsnode_quoteremoval(struct wordsplit * wsp)695 wsnode_quoteremoval (struct wordsplit *wsp)
696 {
697   struct wordsplit_node *p;
698 
699   for (p = wsp->ws_head; p; p = p->next)
700     {
701       const char *str = wsnode_ptr (wsp, p);
702       size_t slen = wsnode_len (p);
703       int unquote;
704 
705       if (wsp->ws_flags & WRDSF_QUOTE)
706 	unquote = !(p->flags & _WSNF_NOEXPAND);
707       else
708 	unquote = 0;
709 
710       if (unquote)
711 	{
712 	  if (!(p->flags & _WSNF_WORD))
713 	    {
714 	      char *newstr = malloc (slen + 1);
715 	      if (!newstr)
716 		return _wsplt_nomem (wsp);
717 	      memcpy (newstr, str, slen);
718 	      newstr[slen] = 0;
719 	      p->v.word = newstr;
720 	      p->flags |= _WSNF_WORD;
721 	    }
722 
723 	  wordsplit_string_unquote_copy (wsp, p->flags & _WSNF_QUOTE,
724 					 p->v.word, str, slen);
725 	}
726     }
727   return 0;
728 }
729 
730 static int
wsnode_coalesce(struct wordsplit * wsp)731 wsnode_coalesce (struct wordsplit *wsp)
732 {
733   struct wordsplit_node *p;
734 
735   for (p = wsp->ws_head; p; p = p->next)
736     {
737       if (p->flags & _WSNF_JOIN)
738 	if (coalesce_segment (wsp, p))
739 	  return 1;
740     }
741   return 0;
742 }
743 
744 static int
wsnode_tail_coalesce(struct wordsplit * wsp,struct wordsplit_node * p)745 wsnode_tail_coalesce (struct wordsplit *wsp, struct wordsplit_node *p)
746 {
747   if (p->next)
748     {
749       struct wordsplit_node *np = p;
750       while (np && np->next)
751 	{
752 	  np->flags |= _WSNF_JOIN;
753 	  np = np->next;
754 	}
755       if (coalesce_segment (wsp, p))
756 	return 1;
757     }
758   return 0;
759 }
760 
761 static size_t skip_delim (struct wordsplit *wsp);
762 
763 static int
wordsplit_finish(struct wordsplit * wsp)764 wordsplit_finish (struct wordsplit *wsp)
765 {
766   struct wordsplit_node *p;
767   size_t n;
768   int delim;
769 
770   /* Postprocess delimiters. It would be rather simple, if it weren't for
771      the incremental operation.
772 
773      Nodes of type _WSNF_DELIM get inserted to the node list if either
774      WRDSF_RETURN_DELIMS flag or WRDSO_MAXWORDS option is set.
775 
776      The following cases should be distinguished:
777 
778      1. If both WRDSF_SQUEEZE_DELIMS and WRDSF_RETURN_DELIMS are set, compress
779 	any runs of similar delimiter nodes to a single node. The nodes are
780 	'similar' if they point to the same delimiter character.
781 
782 	If WRDSO_MAXWORDS option is set, stop compressing when
783 	ws_wordi + 1 == ws_maxwords, and coalesce the rest of nodes into
784 	a single last node.
785 
786      2. If WRDSO_MAXWORDS option is set, but WRDSF_RETURN_DELIMS is not,
787 	remove any delimiter nodes. Stop operation when
788 	ws_wordi + 1 == ws_maxwords, and coalesce the rest of nodes into
789 	a single last node.
790 
791      3. If incremental operation is in progress, restart the loop any time
792 	a delimiter node is about to be returned, unless WRDSF_RETURN_DELIMS
793 	is set.
794   */
795  again:
796   delim = 0;         /* Delimiter being processed (if any) */
797   n = 0;             /* Number of words processed so far */
798   p = wsp->ws_head;  /* Current node */
799 
800   while (p)
801     {
802       struct wordsplit_node *next = p->next;
803       if (p->flags & _WSNF_DELIM)
804 	{
805 	  if (wsp->ws_flags & WRDSF_RETURN_DELIMS)
806 	    {
807 	      if (wsp->ws_flags & WRDSF_SQUEEZE_DELIMS)
808 		{
809 		  char const *s = wsnode_ptr (wsp, p);
810 		  if (delim)
811 		    {
812 		      if (delim == *s)
813 			{
814 			  wsnode_remove (wsp, p);
815 			  p = next;
816 			  continue;
817 			}
818 		      else
819 			{
820 			  delim = 0;
821 			  n++; /* Count this node; it will be returned */
822 			}
823 		    }
824 		  else
825 		    {
826 		      delim = *s;
827 		      p = next;
828 		      continue;
829 		    }
830 		}
831 	    }
832 	  else if (wsp->ws_options & WRDSO_MAXWORDS)
833 	    {
834 	      wsnode_remove (wsp, p);
835 	      p = next;
836 	      continue;
837 	    }
838 	}
839       else
840 	{
841 	  if (delim)
842 	    {
843 	      /* Last node was a delimiter or a compressed run of delimiters;
844 		 Count it, and clear the delimiter marker */
845 	      n++;
846 	      delim = 0;
847 	    }
848 	  if (wsp->ws_options & WRDSO_MAXWORDS)
849 	    {
850 	      if (wsp->ws_wordi + n + 1 == wsp->ws_maxwords)
851 		break;
852 	    }
853 	}
854       n++;
855       if (wsp->ws_flags & WRDSF_INCREMENTAL)
856 	p = NULL; /* Break the loop */
857       else
858 	p = next;
859     }
860 
861   if (p)
862     {
863       /* We're here if WRDSO_MAXWORDS is in effect and wsp->ws_maxwords
864 	 words have already been collected. Reconstruct a single final
865 	 node from the remaining nodes. */
866       if (wsnode_tail_coalesce (wsp, p))
867 	return wsp->ws_errno;
868       n++;
869     }
870 
871   if (n == 0)
872     {
873       /* The loop above have eliminated all nodes. */
874       if (wsp->ws_flags & WRDSF_INCREMENTAL)
875 	{
876 	  /* Restart the processing, if there's any input left. */
877 	  if (wsp->ws_endp < wsp->ws_len)
878 	    {
879 	      int rc;
880 	      if (wsp->ws_flags & WRDSF_SHOWDBG)
881 		wsp->ws_debug (_("Restarting"));
882 	      rc = wordsplit_process_list (wsp, skip_delim (wsp));
883 	      if (rc)
884 		return rc;
885 	    }
886 	  else
887 	    {
888 	      wsp->ws_errno = WRDSE_EOF;
889 	      return WRDSE_EOF;
890 	    }
891 	  goto again;
892 	}
893 
894       if (wsp->ws_flags & WRDSF_NOSPLIT)
895 	{
896 	  if (wordsplit_add_segm (wsp, 0, 0, _WSNF_EMPTYOK))
897 	    return wsp->ws_errno;
898 	  n = 1;
899 	}
900     }
901 
902   if (alloc_space (wsp, n + 1))
903     return wsp->ws_errno;
904 
905   while (wsp->ws_head)
906     {
907       const char *str = wsnode_ptr (wsp, wsp->ws_head);
908       size_t slen = wsnode_len (wsp->ws_head);
909       char *newstr = malloc (slen + 1);
910 
911       /* Assign newstr first, even if it is NULL.  This way
912 	 wordsplit_free will work even if we return
913 	 nomem later. */
914       wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc] = newstr;
915       if (!newstr)
916 	return _wsplt_nomem (wsp);
917       memcpy (newstr, str, slen);
918       newstr[slen] = 0;
919 
920       wsnode_remove (wsp, wsp->ws_head);
921 
922       wsp->ws_wordc++;
923       wsp->ws_wordi++;
924 
925       if (wsp->ws_flags & WRDSF_INCREMENTAL)
926 	break;
927     }
928   wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc] = NULL;
929   return 0;
930 }
931 
932 int
wordsplit_append(wordsplit_t * wsp,int argc,char ** argv)933 wordsplit_append (wordsplit_t *wsp, int argc, char **argv)
934 {
935   int rc;
936   size_t i;
937 
938   rc = alloc_space (wsp, wsp->ws_wordc + argc + 1);
939   if (rc)
940     return rc;
941   for (i = 0; i < argc; i++)
942     {
943       char *newstr = strdup (argv[i]);
944       if (!newstr)
945 	{
946 	  while (i > 0)
947 	    {
948 	      free (wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc + i - 1]);
949 	      wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc + i - 1] = NULL;
950 	      i--;
951 	    }
952 	  return _wsplt_nomem (wsp);
953 	}
954       wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc + i] = newstr;
955     }
956   wsp->ws_wordc += i;
957   wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc] = NULL;
958   return 0;
959 }
960 
961 /* Variable expansion */
962 static int
node_split_prefix(struct wordsplit * wsp,struct wordsplit_node ** ptail,struct wordsplit_node * node,size_t beg,size_t len,int flg)963 node_split_prefix (struct wordsplit *wsp,
964 		   struct wordsplit_node **ptail,
965 		   struct wordsplit_node *node,
966 		   size_t beg, size_t len, int flg)
967 {
968   struct wordsplit_node *newnode;
969 
970   if (len == 0)
971     return 0;
972   if (wsnode_new (wsp, &newnode))
973     return 1;
974   wsnode_insert (wsp, newnode, *ptail, 0);
975   if (node->flags & _WSNF_WORD)
976     {
977       const char *str = wsnode_ptr (wsp, node);
978       char *newstr = malloc (len + 1);
979       if (!newstr)
980 	return _wsplt_nomem (wsp);
981       memcpy (newstr, str + beg, len);
982       newstr[len] = 0;
983       newnode->flags = _WSNF_WORD;
984       newnode->v.word = newstr;
985     }
986   else
987     {
988       newnode->v.segm.beg = node->v.segm.beg + beg;
989       newnode->v.segm.end = newnode->v.segm.beg + len;
990     }
991   newnode->flags |= flg;
992   *ptail = newnode;
993   return 0;
994 }
995 
996 static int
find_closing_paren(const char * str,size_t i,size_t len,size_t * poff,char const * paren)997 find_closing_paren (const char *str, size_t i, size_t len, size_t *poff,
998 		    char const *paren)
999 {
1000   enum { st_init, st_squote, st_dquote } state = st_init;
1001   size_t level = 1;
1002 
1003   for (; i < len; i++)
1004     {
1005       switch (state)
1006 	{
1007 	case st_init:
1008 	  switch (str[i])
1009 	    {
1010 	    default:
1011 	      if (str[i] == paren[0])
1012 		{
1013 		  level++;
1014 		  break;
1015 		}
1016 	      else if (str[i] == paren[1])
1017 		{
1018 		  if (--level == 0)
1019 		    {
1020 		      *poff = i;
1021 		      return 0;
1022 		    }
1023 		  break;
1024 		}
1025 	      break;
1026 
1027 	    case '"':
1028 	      state = st_dquote;
1029 	      break;
1030 
1031 	    case '\'':
1032 	      state = st_squote;
1033 	      break;
1034 	    }
1035 	  break;
1036 
1037 	case st_squote:
1038 	  if (str[i] == '\'')
1039 	    state = st_init;
1040 	  break;
1041 
1042 	case st_dquote:
1043 	  if (str[i] == '\\')
1044 	    i++;
1045 	  else if (str[i] == '"')
1046 	    state = st_init;
1047 	  break;
1048 	}
1049     }
1050   return 1;
1051 }
1052 
1053 static char const *
wsplt_env_find(struct wordsplit * wsp,const char * name,size_t len)1054 wsplt_env_find (struct wordsplit *wsp, const char *name, size_t len)
1055 {
1056   size_t i;
1057 
1058   if (!wsp->ws_env)
1059     return NULL;
1060   if (wsp->ws_flags & WRDSF_ENV_KV)
1061     {
1062       /* A key-value pair environment */
1063       for (i = 0; wsp->ws_env[i]; i++)
1064 	{
1065 	  size_t elen = strlen (wsp->ws_env[i]);
1066 	  if (elen == len && memcmp (wsp->ws_env[i], name, elen) == 0)
1067 	    return wsp->ws_env[i + 1];
1068 	  /* Skip the value.  Break the loop if it is NULL. */
1069 	  i++;
1070 	  if (wsp->ws_env[i] == NULL)
1071 	    break;
1072 	}
1073     }
1074   else
1075     {
1076       /* Usual (A=B) environment. */
1077       for (i = 0; wsp->ws_env[i]; i++)
1078 	{
1079 	  size_t j;
1080 	  const char *var = wsp->ws_env[i];
1081 
1082 	  for (j = 0; j < len; j++)
1083 	    if (name[j] != var[j])
1084 	      break;
1085 	  if (j == len && var[j] == '=')
1086 	    return var + j + 1;
1087 	}
1088     }
1089   return NULL;
1090 }
1091 
1092 static int
wsplt_env_lookup(struct wordsplit * wsp,const char * name,size_t len,char ** ret)1093 wsplt_env_lookup (struct wordsplit *wsp, const char *name, size_t len,
1094 		  char **ret)
1095 {
1096   if (wsp->ws_flags & WRDSF_ENV)
1097     {
1098       char const *val = wsplt_env_find (wsp, name, len);
1099       if (val)
1100 	{
1101 	  char *retval = strdup (val);
1102 	  if (!retval)
1103 	    return WRDSE_NOSPACE;
1104 	  *ret = retval;
1105 	  return WRDSE_OK;
1106 	}
1107     }
1108   return WRDSE_UNDEF;
1109 }
1110 
1111 static int
wsplt_env_getvar(struct wordsplit * wsp,const char * name,size_t len,char ** ret)1112 wsplt_env_getvar (struct wordsplit *wsp, const char *name, size_t len,
1113 		  char **ret)
1114 {
1115   return wsp->ws_getvar (ret, name, len, wsp->ws_closure);
1116 }
1117 
1118 static int
wsplt_assign_var(struct wordsplit * wsp,const char * name,size_t namelen,char const * value)1119 wsplt_assign_var (struct wordsplit *wsp, const char *name, size_t namelen,
1120 		  char const *value)
1121 {
1122   int n = (wsp->ws_flags & WRDSF_ENV_KV) ? 2 : 1;
1123   char *v;
1124 
1125   if (wsp->ws_envidx + n >= wsp->ws_envsiz)
1126     {
1127       size_t sz;
1128       char **newenv;
1129 
1130       if (!wsp->ws_envbuf)
1131 	{
1132 	  if (wsp->ws_flags & WRDSF_ENV)
1133 	    {
1134 	      size_t i = 0, j;
1135 
1136 	      if (wsp->ws_env)
1137 		{
1138 		  for (; wsp->ws_env[i]; i++)
1139 		    ;
1140 		}
1141 
1142 	      sz = i + n + 1;
1143 
1144 	      newenv = calloc (sz, sizeof(newenv[0]));
1145 	      if (!newenv)
1146 		return _wsplt_nomem (wsp);
1147 
1148 	      for (j = 0; j < i; j++)
1149 		{
1150 		  newenv[j] = strdup (wsp->ws_env[j]);
1151 		  if (!newenv[j])
1152 		    {
1153 		      for (; j > 1; j--)
1154 			free (newenv[j-1]);
1155 		      free (newenv);
1156 		      return _wsplt_nomem (wsp);
1157 		    }
1158 		}
1159 	      newenv[j] = NULL;
1160 
1161 	      wsp->ws_envbuf = newenv;
1162 	      wsp->ws_envidx = i;
1163 	      wsp->ws_envsiz = sz;
1164 	      wsp->ws_env = (const char**) wsp->ws_envbuf;
1165 	    }
1166 	  else
1167 	    {
1168 	      newenv = calloc (WORDSPLIT_ENV_INIT, sizeof(newenv[0]));
1169 	      if (!newenv)
1170 		return _wsplt_nomem (wsp);
1171 	      wsp->ws_envbuf = newenv;
1172 	      wsp->ws_envidx = 0;
1173 	      wsp->ws_envsiz = WORDSPLIT_ENV_INIT;
1174 	      wsp->ws_env = (const char**) wsp->ws_envbuf;
1175 	      wsp->ws_flags |= WRDSF_ENV;
1176 	    }
1177 	}
1178       else
1179 	{
1180 	  size_t n = wsp->ws_envsiz;
1181 
1182 	  if ((size_t) -1 / 3 * 2 / sizeof (wsp->ws_envbuf[0]) <= n)
1183 	    return _wsplt_nomem (wsp);
1184 	  n += (n + 1) / 2;
1185 	  newenv = realloc (wsp->ws_envbuf, n * sizeof (wsp->ws_envbuf[0]));
1186 	  if (!newenv)
1187 	    return _wsplt_nomem (wsp);
1188 	  wsp->ws_envbuf = newenv;
1189 	  wsp->ws_envsiz = n;
1190 	  wsp->ws_env = (const char**) wsp->ws_envbuf;
1191 	}
1192     }
1193 
1194   if (wsp->ws_flags & WRDSF_ENV_KV)
1195     {
1196       /* A key-value pair environment */
1197       char *p = malloc (namelen + 1);
1198       if (!p)
1199 	return _wsplt_nomem (wsp);
1200       memcpy (p, name, namelen);
1201       p[namelen] = 0;
1202 
1203       v = strdup (value);
1204       if (!v)
1205 	{
1206 	  free (p);
1207 	  return _wsplt_nomem (wsp);
1208 	}
1209       wsp->ws_env[wsp->ws_envidx++] = p;
1210       wsp->ws_env[wsp->ws_envidx++] = v;
1211     }
1212   else
1213     {
1214       v = malloc (namelen + strlen(value) + 2);
1215       if (!v)
1216 	return _wsplt_nomem (wsp);
1217       memcpy (v, name, namelen);
1218       v[namelen++] = '=';
1219       strcpy(v + namelen, value);
1220       wsp->ws_env[wsp->ws_envidx++] = v;
1221     }
1222   wsp->ws_env[wsp->ws_envidx] = NULL;
1223   return WRDSE_OK;
1224 }
1225 
1226 static int
wsplt_assign_param(struct wordsplit * wsp,int param_idx,char * value)1227 wsplt_assign_param (struct wordsplit *wsp, int param_idx, char *value)
1228 {
1229   char *v;
1230 
1231   if (param_idx < 0)
1232     return _wsplt_seterr (wsp, WRDSE_BADPARAM);
1233   if (param_idx == wsp->ws_paramc)
1234     {
1235       char **parambuf;
1236       if (!wsp->ws_parambuf)
1237 	{
1238 	  size_t i;
1239 
1240 	  parambuf = calloc ((size_t)param_idx + 1, sizeof (parambuf[0]));
1241 	  if (!parambuf)
1242 	    return _wsplt_nomem (wsp);
1243 
1244 	  for (i = 0; i < wsp->ws_paramc; i++)
1245 	    {
1246 	      parambuf[i] = strdup (wsp->ws_paramv[i]);
1247 	      if (!parambuf[i])
1248 		{
1249 		  for (; i > 1; i--)
1250 		    free (parambuf[i-1]);
1251 		  free (parambuf);
1252 		  return _wsplt_nomem (wsp);
1253 		}
1254 	    }
1255 
1256 	  wsp->ws_parambuf = parambuf;
1257 	  wsp->ws_paramidx = param_idx;
1258 	  wsp->ws_paramsiz = param_idx + 1;
1259 	}
1260       else
1261 	{
1262 	  size_t n = wsp->ws_paramsiz;
1263 
1264 	  if ((size_t) -1 / 3 * 2 / sizeof (wsp->ws_parambuf[0]) <= n)
1265 	    return _wsplt_nomem (wsp);
1266 	  n += (n + 1) / 2;
1267 	  parambuf = realloc (wsp->ws_parambuf, n * sizeof (wsp->ws_parambuf[0]));
1268 	  if (!parambuf)
1269 	    return _wsplt_nomem (wsp);
1270 	  wsp->ws_parambuf = parambuf;
1271 	  wsp->ws_paramsiz = n;
1272 	  wsp->ws_parambuf[param_idx] = NULL;
1273 	}
1274 
1275       wsp->ws_paramv = (const char**) wsp->ws_parambuf;
1276       wsp->ws_paramc = param_idx + 1;
1277     }
1278   else if (param_idx > wsp->ws_paramc)
1279     return _wsplt_seterr (wsp, WRDSE_BADPARAM);
1280 
1281   v = strdup (value);
1282   if (!v)
1283     return _wsplt_nomem (wsp);
1284 
1285   free (wsp->ws_parambuf[param_idx]);
1286   wsp->ws_parambuf[param_idx] = v;
1287   return WRDSE_OK;
1288 }
1289 
1290 /* Recover from what looked like a variable reference, but turned out
1291    not to be one. STR points to first character after '$'. */
1292 static int
expvar_recover(struct wordsplit * wsp,const char * str,struct wordsplit_node ** ptail,const char ** pend,int flg)1293 expvar_recover (struct wordsplit *wsp, const char *str,
1294 		struct wordsplit_node **ptail, const char **pend, int flg)
1295 {
1296   struct wordsplit_node *newnode;
1297 
1298   if (wsnode_new (wsp, &newnode))
1299     return 1;
1300   wsnode_insert (wsp, newnode, *ptail, 0);
1301   *ptail = newnode;
1302   newnode->flags = _WSNF_WORD | flg;
1303   newnode->v.word = malloc (3);
1304   if (!newnode->v.word)
1305     return _wsplt_nomem (wsp);
1306   newnode->v.word[0] = '$';
1307   newnode->v.word[1] = str[0];
1308   newnode->v.word[2] = 0;
1309   *pend = str;
1310   return 0;
1311 }
1312 
1313 static int
expand_paramv(struct wordsplit * wsp,struct wordsplit_node ** ptail,int flg,int q)1314 expand_paramv (struct wordsplit *wsp, struct wordsplit_node **ptail, int flg,
1315 	       int q)
1316 {
1317   struct wordsplit ws;
1318   int wsflags = WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_QUOTE
1319 	      | (WSP_RETURN_DELIMS (wsp) ? WRDSF_RETURN_DELIMS : 0)
1320 	      | (q ? WRDSF_NOSPLIT : 0);
1321   size_t i;
1322   struct wordsplit_node *tail = *ptail;
1323 
1324   for (i = 0; i < wsp->ws_paramc; i++)
1325     {
1326       struct wordsplit_node *np;
1327       int rc = _wsplt_subsplit (wsp, &ws,
1328 				wsp->ws_paramv[i], strlen (wsp->ws_paramv[i]),
1329 				wsflags, q);
1330       if (rc)
1331 	{
1332 	  _wsplt_seterr_sub (wsp, &ws);
1333 	  wordsplit_free (&ws);
1334 	  return 1;
1335 	}
1336 
1337       if (q)
1338 	{
1339 	  if (wsnode_new (wsp, &np))
1340 	    return 1;
1341 	  wsnode_insert (wsp, np, *ptail, 0);
1342 	  *ptail = np;
1343 	  np->flags = _WSNF_WORD | _WSNF_NOEXPAND | flg;
1344 	  np->v.word = ws.ws_wordv[0];
1345 
1346 	  ws.ws_wordv[0] = NULL;
1347 	}
1348       else
1349 	{
1350 	  for (np = ws.ws_head; np; np = np->next)
1351 	    np->flags = _WSNF_WORD | _WSNF_NOEXPAND | flg;
1352 	  wsnode_insert (wsp, ws.ws_head, *ptail, 0);
1353 	  *ptail = ws.ws_tail;
1354 	  ws.ws_head = ws.ws_tail = NULL;
1355 	}
1356 
1357       wsflags |= WRDSF_REUSE;
1358     }
1359   if (wsflags & WRDSF_REUSE)
1360     wordsplit_free (&ws);
1361 
1362   if (flg & _WSNF_QUOTE)
1363     {
1364       tail = tail->next;
1365       /* Insert delimiters, mark nodes as joinable */
1366       while (tail != *ptail)
1367 	{
1368 	  struct wordsplit_node *next = tail->next;
1369 	  struct wordsplit_node *newnode;
1370 
1371 	  tail->flags |= _WSNF_JOIN;
1372 
1373 	  if (wsnode_new (wsp, &newnode))
1374 	    return 1;
1375 	  newnode->flags = _WSNF_WORD | _WSNF_CONST | _WSNF_NOEXPAND | _WSNF_JOIN;
1376 	  newnode->v.word = wsp->ws_sep;
1377 
1378 	  wsnode_insert (wsp, newnode, tail, 0);
1379 	  tail = next;
1380 	}
1381     }
1382 
1383   return 0;
1384 }
1385 
1386 static int
expvar(struct wordsplit * wsp,const char * str,size_t len,struct wordsplit_node ** ptail,const char ** pend,int flg)1387 expvar (struct wordsplit *wsp, const char *str, size_t len,
1388 	struct wordsplit_node **ptail, const char **pend, int flg)
1389 {
1390   size_t i = 0;
1391   const char *defstr = NULL;
1392   char *value;
1393   struct wordsplit_node *newnode;
1394   const char *start = str - 1;
1395   int rc;
1396   struct wordsplit ws;
1397   int is_param = 0;
1398   long param_idx = 0;
1399 
1400   if (ISVARBEG (str[0]))
1401     {
1402       for (i = 1; i < len; i++)
1403 	if (!is_name_char (wsp, str[i]))
1404 	  break;
1405       *pend = str + i - 1;
1406     }
1407   else if ((wsp->ws_options & WRDSO_PARAMV) && ISDIGIT (str[0]))
1408     {
1409       i = 1;
1410       *pend = str;
1411       is_param = 1;
1412       param_idx = to_num (str[0]);
1413     }
1414   else if ((wsp->ws_options & WRDSO_PARAMV) && str[0] == '#')
1415     {
1416       char b[16];
1417       snprintf (b, sizeof(b), "%d", (int) wsp->ws_paramc);
1418       value = strdup (b);
1419       if (!value)
1420 	return _wsplt_nomem (wsp);
1421       if (wsnode_new (wsp, &newnode))
1422 	return 1;
1423       wsnode_insert (wsp, newnode, *ptail, 0);
1424       *ptail = newnode;
1425       newnode->flags = _WSNF_WORD | _WSNF_NOEXPAND | flg;
1426       newnode->v.word = value;
1427       return 0;
1428     }
1429   else if ((wsp->ws_options & WRDSO_PARAMV) && str[0] == '*')
1430     {
1431       return expand_paramv (wsp, ptail, flg, 0);
1432     }
1433   else if ((wsp->ws_options & WRDSO_PARAMV) && str[0] == '@')
1434     {
1435       return expand_paramv (wsp, ptail, flg, 1);
1436     }
1437   else if (str[0] == '{'
1438 	   && (ISVARBEG (str[1])
1439 	       || (is_param = (((wsp->ws_options & WRDSO_PARAMV)
1440 				&& ISDIGIT (str[1]))
1441 			       || ((wsp->ws_options & WRDSO_PARAM_NEGIDX)
1442 				   && (str[1] == '-'
1443 				       && ISDIGIT (str[2]))))) != 0))
1444     {
1445       int i0 = str[0] == '-' ? 1 : 0;
1446       str++;
1447       len--;
1448       for (i = i0; i < len; i++)
1449 	{
1450 	  if (str[i] == '}')
1451 	    {
1452 	      defstr = NULL;
1453 	      *pend = str + i;
1454 	      break;
1455 	    }
1456 	  else if (strchr ("-+?=", str[i]))
1457 	    {
1458 	      size_t j;
1459 
1460 	      defstr = str + i;
1461 	      if (find_closing_paren (str, i, len, &j, "{}"))
1462 		return _wsplt_seterr (wsp, WRDSE_CBRACE);
1463 	      if (i > i0 + 1 && str[i-1] == ':')
1464 		i--;
1465 	      *pend = str + j;
1466 	      break;
1467 	    }
1468 	  else if (is_param)
1469 	    {
1470 	      if (ISDIGIT (str[i]))
1471 		{
1472 		  param_idx = param_idx * 10 + to_num (str[i]);
1473 		  if ((str[0] == '-' && -param_idx < INT_MIN)
1474 		      || param_idx > INT_MAX)
1475 		    return expvar_recover (wsp, str - 1, ptail, pend, flg);
1476 		}
1477 	      else
1478 		{
1479 		  return expvar_recover (wsp, str - 1, ptail, pend, flg);
1480 		}
1481 	    }
1482 	  else if (!is_name_char (wsp, str[i]))
1483 	    {
1484 	      if (str[i] == ':' && i + 1 < len && strchr ("-+?=", str[i+1]))
1485 		continue;
1486 	      return expvar_recover (wsp, str - 1, ptail, pend, flg);
1487 	    }
1488 	}
1489 
1490       if (is_param && str[0] == '-')
1491 	param_idx = wsp->ws_paramc - param_idx;
1492 
1493       if (i == len)
1494 	return _wsplt_seterr (wsp, WRDSE_CBRACE);
1495     }
1496   else
1497     {
1498       return expvar_recover (wsp, str, ptail, pend, flg);
1499     }
1500 
1501   /* Actually expand the variable */
1502   /* str - start of the variable name
1503      i   - its length
1504      defstr - default replacement str */
1505 
1506   if (is_param)
1507     {
1508       if (param_idx >= 0 && param_idx < wsp->ws_paramc)
1509 	{
1510 	  value = strdup (wsp->ws_paramv[param_idx]);
1511 	  if (!value)
1512 	    rc = WRDSE_NOSPACE;
1513 	  else
1514 	    rc = WRDSE_OK;
1515 	}
1516       else
1517 	rc = WRDSE_UNDEF;
1518     }
1519   else
1520     {
1521       if (wsp->ws_flags & WRDSF_GETVAR)
1522 	{
1523 	  if (wsp->ws_options & WRDSO_GETVARPREF)
1524 	    {
1525 	      rc = wsplt_env_getvar (wsp, str, i, &value);
1526 	      if (rc == WRDSE_UNDEF)
1527 		rc = wsplt_env_lookup (wsp, str, i, &value);
1528 	    }
1529 	  else
1530 	    {
1531 	      rc = wsplt_env_lookup (wsp, str, i, &value);
1532 	      if (rc == WRDSE_UNDEF)
1533 		rc = wsplt_env_getvar (wsp, str, i, &value);
1534 	    }
1535 	}
1536       else
1537 	rc = wsplt_env_lookup (wsp, str, i, &value);
1538     }
1539 
1540   if (rc == WRDSE_OK
1541       && (!value || value[0] == 0)
1542       && defstr && defstr[-1] == ':')
1543     {
1544       free (value);
1545       rc = WRDSE_UNDEF;
1546     }
1547 
1548   switch (rc)
1549     {
1550     case WRDSE_OK:
1551       if (defstr && *defstr == '+')
1552 	{
1553 	  size_t size = *pend - ++defstr;
1554 
1555 	  rc = _wsplt_subsplit (wsp, &ws, defstr, size,
1556 				WRDSF_NOSPLIT | WRDSF_WS | WRDSF_QUOTE |
1557 				(wsp->ws_flags &
1558 				 (WRDSF_NOVAR | WRDSF_NOCMD)), 1);
1559 	  if (rc)
1560 	    return rc;
1561 	  free (value);
1562 	  value = ws.ws_wordv[0];
1563 	  ws.ws_wordv[0] = NULL;
1564 	  wordsplit_free (&ws);
1565 	}
1566       break;
1567 
1568     case WRDSE_UNDEF:
1569       if (defstr)
1570 	{
1571 	  size_t size;
1572 	  if (*defstr == '-' || *defstr == '=')
1573 	    {
1574 	      size = *pend - ++defstr;
1575 
1576 	      rc = _wsplt_subsplit (wsp, &ws, defstr, size,
1577 				    WRDSF_NOSPLIT | WRDSF_WS | WRDSF_QUOTE |
1578 				    (wsp->ws_flags &
1579 				     (WRDSF_NOVAR | WRDSF_NOCMD)),
1580 				    1);
1581 	      if (rc)
1582 		return rc;
1583 
1584 	      value = ws.ws_wordv[0];
1585 	      ws.ws_wordv[0] = NULL;
1586 	      wordsplit_free (&ws);
1587 
1588 	      if (defstr[-1] == '=')
1589 		{
1590 		  if (is_param)
1591 		    rc = wsplt_assign_param (wsp, param_idx, value);
1592 		  else
1593 		    rc = wsplt_assign_var (wsp, str, i, value);
1594 		}
1595 	      if (rc)
1596 		{
1597 		  free (value);
1598 		  return rc;
1599 		}
1600 	    }
1601 	  else
1602 	    {
1603 	      if (*defstr == '?')
1604 		{
1605 		  size = *pend - ++defstr;
1606 		  if (size == 0)
1607 		    wsp->ws_error (_("%.*s: variable null or not set"),
1608 				   (int) i, str);
1609 		  else
1610 		    {
1611 		      rc = _wsplt_subsplit (wsp, &ws, defstr, size,
1612 					    WRDSF_NOSPLIT | WRDSF_WS |
1613 					    WRDSF_QUOTE |
1614 					    (wsp->ws_flags &
1615 					     (WRDSF_NOVAR | WRDSF_NOCMD)),
1616 					    1);
1617 		      if (rc == 0)
1618 			wsp->ws_error ("%.*s: %s",
1619 				       (int) i, str, ws.ws_wordv[0]);
1620 		      else
1621 			wsp->ws_error ("%.*s: %.*s",
1622 				       (int) i, str, (int) size, defstr);
1623 		      wordsplit_free (&ws);
1624 		    }
1625 		}
1626 	      value = NULL;
1627 	    }
1628 	}
1629       else if (wsp->ws_flags & WRDSF_UNDEF)
1630 	{
1631 	  _wsplt_setctxerr (wsp, WRDSE_UNDEF, str, i);
1632 	  return 1;
1633 	}
1634       else
1635 	{
1636 	  if (wsp->ws_flags & WRDSF_WARNUNDEF)
1637 	    wsp->ws_error (_("warning: undefined variable `%.*s'"),
1638 			   (int) i, str);
1639 	  if (wsp->ws_flags & WRDSF_KEEPUNDEF)
1640 	    value = NULL;
1641 	  else
1642 	    {
1643 	      value = strdup ("");
1644 	      if (!value)
1645 		return _wsplt_nomem (wsp);
1646 	    }
1647 	}
1648       break;
1649 
1650     case WRDSE_NOSPACE:
1651       return _wsplt_nomem (wsp);
1652 
1653     case WRDSE_USERERR:
1654       if (wsp->ws_errno == WRDSE_USERERR)
1655 	free (wsp->ws_usererr);
1656       wsp->ws_usererr = value;
1657       /* fall through */
1658     default:
1659       _wsplt_seterr (wsp, rc);
1660       return 1;
1661     }
1662 
1663   if (value)
1664     {
1665       if (flg & _WSNF_QUOTE)
1666 	{
1667 	  if (wsnode_new (wsp, &newnode))
1668 	    return 1;
1669 	  wsnode_insert (wsp, newnode, *ptail, 0);
1670 	  *ptail = newnode;
1671 	  newnode->flags = _WSNF_WORD | _WSNF_NOEXPAND | flg;
1672 	  newnode->v.word = value;
1673 	}
1674       else if (*value == 0)
1675 	{
1676 	  free (value);
1677 	  /* Empty string is a special case */
1678 	  if (wsnode_new (wsp, &newnode))
1679 	    return 1;
1680 	  wsnode_insert (wsp, newnode, *ptail, 0);
1681 	  *ptail = newnode;
1682 	  newnode->flags = _WSNF_NULL;
1683 	}
1684       else
1685 	{
1686 	  struct wordsplit ws;
1687 	  int rc;
1688 
1689 	  rc = _wsplt_subsplit (wsp, &ws, value, strlen (value),
1690 				WRDSF_NOVAR | WRDSF_NOCMD |
1691 				WRDSF_QUOTE
1692 				| (WSP_RETURN_DELIMS (wsp) ? WRDSF_RETURN_DELIMS : 0) ,
1693 				0);
1694 	  free (value);
1695 	  if (rc)
1696 	    {
1697 	      _wsplt_seterr_sub (wsp, &ws);
1698 	      wordsplit_free (&ws);
1699 	      return 1;
1700 	    }
1701 	  wsnode_insert (wsp, ws.ws_head, *ptail, 0);
1702 	  *ptail = ws.ws_tail;
1703 	  ws.ws_head = ws.ws_tail = NULL;
1704 	  wordsplit_free (&ws);
1705 	}
1706     }
1707   else if (wsp->ws_flags & WRDSF_KEEPUNDEF)
1708     {
1709       size_t size = *pend - start + 1;
1710 
1711       if (wsnode_new (wsp, &newnode))
1712 	return 1;
1713       wsnode_insert (wsp, newnode, *ptail, 0);
1714       *ptail = newnode;
1715       newnode->flags = _WSNF_WORD | _WSNF_NOEXPAND | flg;
1716       newnode->v.word = malloc (size + 1);
1717       if (!newnode->v.word)
1718 	return _wsplt_nomem (wsp);
1719       memcpy (newnode->v.word, start, size);
1720       newnode->v.word[size] = 0;
1721     }
1722   else
1723     {
1724       if (wsnode_new (wsp, &newnode))
1725 	return 1;
1726       wsnode_insert (wsp, newnode, *ptail, 0);
1727       *ptail = newnode;
1728       newnode->flags = _WSNF_NULL;
1729     }
1730   return 0;
1731 }
1732 
1733 static int
begin_var_p(int c)1734 begin_var_p (int c)
1735 {
1736   return memchr ("{#@*", c, 4) != NULL || ISVARBEG (c) || ISDIGIT (c);
1737 }
1738 
1739 static int
node_expand(struct wordsplit * wsp,struct wordsplit_node * node,int (* beg_p)(int),int (* ws_exp_fn)(struct wordsplit * wsp,const char * str,size_t len,struct wordsplit_node ** ptail,const char ** pend,int flg))1740 node_expand (struct wordsplit *wsp, struct wordsplit_node *node,
1741 	     int (*beg_p) (int),
1742 	     int (*ws_exp_fn) (struct wordsplit *wsp,
1743 			       const char *str, size_t len,
1744 			       struct wordsplit_node **ptail,
1745 			       const char **pend,
1746 			       int flg))
1747 {
1748   const char *str = wsnode_ptr (wsp, node);
1749   size_t slen = wsnode_len (node);
1750   const char *end = str + slen;
1751   const char *p;
1752   size_t off = 0;
1753   struct wordsplit_node *tail = node;
1754 
1755   for (p = str; p < end; p++)
1756     {
1757       if (*p == '\\')
1758 	{
1759 	  p++;
1760 	  continue;
1761 	}
1762       if (*p == '$' && beg_p (p[1]))
1763 	{
1764 	  size_t n = p - str;
1765 
1766 	  if (tail != node)
1767 	    tail->flags |= _WSNF_JOIN;
1768 	  if (node_split_prefix (wsp, &tail, node, off, n, _WSNF_JOIN))
1769 	    return 1;
1770 	  p++;
1771 	  if (ws_exp_fn (wsp, p, slen - n, &tail, &p,
1772 			 node->flags & (_WSNF_JOIN | _WSNF_QUOTE)))
1773 	    return 1;
1774 	  off += p - str + 1;
1775 	  str = p + 1;
1776 	}
1777     }
1778   if (p > str)
1779     {
1780       if (tail != node)
1781 	tail->flags |= _WSNF_JOIN;
1782       if (node_split_prefix (wsp, &tail, node, off, p - str,
1783 			     node->flags & (_WSNF_JOIN|_WSNF_QUOTE)))
1784 	return 1;
1785     }
1786   if (tail != node)
1787     {
1788       wsnode_remove (wsp, node);
1789     }
1790   return 0;
1791 }
1792 
1793 /* Remove NULL nodes from the list */
1794 static void
wsnode_nullelim(struct wordsplit * wsp)1795 wsnode_nullelim (struct wordsplit *wsp)
1796 {
1797   struct wordsplit_node *p;
1798 
1799   for (p = wsp->ws_head; p;)
1800     {
1801       struct wordsplit_node *next = p->next;
1802       if (p->flags & _WSNF_DELIM && p->prev)
1803 	p->prev->flags &= ~_WSNF_JOIN;
1804       if (p->flags & _WSNF_NULL)
1805 	{
1806 	  wsnode_remove (wsp, p);
1807 	}
1808       p = next;
1809     }
1810 }
1811 
1812 static int
wordsplit_varexp(struct wordsplit * wsp)1813 wordsplit_varexp (struct wordsplit *wsp)
1814 {
1815   struct wordsplit_node *p;
1816 
1817   for (p = wsp->ws_head; p;)
1818     {
1819       struct wordsplit_node *next = p->next;
1820       if (!(p->flags & (_WSNF_NOEXPAND|_WSNF_DELIM)))
1821 	if (node_expand (wsp, p, begin_var_p, expvar))
1822 	  return 1;
1823       p = next;
1824     }
1825 
1826   wsnode_nullelim (wsp);
1827   return 0;
1828 }
1829 
1830 static int
begin_cmd_p(int c)1831 begin_cmd_p (int c)
1832 {
1833   return c == '(';
1834 }
1835 
1836 static int
expcmd(struct wordsplit * wsp,const char * str,size_t len,struct wordsplit_node ** ptail,const char ** pend,int flg)1837 expcmd (struct wordsplit *wsp, const char *str, size_t len,
1838 	struct wordsplit_node **ptail, const char **pend, int flg)
1839 {
1840   int rc;
1841   size_t j;
1842   char *value;
1843   struct wordsplit_node *newnode;
1844   struct wordsplit ws;
1845 
1846   str++;
1847   len--;
1848 
1849   if (find_closing_paren (str, 0, len, &j, "()"))
1850     {
1851       _wsplt_seterr (wsp, WRDSE_PAREN);
1852       return 1;
1853     }
1854 
1855   *pend = str + j;
1856   rc = _wsplt_subsplit (wsp, &ws, str, j, WRDSF_WS | WRDSF_QUOTE, 1);
1857   if (rc)
1858     {
1859       _wsplt_seterr_sub (wsp, &ws);
1860       wordsplit_free (&ws);
1861       return 1;
1862     }
1863   rc = wsp->ws_command (&value, str, j, ws.ws_wordv, wsp->ws_closure);
1864   wordsplit_free (&ws);
1865 
1866   if (rc == WRDSE_NOSPACE)
1867     return _wsplt_nomem (wsp);
1868   else if (rc)
1869     {
1870       if (rc == WRDSE_USERERR)
1871 	{
1872 	  if (wsp->ws_errno == WRDSE_USERERR)
1873 	    free (wsp->ws_usererr);
1874 	  wsp->ws_usererr = value;
1875 	}
1876       _wsplt_seterr (wsp, rc);
1877       return 1;
1878     }
1879 
1880   if (value)
1881     {
1882       if (flg & _WSNF_QUOTE)
1883 	{
1884 	  if (wsnode_new (wsp, &newnode))
1885 	    return 1;
1886 	  wsnode_insert (wsp, newnode, *ptail, 0);
1887 	  *ptail = newnode;
1888 	  newnode->flags = _WSNF_WORD | _WSNF_NOEXPAND | flg;
1889 	  newnode->v.word = value;
1890 	}
1891       else if (*value == 0)
1892 	{
1893 	  free (value);
1894 	  /* Empty string is a special case */
1895 	  if (wsnode_new (wsp, &newnode))
1896 	    return 1;
1897 	  wsnode_insert (wsp, newnode, *ptail, 0);
1898 	  *ptail = newnode;
1899 	  newnode->flags = _WSNF_NULL;
1900 	}
1901       else
1902 	{
1903 	  struct wordsplit ws;
1904 	  int rc;
1905 
1906 	  rc = _wsplt_subsplit (wsp, &ws, value, strlen (value),
1907 				WRDSF_NOVAR | WRDSF_NOCMD
1908 				| WRDSF_WS | WRDSF_QUOTE
1909 				| (WSP_RETURN_DELIMS (wsp) ? WRDSF_RETURN_DELIMS : 0),
1910 				0);
1911 	  free (value);
1912 	  if (rc)
1913 	    {
1914 	      _wsplt_seterr_sub (wsp, &ws);
1915 	      wordsplit_free (&ws);
1916 	      return 1;
1917 	    }
1918 	  wsnode_insert (wsp, ws.ws_head, *ptail, 0);
1919 	  *ptail = ws.ws_tail;
1920 	  ws.ws_head = ws.ws_tail = NULL;
1921 	  wordsplit_free (&ws);
1922 	}
1923     }
1924   else
1925     {
1926       if (wsnode_new (wsp, &newnode))
1927 	return 1;
1928       wsnode_insert (wsp, newnode, *ptail, 0);
1929       *ptail = newnode;
1930       newnode->flags = _WSNF_NULL;
1931     }
1932   return 0;
1933 }
1934 
1935 static int
wordsplit_cmdexp(struct wordsplit * wsp)1936 wordsplit_cmdexp (struct wordsplit *wsp)
1937 {
1938   struct wordsplit_node *p;
1939 
1940   for (p = wsp->ws_head; p;)
1941     {
1942       struct wordsplit_node *next = p->next;
1943       if (!(p->flags & _WSNF_NOEXPAND))
1944 	if (node_expand (wsp, p, begin_cmd_p, expcmd))
1945 	  return 1;
1946       p = next;
1947     }
1948 
1949   wsnode_nullelim (wsp);
1950   return 0;
1951 }
1952 
1953 /* Strip off any leading and trailing whitespace.  This function is called
1954    right after the initial scanning, therefore it assumes that every
1955    node in the list is a text reference node. */
1956 static int
wordsplit_trimws(struct wordsplit * wsp)1957 wordsplit_trimws (struct wordsplit *wsp)
1958 {
1959   struct wordsplit_node *p;
1960 
1961   for (p = wsp->ws_head; p; p = p->next)
1962     {
1963       size_t n;
1964 
1965       if (!(p->flags & _WSNF_QUOTE))
1966 	{
1967 	  /* Skip leading whitespace: */
1968 	  for (n = p->v.segm.beg; n < p->v.segm.end && ISWS (wsp->ws_input[n]);
1969 	       n++)
1970 	    ;
1971 	  p->v.segm.beg = n;
1972 	}
1973 
1974       while (p->next && (p->flags & _WSNF_JOIN))
1975 	p = p->next;
1976 
1977       if (p->flags & _WSNF_QUOTE)
1978 	continue;
1979 
1980       /* Trim trailing whitespace */
1981       for (n = p->v.segm.end;
1982 	   n > p->v.segm.beg && ISWS (wsp->ws_input[n - 1]); n--);
1983       p->v.segm.end = n;
1984       if (p->v.segm.beg == p->v.segm.end)
1985 	p->flags |= _WSNF_NULL;
1986     }
1987 
1988   wsnode_nullelim (wsp);
1989   return 0;
1990 }
1991 
1992 static int
wordsplit_tildexpand(struct wordsplit * wsp)1993 wordsplit_tildexpand (struct wordsplit *wsp)
1994 {
1995   struct wordsplit_node *p;
1996   char *uname = NULL;
1997   size_t usize = 0;
1998 
1999   for (p = wsp->ws_head; p; p = p->next)
2000     {
2001       const char *str;
2002 
2003       if (p->flags & _WSNF_QUOTE)
2004 	continue;
2005 
2006       str = wsnode_ptr (wsp, p);
2007       if (str[0] == '~')
2008 	{
2009 	  size_t i, size, dlen;
2010 	  size_t slen = wsnode_len (p);
2011 	  struct passwd *pw;
2012 	  char *newstr;
2013 
2014 	  for (i = 1; i < slen && str[i] != '/'; i++)
2015 	    ;
2016 	  if (i == slen)
2017 	    continue;
2018 	  if (i > 1)
2019 	    {
2020 	      if (i > usize)
2021 		{
2022 		  char *p = realloc (uname, i);
2023 		  if (!p)
2024 		    {
2025 		      free (uname);
2026 		      return _wsplt_nomem (wsp);
2027 		    }
2028 		  uname = p;
2029 		  usize = i;
2030 		}
2031 	      --i;
2032 	      memcpy (uname, str + 1, i);
2033 	      uname[i] = 0;
2034 	      pw = getpwnam (uname);
2035 	    }
2036 	  else
2037 	    pw = getpwuid (getuid ());
2038 
2039 	  if (!pw)
2040 	    continue;
2041 
2042 	  dlen = strlen (pw->pw_dir);
2043 	  size = slen - i + dlen;
2044 	  newstr = malloc (size);
2045 	  if (!newstr)
2046 	    {
2047 	      free (uname);
2048 	      return _wsplt_nomem (wsp);
2049 	    }
2050 	  --size;
2051 
2052 	  memcpy (newstr, pw->pw_dir, dlen);
2053 	  memcpy (newstr + dlen, str + i + 1, slen - i - 1);
2054 	  newstr[size] = 0;
2055 	  if (p->flags & _WSNF_WORD)
2056 	    free (p->v.word);
2057 	  p->v.word = newstr;
2058 	  p->flags |= _WSNF_WORD;
2059 	}
2060     }
2061   free (uname);
2062   return 0;
2063 }
2064 
2065 static int
isglob(const char * s,int l)2066 isglob (const char *s, int l)
2067 {
2068   while (l--)
2069     {
2070       if (strchr ("*?[", *s++))
2071 	return 1;
2072     }
2073   return 0;
2074 }
2075 
2076 static int
wordsplit_pathexpand(struct wordsplit * wsp)2077 wordsplit_pathexpand (struct wordsplit *wsp)
2078 {
2079   struct wordsplit_node *p, *next;
2080   char *pattern = NULL;
2081   size_t patsize = 0;
2082   size_t slen;
2083   int flags = 0;
2084 
2085 #ifdef GLOB_PERIOD
2086   if (wsp->ws_options & WRDSO_DOTGLOB)
2087     flags = GLOB_PERIOD;
2088 #endif
2089 
2090   for (p = wsp->ws_head; p; p = next)
2091     {
2092       const char *str;
2093 
2094       next = p->next;
2095 
2096       if (p->flags & _WSNF_QUOTE)
2097 	continue;
2098 
2099       str = wsnode_ptr (wsp, p);
2100       slen = wsnode_len (p);
2101 
2102       if (isglob (str, slen))
2103 	{
2104 	  int i;
2105 	  glob_t g;
2106 	  struct wordsplit_node *prev;
2107 
2108 	  if (slen + 1 > patsize)
2109 	    {
2110 	      char *p = realloc (pattern, slen + 1);
2111 	      if (!p)
2112 		return _wsplt_nomem (wsp);
2113 	      pattern = p;
2114 	      patsize = slen + 1;
2115 	    }
2116 	  memcpy (pattern, str, slen);
2117 	  pattern[slen] = 0;
2118 
2119 	  switch (glob (pattern, flags, NULL, &g))
2120 	    {
2121 	    case 0:
2122 	      break;
2123 
2124 	    case GLOB_NOSPACE:
2125 	      free (pattern);
2126 	      return _wsplt_nomem (wsp);
2127 
2128 	    case GLOB_NOMATCH:
2129 	      if (wsp->ws_options & WRDSO_NULLGLOB)
2130 		{
2131 		  wsnode_remove (wsp, p);
2132 		}
2133 	      else if (wsp->ws_options & WRDSO_FAILGLOB)
2134 		{
2135 		  char buf[128];
2136 		  if (wsp->ws_errno == WRDSE_USERERR)
2137 		    free (wsp->ws_usererr);
2138 		  snprintf (buf, sizeof (buf), _("no files match pattern %s"),
2139 			    pattern);
2140 		  free (pattern);
2141 		  wsp->ws_usererr = strdup (buf);
2142 		  if (!wsp->ws_usererr)
2143 		    return _wsplt_nomem (wsp);
2144 		  else
2145 		    return _wsplt_seterr (wsp, WRDSE_USERERR);
2146 		}
2147 	      continue;
2148 
2149 	    default:
2150 	      free (pattern);
2151 	      return _wsplt_setctxerr (wsp, WRDSE_GLOBERR, pattern, slen);
2152 	    }
2153 
2154 	  prev = p;
2155 	  for (i = 0; i < g.gl_pathc; i++)
2156 	    {
2157 	      struct wordsplit_node *newnode;
2158 	      char *newstr;
2159 
2160 	      if (wsnode_new (wsp, &newnode))
2161 		return 1;
2162 	      newstr = strdup (g.gl_pathv[i]);
2163 	      if (!newstr)
2164 		return _wsplt_nomem (wsp);
2165 	      newnode->v.word = newstr;
2166 	      newnode->flags |= _WSNF_WORD|_WSNF_QUOTE;
2167 	      wsnode_insert (wsp, newnode, prev, 0);
2168 	      prev = newnode;
2169 	    }
2170 	  globfree (&g);
2171 
2172 	  wsnode_remove (wsp, p);
2173 	}
2174     }
2175   free (pattern);
2176   return 0;
2177 }
2178 
2179 static int
skip_sed_expr(const char * command,size_t i,size_t len)2180 skip_sed_expr (const char *command, size_t i, size_t len)
2181 {
2182   int state;
2183 
2184   do
2185     {
2186       int delim;
2187 
2188       if (command[i] == ';')
2189 	i++;
2190       if (!(command[i] == 's' && i + 3 < len && ISPUNCT (command[i + 1])))
2191 	break;
2192 
2193       delim = command[++i];
2194       state = 1;
2195       for (i++; i < len; i++)
2196 	{
2197 	  if (state == 3)
2198 	    {
2199 	      if (command[i] == delim || !ISALNUM (command[i]))
2200 		break;
2201 	    }
2202 	  else if (command[i] == '\\')
2203 	    i++;
2204 	  else if (command[i] == delim)
2205 	    state++;
2206 	}
2207     }
2208   while (state == 3 && i < len && command[i] == ';');
2209   return i;
2210 }
2211 
2212 /* wsp->ws_endp points to a delimiter character. If RETURN_DELIMS
2213    is true, return its value, otherwise return the index past it. */
2214 static inline size_t
skip_delim_internal(struct wordsplit * wsp,int return_delims)2215 skip_delim_internal (struct wordsplit *wsp, int return_delims)
2216 {
2217   return return_delims ? wsp->ws_endp : wsp->ws_endp + 1;
2218 }
2219 
2220 static inline size_t
skip_delim(struct wordsplit * wsp)2221 skip_delim (struct wordsplit *wsp)
2222 {
2223   return skip_delim_internal (wsp, WSP_RETURN_DELIMS (wsp));
2224 }
2225 
2226 static inline size_t
skip_delim_real(struct wordsplit * wsp)2227 skip_delim_real (struct wordsplit *wsp)
2228 {
2229   return skip_delim_internal (wsp, wsp->ws_flags & WRDSF_RETURN_DELIMS);
2230 }
2231 
2232 #define _WRDS_EOF   0
2233 #define _WRDS_OK    1
2234 #define _WRDS_ERR   2
2235 
2236 static int
scan_qstring(struct wordsplit * wsp,size_t start,size_t * end)2237 scan_qstring (struct wordsplit *wsp, size_t start, size_t *end)
2238 {
2239   size_t j;
2240   const char *command = wsp->ws_input;
2241   size_t len = wsp->ws_len;
2242   char q = command[start];
2243 
2244   for (j = start + 1; j < len && command[j] != q; j++)
2245     if (q == '"' && command[j] == '\\')
2246       j++;
2247   if (j < len && command[j] == q)
2248     {
2249       int flags = _WSNF_QUOTE | _WSNF_EMPTYOK;
2250       if (q == '\'')
2251 	flags |= _WSNF_NOEXPAND;
2252       if (wordsplit_add_segm (wsp, start + 1, j, flags))
2253 	return _WRDS_ERR;
2254       *end = j;
2255     }
2256   else
2257     {
2258       wsp->ws_endp = start;
2259       _wsplt_seterr (wsp, WRDSE_QUOTE);
2260       return _WRDS_ERR;
2261     }
2262   return 0;
2263 }
2264 
2265 static int
scan_word(struct wordsplit * wsp,size_t start,int consume_all)2266 scan_word (struct wordsplit *wsp, size_t start, int consume_all)
2267 {
2268   size_t len = wsp->ws_len;
2269   const char *command = wsp->ws_input;
2270   const char *comment = wsp->ws_comment;
2271   int join = 0;
2272   int flags = 0;
2273   struct wordsplit_node *np = wsp->ws_tail;
2274 
2275   size_t i = start;
2276 
2277   if (i >= len)
2278     {
2279       wsp->ws_errno = WRDSE_EOF;
2280       return _WRDS_EOF;
2281     }
2282 
2283   start = i;
2284 
2285   if (wsp->ws_flags & WRDSF_SED_EXPR
2286       && command[i] == 's' && i + 3 < len && ISPUNCT (command[i + 1]))
2287     {
2288       flags = _WSNF_SEXP;
2289       i = skip_sed_expr (command, i, len);
2290     }
2291   else if (consume_all || !ISDELIM (wsp, command[i]))
2292     {
2293       while (i < len)
2294 	{
2295 	  if (comment && strchr (comment, command[i]) != NULL)
2296 	    {
2297 	      size_t j;
2298 	      for (j = i + 1; j < len && command[j] != '\n'; j++)
2299 		;
2300 	      if (wordsplit_add_segm (wsp, start, i, 0))
2301 		return _WRDS_ERR;
2302 	      wsp->ws_endp = j;
2303 	      return _WRDS_OK;
2304 	    }
2305 
2306 	  if (wsp->ws_flags & WRDSF_QUOTE)
2307 	    {
2308 	      if (command[i] == '\\')
2309 		{
2310 		  if (++i == len)
2311 		    break;
2312 		  i++;
2313 		  continue;
2314 		}
2315 
2316 	      if (((wsp->ws_flags & WRDSF_SQUOTE) && command[i] == '\'') ||
2317 		  ((wsp->ws_flags & WRDSF_DQUOTE) && command[i] == '"'))
2318 		{
2319 		  if (join && wsp->ws_tail)
2320 		    wsp->ws_tail->flags |= _WSNF_JOIN;
2321 		  if (wordsplit_add_segm (wsp, start, i, _WSNF_JOIN))
2322 		    return _WRDS_ERR;
2323 		  if (scan_qstring (wsp, i, &i))
2324 		    return _WRDS_ERR;
2325 		  start = i + 1;
2326 		  join = 1;
2327 		}
2328 	    }
2329 
2330 	  if (command[i] == '$')
2331 	    {
2332 	      if ((!(wsp->ws_flags & WRDSF_NOVAR)
2333 		   || (wsp->ws_options & WRDSO_NOVARSPLIT))
2334 		  && command[i+1] == '{'
2335 		  && find_closing_paren (command, i + 2, len, &i, "{}") == 0)
2336 		continue;
2337 	      if ((!(wsp->ws_flags & WRDSF_NOCMD)
2338 		   || (wsp->ws_options & WRDSO_NOCMDSPLIT))
2339 		  && command[i+1] == '('
2340 		  && find_closing_paren (command, i + 2, len, &i, "()") == 0)
2341 		continue;
2342 	    }
2343 
2344 	  if (!consume_all && ISDELIM (wsp, command[i]))
2345 	    break;
2346 	  else
2347 	    i++;
2348 	}
2349     }
2350   else if (WSP_RETURN_DELIMS (wsp))
2351     {
2352       i++;
2353       flags |= _WSNF_DELIM;
2354     }
2355   else if (!(wsp->ws_flags & WRDSF_SQUEEZE_DELIMS))
2356     flags |= _WSNF_EMPTYOK;
2357 
2358   if (join && i > start && wsp->ws_tail)
2359     wsp->ws_tail->flags |= _WSNF_JOIN;
2360   if (wordsplit_add_segm (wsp, start, i, flags))
2361     return _WRDS_ERR;
2362   wsp->ws_endp = i;
2363   if (wsp->ws_flags & WRDSF_INCREMENTAL)
2364     return _WRDS_EOF;
2365 
2366   if (consume_all)
2367     {
2368       if (!np)
2369 	np = wsp->ws_head;
2370       while (np)
2371 	{
2372 	  np->flags |= _WSNF_QUOTE;
2373 	  np = np->next;
2374 	}
2375     }
2376 
2377   return _WRDS_OK;
2378 }
2379 
2380 static int
xtonum(int * pval,const char * src,int base,int cnt)2381 xtonum (int *pval, const char *src, int base, int cnt)
2382 {
2383   int i, val;
2384 
2385   for (i = 0, val = 0; i < cnt; i++, src++)
2386     {
2387       int n = *(unsigned char *) src;
2388       if (n > 127 || (n = to_num (n)) >= base)
2389 	break;
2390       val = val * base + n;
2391     }
2392   *pval = val;
2393   return i;
2394 }
2395 
2396 size_t
wordsplit_c_quoted_length(const char * str,int quote_hex,int * quote)2397 wordsplit_c_quoted_length (const char *str, int quote_hex, int *quote)
2398 {
2399   size_t len = 0;
2400 
2401   *quote = 0;
2402   for (; *str; str++)
2403     {
2404       if (strchr (" \"", *str))
2405 	*quote = 1;
2406 
2407       if (*str == ' ')
2408 	len++;
2409       else if (*str == '"')
2410 	len += 2;
2411       else if (*str != '\t' && *str != '\\' && ISPRINT (*str))
2412 	len++;
2413       else if (quote_hex)
2414 	len += 3;
2415       else
2416 	{
2417 	  if (wordsplit_c_quote_char (*str))
2418 	    len += 2;
2419 	  else
2420 	    len += 4;
2421 	}
2422     }
2423   return len;
2424 }
2425 
2426 static int
wsplt_unquote_char(const char * transtab,int c)2427 wsplt_unquote_char (const char *transtab, int c)
2428 {
2429   while (*transtab && transtab[1])
2430     {
2431       if (*transtab++ == c)
2432 	return *transtab;
2433       ++transtab;
2434     }
2435   return 0;
2436 }
2437 
2438 static int
wsplt_quote_char(const char * transtab,int c)2439 wsplt_quote_char (const char *transtab, int c)
2440 {
2441   for (; *transtab && transtab[1]; transtab += 2)
2442     {
2443       if (transtab[1] == c)
2444 	return *transtab;
2445     }
2446   return 0;
2447 }
2448 
2449 int
wordsplit_c_unquote_char(int c)2450 wordsplit_c_unquote_char (int c)
2451 {
2452   return wsplt_unquote_char (wordsplit_c_escape_tab, c);
2453 }
2454 
2455 int
wordsplit_c_quote_char(int c)2456 wordsplit_c_quote_char (int c)
2457 {
2458   return wsplt_quote_char (wordsplit_c_escape_tab, c);
2459 }
2460 
2461 void
wordsplit_string_unquote_copy(struct wordsplit * ws,int inquote,char * dst,const char * src,size_t n)2462 wordsplit_string_unquote_copy (struct wordsplit *ws, int inquote,
2463 			       char *dst, const char *src, size_t n)
2464 {
2465   int i = 0;
2466   int c;
2467 
2468   inquote = !!inquote;
2469   while (i < n)
2470     {
2471       if (src[i] == '\\')
2472 	{
2473 	  ++i;
2474 	  if (WRDSO_ESC_TEST (ws, inquote, WRDSO_XESC)
2475 	      && (src[i] == 'x' || src[i] == 'X'))
2476 	    {
2477 	      if (n - i < 2)
2478 		{
2479 		  *dst++ = '\\';
2480 		  *dst++ = src[i++];
2481 		}
2482 	      else
2483 		{
2484 		  int off = xtonum (&c, src + i + 1,
2485 				    16, 2);
2486 		  if (off == 0)
2487 		    {
2488 		      *dst++ = '\\';
2489 		      *dst++ = src[i++];
2490 		    }
2491 		  else
2492 		    {
2493 		      *dst++ = c;
2494 		      i += off + 1;
2495 		    }
2496 		}
2497 	    }
2498 	  else if (WRDSO_ESC_TEST (ws, inquote, WRDSO_OESC)
2499 		   && (unsigned char) src[i] < 128 && ISDIGIT (src[i]))
2500 	    {
2501 	      if (n - i < 1)
2502 		{
2503 		  *dst++ = '\\';
2504 		  *dst++ = src[i++];
2505 		}
2506 	      else
2507 		{
2508 		  int off = xtonum (&c, src + i, 8, 3);
2509 		  if (off == 0)
2510 		    {
2511 		      *dst++ = '\\';
2512 		      *dst++ = src[i++];
2513 		    }
2514 		  else
2515 		    {
2516 		      *dst++ = c;
2517 		      i += off;
2518 		    }
2519 		}
2520 	    }
2521 	  else if ((c = wsplt_unquote_char (ws->ws_escape[inquote], src[i])))
2522 	    {
2523 	      *dst++ = c;
2524 	      ++i;
2525 	    }
2526 	  else
2527 	    {
2528 	      if (WRDSO_ESC_TEST (ws, inquote, WRDSO_BSKEEP))
2529 		*dst++ = '\\';
2530 	      *dst++ = src[i++];
2531 	    }
2532 	}
2533       else
2534 	*dst++ = src[i++];
2535     }
2536   *dst = 0;
2537 }
2538 
2539 void
wordsplit_c_quote_copy(char * dst,const char * src,int quote_hex)2540 wordsplit_c_quote_copy (char *dst, const char *src, int quote_hex)
2541 {
2542   for (; *src; src++)
2543     {
2544       if (*src == '"')
2545 	{
2546 	  *dst++ = '\\';
2547 	  *dst++ = *src;
2548 	}
2549       else if (*src != '\t' && *src != '\\' && ISPRINT (*src))
2550 	*dst++ = *src;
2551       else
2552 	{
2553 	  char tmp[4];
2554 
2555 	  if (quote_hex)
2556 	    {
2557 	      snprintf (tmp, sizeof tmp, "%%%02X", *(unsigned char *) src);
2558 	      memcpy (dst, tmp, 3);
2559 	      dst += 3;
2560 	    }
2561 	  else
2562 	    {
2563 	      int c = wordsplit_c_quote_char (*src);
2564 	      *dst++ = '\\';
2565 	      if (c)
2566 		*dst++ = c;
2567 	      else
2568 		{
2569 		  snprintf (tmp, sizeof tmp, "%03o", *(unsigned char *) src);
2570 		  memcpy (dst, tmp, 3);
2571 		  dst += 3;
2572 		}
2573 	    }
2574 	}
2575     }
2576 }
2577 
2578 
2579 /* This structure describes a single expansion phase */
2580 struct exptab
2581 {
2582   char const *descr; /* Textual description (for debugging) */
2583   int flag;          /* WRDSF_ bit that controls this phase */
2584   int opt;           /* Entry-specific options (see EXPOPT_ flags below */
2585   int (*expansion) (struct wordsplit *wsp); /* expansion function */
2586 };
2587 
2588 /* The following options control expansions: */
2589 /* Normally the exptab entry is run if its flag bit is set in struct
2590    wordsplit.  The EXPOPT_NEG option negates this test so that expansion
2591    is performed if its associated flag bit is not set in struct wordsplit. */
2592 #define EXPOPT_NEG      0x01
2593 /* All bits in flag must be set in order for entry to match */
2594 #define EXPORT_ALLOF    0x02
2595 /* Coalesce the input list before running the expansion. */
2596 #define EXPOPT_COALESCE 0x04
2597 
2598 static struct exptab exptab[] = {
2599   { N_("WS trimming"),          WRDSF_WS,         0,
2600     wordsplit_trimws },
2601   { N_("command substitution"), WRDSF_NOCMD,      EXPOPT_NEG|EXPOPT_COALESCE,
2602     wordsplit_cmdexp },
2603   { N_("coalesce list"),        0,                EXPOPT_NEG|EXPOPT_COALESCE,
2604     NULL },
2605   { N_("tilde expansion"),      WRDSF_PATHEXPAND, 0,
2606     wordsplit_tildexpand },
2607   { N_("variable expansion"),   WRDSF_NOVAR,      EXPOPT_NEG,
2608     wordsplit_varexp },
2609   { N_("quote removal"),        0,                EXPOPT_NEG,
2610     wsnode_quoteremoval },
2611   { N_("coalesce list"),        0,                EXPOPT_NEG|EXPOPT_COALESCE,
2612     NULL },
2613   { N_("path expansion"),       WRDSF_PATHEXPAND, 0,
2614     wordsplit_pathexpand },
2615   { NULL }
2616 };
2617 
2618 static inline int
exptab_matches(struct exptab * p,struct wordsplit * wsp)2619 exptab_matches(struct exptab *p, struct wordsplit *wsp)
2620 {
2621   int result;
2622 
2623   result = (wsp->ws_flags & p->flag);
2624   if (p->opt & EXPORT_ALLOF)
2625     result = result == p->flag;
2626   if (p->opt & EXPOPT_NEG)
2627     result = !result;
2628 
2629   return result;
2630 }
2631 
2632 static int
wordsplit_process_list(struct wordsplit * wsp,size_t start)2633 wordsplit_process_list (struct wordsplit *wsp, size_t start)
2634 {
2635   struct exptab *p;
2636 
2637   if (wsp->ws_flags & WRDSF_SHOWDBG)
2638     wsp->ws_debug (_("(%02d) Input:%.*s;"),
2639 		   wsp->ws_lvl, (int) wsp->ws_len, wsp->ws_input);
2640 
2641   if ((wsp->ws_flags & WRDSF_NOSPLIT)
2642       || ((wsp->ws_options & WRDSO_MAXWORDS)
2643 	  && wsp->ws_wordi + 1 == wsp->ws_maxwords))
2644     {
2645       /* Treat entire input as a single word */
2646       if (scan_word (wsp, start, 1) == _WRDS_ERR)
2647 	return wsp->ws_errno;
2648     }
2649   else
2650     {
2651       int rc;
2652 
2653       while ((rc = scan_word (wsp, start, 0)) == _WRDS_OK)
2654 	start = skip_delim (wsp);
2655       /* Make sure tail element is not joinable */
2656       if (wsp->ws_tail)
2657 	wsp->ws_tail->flags &= ~_WSNF_JOIN;
2658       if (rc == _WRDS_ERR)
2659 	return wsp->ws_errno;
2660     }
2661 
2662   if (wsp->ws_flags & WRDSF_SHOWDBG)
2663     {
2664       wsp->ws_debug ("(%02d) %s", wsp->ws_lvl, _("Initial list:"));
2665       wordsplit_dump_nodes (wsp);
2666     }
2667 
2668   for (p = exptab; p->descr; p++)
2669     {
2670       if (exptab_matches(p, wsp))
2671 	{
2672 	  if (p->opt & EXPOPT_COALESCE)
2673 	    {
2674 	      if (wsnode_coalesce (wsp))
2675 		break;
2676 	      if (wsp->ws_flags & WRDSF_SHOWDBG)
2677 		{
2678 		  wsp->ws_debug ("(%02d) %s", wsp->ws_lvl,
2679 				 _("Coalesced list:"));
2680 		  wordsplit_dump_nodes (wsp);
2681 		}
2682 	    }
2683 	  if (p->expansion)
2684 	    {
2685 	      if (p->expansion (wsp))
2686 		break;
2687 	      if (wsp->ws_flags & WRDSF_SHOWDBG)
2688 		{
2689 		  wsp->ws_debug ("(%02d) %s", wsp->ws_lvl, _(p->descr));
2690 		  wordsplit_dump_nodes (wsp);
2691 		}
2692 	    }
2693 	}
2694     }
2695 
2696   return wsp->ws_errno;
2697 }
2698 
2699 static int
wordsplit_run(const char * command,size_t length,struct wordsplit * wsp,int flags,int lvl)2700 wordsplit_run (const char *command, size_t length, struct wordsplit *wsp,
2701 	       int flags, int lvl)
2702 {
2703   int rc;
2704   size_t start;
2705 
2706   /* Initialize error context early */
2707   wsp->ws_errctx = NULL;
2708   if (!command)
2709     {
2710       if (!(flags & WRDSF_INCREMENTAL))
2711 	return _wsplt_seterr (wsp, WRDSE_USAGE);
2712 
2713       if (wsp->ws_head)
2714 	return wordsplit_finish (wsp);
2715 
2716       start = skip_delim_real (wsp);
2717       if (wsp->ws_endp == wsp->ws_len)
2718 	return _wsplt_seterr (wsp, WRDSE_NOINPUT);
2719 
2720       wsp->ws_flags |= WRDSF_REUSE;
2721       wordsplit_init0 (wsp);
2722     }
2723   else
2724     {
2725       start = 0;
2726       rc = wordsplit_init (wsp, command, length, flags);
2727       if (rc)
2728 	return rc;
2729       wsp->ws_lvl = lvl;
2730     }
2731 
2732   rc = wordsplit_process_list (wsp, start);
2733   if (rc)
2734     return rc;
2735   return wordsplit_finish (wsp);
2736 }
2737 
2738 int
wordsplit_len(const char * command,size_t length,struct wordsplit * wsp,int flags)2739 wordsplit_len (const char *command, size_t length, struct wordsplit *wsp,
2740 	       int flags)
2741 {
2742   return wordsplit_run (command, length, wsp, flags, 0);
2743 }
2744 
2745 int
wordsplit(const char * command,struct wordsplit * ws,int flags)2746 wordsplit (const char *command, struct wordsplit *ws, int flags)
2747 {
2748   return wordsplit_len (command, command ? strlen (command) : 0, ws, flags);
2749 }
2750 
2751 void
wordsplit_free_words(struct wordsplit * ws)2752 wordsplit_free_words (struct wordsplit *ws)
2753 {
2754   size_t i;
2755 
2756   for (i = 0; i < ws->ws_wordc; i++)
2757     {
2758       char *p = ws->ws_wordv[ws->ws_offs + i];
2759       if (p)
2760 	{
2761 	  free (p);
2762 	  ws->ws_wordv[ws->ws_offs + i] = NULL;
2763 	}
2764     }
2765   ws->ws_wordc = 0;
2766 }
2767 
2768 void
wordsplit_free_envbuf(struct wordsplit * ws)2769 wordsplit_free_envbuf (struct wordsplit *ws)
2770 {
2771   if (!(ws->ws_flags & WRDSF_ENV))
2772     return;
2773   if (ws->ws_envbuf)
2774     {
2775       size_t i;
2776 
2777       for (i = 0; ws->ws_envbuf[i]; i++)
2778 	free (ws->ws_envbuf[i]);
2779       free (ws->ws_envbuf);
2780       ws->ws_envidx = ws->ws_envsiz = 0;
2781       ws->ws_envbuf = NULL;
2782     }
2783 }
2784 
2785 void
wordsplit_free_parambuf(struct wordsplit * ws)2786 wordsplit_free_parambuf (struct wordsplit *ws)
2787 {
2788   if (!(ws->ws_options & WRDSO_PARAMV))
2789     return;
2790   if (ws->ws_parambuf)
2791     {
2792       size_t i;
2793 
2794       for (i = 0; ws->ws_parambuf[i]; i++)
2795 	free (ws->ws_parambuf[i]);
2796       free (ws->ws_parambuf);
2797       ws->ws_paramidx = ws->ws_paramsiz = 0;
2798       ws->ws_parambuf = NULL;
2799     }
2800 }
2801 
2802 void
wordsplit_clearerr(struct wordsplit * ws)2803 wordsplit_clearerr (struct wordsplit *ws)
2804 {
2805   if (ws->ws_errno == WRDSE_USERERR)
2806     free (ws->ws_usererr);
2807   ws->ws_usererr = NULL;
2808 
2809   free (ws->ws_errctx);
2810   ws->ws_errctx = NULL;
2811 
2812   ws->ws_errno = WRDSE_OK;
2813 }
2814 
2815 void
wordsplit_free(struct wordsplit * ws)2816 wordsplit_free (struct wordsplit *ws)
2817 {
2818   if (ws->ws_errno == WRDSE_USAGE)
2819     /* Usage error: the structure is not properly initialized and there's
2820        nothing to free. */
2821     return;
2822   wordsplit_clearerr (ws);
2823   wordsplit_free_nodes (ws);
2824   wordsplit_free_words (ws);
2825   free (ws->ws_wordv);
2826   ws->ws_wordv = NULL;
2827   wordsplit_free_envbuf (ws);
2828   wordsplit_free_parambuf (ws);
2829 }
2830 
2831 int
wordsplit_get_words(struct wordsplit * ws,size_t * wordc,char *** wordv)2832 wordsplit_get_words (struct wordsplit *ws, size_t *wordc, char ***wordv)
2833 {
2834   char **p = realloc (ws->ws_wordv,
2835 		      (ws->ws_wordc + 1) * sizeof (ws->ws_wordv[0]));
2836   if (!p)
2837     return -1;
2838   *wordv = p;
2839   *wordc = ws->ws_wordc;
2840 
2841   ws->ws_wordv = NULL;
2842   ws->ws_wordc = 0;
2843   ws->ws_wordn = 0;
2844 
2845   return 0;
2846 }
2847 
2848 const char *_wordsplit_errstr[] = {
2849   N_("no error"),
2850   N_("missing closing quote"),
2851   N_("memory exhausted"),
2852   N_("invalid wordsplit usage"),
2853   N_("unbalanced curly brace"),
2854   N_("undefined variable"),
2855   N_("input exhausted"),
2856   N_("unbalanced parenthesis"),
2857   N_("globbing error"),
2858   N_("user-defined error"),
2859   N_("invalid parameter number in assignment")
2860 };
2861 int _wordsplit_nerrs =
2862   sizeof (_wordsplit_errstr) / sizeof (_wordsplit_errstr[0]);
2863 
2864 const char *
wordsplit_strerror(struct wordsplit * ws)2865 wordsplit_strerror (struct wordsplit *ws)
2866 {
2867   if (ws->ws_errno == WRDSE_USERERR)
2868     return ws->ws_usererr;
2869   if (ws->ws_errno < _wordsplit_nerrs)
2870     return _wordsplit_errstr[ws->ws_errno];
2871   return N_("unknown error");
2872 }
2873 
2874 void
wordsplit_perror(struct wordsplit * wsp)2875 wordsplit_perror (struct wordsplit *wsp)
2876 {
2877   switch (wsp->ws_errno)
2878     {
2879     case WRDSE_QUOTE:
2880       wsp->ws_error (_("missing closing %c (start near #%lu)"),
2881 		     wsp->ws_input[wsp->ws_endp],
2882 		     (unsigned long) wsp->ws_endp);
2883       break;
2884 
2885     default:
2886       if (wsp->ws_errctx)
2887 	wsp->ws_error ("%s: %s", wordsplit_strerror (wsp), wsp->ws_errctx);
2888       else
2889 	wsp->ws_error ("%s", wordsplit_strerror (wsp));
2890     }
2891 }
2892