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