1/* POSIX.2 wordexp implementation.
2 Copyright (C) 1997-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#include <ctype.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <fnmatch.h>
23#include <glob.h>
24#include <libintl.h>
25#include <paths.h>
26#include <pwd.h>
27#include <stdbool.h>
28#include <stdio.h>
29#include <string.h>
30#include <sys/param.h>
31#include <sys/wait.h>
32#include <unistd.h>
33#include <wordexp.h>
34#include <spawn.h>
35#include <scratch_buffer.h>
36#include <_itoa.h>
37#include <assert.h>
38
39/*
40 * This is a recursive-descent-style word expansion routine.
41 */
42
43/* These variables are defined and initialized in the startup code. */
44extern int __libc_argc attribute_hidden;
45extern char **__libc_argv attribute_hidden;
46
47/* Some forward declarations */
48static int parse_dollars (char **word, size_t *word_length, size_t *max_length,
49 const char *words, size_t *offset, int flags,
50 wordexp_t *pwordexp, const char *ifs,
51 const char *ifs_white, int quoted);
52static int parse_backtick (char **word, size_t *word_length,
53 size_t *max_length, const char *words,
54 size_t *offset, int flags, wordexp_t *pwordexp,
55 const char *ifs, const char *ifs_white);
56static int parse_dquote (char **word, size_t *word_length, size_t *max_length,
57 const char *words, size_t *offset, int flags,
58 wordexp_t *pwordexp, const char *ifs,
59 const char *ifs_white);
60static int eval_expr (char *expr, long int *result);
61
62/* The w_*() functions manipulate word lists. */
63
64#define W_CHUNK (100)
65
66/* Result of w_newword will be ignored if it's the last word. */
67static inline char *
68w_newword (size_t *actlen, size_t *maxlen)
69{
70 *actlen = *maxlen = 0;
71 return NULL;
72}
73
74static char *
75w_addchar (char *buffer, size_t *actlen, size_t *maxlen, char ch)
76 /* (lengths exclude trailing zero) */
77{
78 /* Add a character to the buffer, allocating room for it if needed. */
79
80 if (*actlen == *maxlen)
81 {
82 char *old_buffer = buffer;
83 assert (buffer == NULL || *maxlen != 0);
84 *maxlen += W_CHUNK;
85 buffer = (char *) realloc (ptr: buffer, size: 1 + *maxlen);
86
87 if (buffer == NULL)
88 free (ptr: old_buffer);
89 }
90
91 if (buffer != NULL)
92 {
93 buffer[*actlen] = ch;
94 buffer[++(*actlen)] = '\0';
95 }
96
97 return buffer;
98}
99
100static char *
101w_addmem (char *buffer, size_t *actlen, size_t *maxlen, const char *str,
102 size_t len)
103{
104 /* Add a string to the buffer, allocating room for it if needed.
105 */
106 if (*actlen + len > *maxlen)
107 {
108 char *old_buffer = buffer;
109 assert (buffer == NULL || *maxlen != 0);
110 *maxlen += MAX (2 * len, W_CHUNK);
111 buffer = realloc (ptr: old_buffer, size: 1 + *maxlen);
112
113 if (buffer == NULL)
114 free (ptr: old_buffer);
115 }
116
117 if (buffer != NULL)
118 {
119 *((char *) __mempcpy (&buffer[*actlen], str, len)) = '\0';
120 *actlen += len;
121 }
122
123 return buffer;
124}
125
126static char *
127w_addstr (char *buffer, size_t *actlen, size_t *maxlen, const char *str)
128 /* (lengths exclude trailing zero) */
129{
130 /* Add a string to the buffer, allocating room for it if needed.
131 */
132 size_t len;
133
134 assert (str != NULL); /* w_addstr only called from this file */
135 len = strlen (str);
136
137 return w_addmem (buffer, actlen, maxlen, str, len);
138}
139
140static int
141w_addword (wordexp_t *pwordexp, char *word)
142{
143 /* Add a word to the wordlist */
144 size_t num_p;
145 char **new_wordv;
146 bool allocated = false;
147
148 /* Internally, NULL acts like "". Convert NULLs to "" before
149 * the caller sees them.
150 */
151 if (word == NULL)
152 {
153 word = __strdup ("");
154 if (word == NULL)
155 goto no_space;
156 allocated = true;
157 }
158
159 num_p = 2 + pwordexp->we_wordc + pwordexp->we_offs;
160 new_wordv = realloc (ptr: pwordexp->we_wordv, size: sizeof (char *) * num_p);
161 if (new_wordv != NULL)
162 {
163 pwordexp->we_wordv = new_wordv;
164 pwordexp->we_wordv[pwordexp->we_offs + pwordexp->we_wordc++] = word;
165 pwordexp->we_wordv[pwordexp->we_offs + pwordexp->we_wordc] = NULL;
166 return 0;
167 }
168
169 if (allocated)
170 free (ptr: word);
171
172no_space:
173 return WRDE_NOSPACE;
174}
175
176/* The parse_*() functions should leave *offset being the offset in 'words'
177 * to the last character processed.
178 */
179
180static int
181parse_backslash (char **word, size_t *word_length, size_t *max_length,
182 const char *words, size_t *offset)
183{
184 /* We are poised _at_ a backslash, not in quotes */
185
186 switch (words[1 + *offset])
187 {
188 case 0:
189 /* Backslash is last character of input words */
190 return WRDE_SYNTAX;
191
192 case '\n':
193 ++(*offset);
194 break;
195
196 default:
197 *word = w_addchar (buffer: *word, actlen: word_length, maxlen: max_length, ch: words[1 + *offset]);
198 if (*word == NULL)
199 return WRDE_NOSPACE;
200
201 ++(*offset);
202 break;
203 }
204
205 return 0;
206}
207
208static int
209parse_qtd_backslash (char **word, size_t *word_length, size_t *max_length,
210 const char *words, size_t *offset)
211{
212 /* We are poised _at_ a backslash, inside quotes */
213
214 switch (words[1 + *offset])
215 {
216 case 0:
217 /* Backslash is last character of input words */
218 return WRDE_SYNTAX;
219
220 case '\n':
221 ++(*offset);
222 break;
223
224 case '$':
225 case '`':
226 case '"':
227 case '\\':
228 *word = w_addchar (buffer: *word, actlen: word_length, maxlen: max_length, ch: words[1 + *offset]);
229 if (*word == NULL)
230 return WRDE_NOSPACE;
231
232 ++(*offset);
233 break;
234
235 default:
236 *word = w_addchar (buffer: *word, actlen: word_length, maxlen: max_length, ch: words[*offset]);
237 if (*word != NULL)
238 *word = w_addchar (buffer: *word, actlen: word_length, maxlen: max_length, ch: words[1 + *offset]);
239
240 if (*word == NULL)
241 return WRDE_NOSPACE;
242
243 ++(*offset);
244 break;
245 }
246
247 return 0;
248}
249
250static int
251parse_tilde (char **word, size_t *word_length, size_t *max_length,
252 const char *words, size_t *offset, size_t wordc)
253{
254 /* We are poised _at_ a tilde */
255 size_t i;
256
257 if (*word_length != 0)
258 {
259 if (!((*word)[*word_length - 1] == '=' && wordc == 0))
260 {
261 if (!((*word)[*word_length - 1] == ':'
262 && strchr (*word, '=') && wordc == 0))
263 {
264 *word = w_addchar (buffer: *word, actlen: word_length, maxlen: max_length, ch: '~');
265 return *word ? 0 : WRDE_NOSPACE;
266 }
267 }
268 }
269
270 for (i = 1 + *offset; words[i]; i++)
271 {
272 if (words[i] == ':' || words[i] == '/' || words[i] == ' '
273 || words[i] == '\t' || words[i] == 0 )
274 break;
275
276 if (words[i] == '\\')
277 {
278 *word = w_addchar (buffer: *word, actlen: word_length, maxlen: max_length, ch: '~');
279 return *word ? 0 : WRDE_NOSPACE;
280 }
281 }
282
283 if (i == 1 + *offset)
284 {
285 /* Tilde appears on its own */
286 char* home;
287
288 /* POSIX.2 says ~ expands to $HOME and if HOME is unset the
289 results are unspecified. We do a lookup on the uid if
290 HOME is unset. */
291
292 home = getenv ("HOME");
293 if (home != NULL)
294 {
295 *word = w_addstr (buffer: *word, actlen: word_length, maxlen: max_length, str: home);
296 if (*word == NULL)
297 return WRDE_NOSPACE;
298 }
299 else
300 {
301 struct passwd pwd, *tpwd;
302 uid_t uid = __getuid ();
303 int result;
304 struct scratch_buffer tmpbuf;
305 scratch_buffer_init (buffer: &tmpbuf);
306
307 while ((result = __getpwuid_r (uid: uid, resultbuf: &pwd,
308 buffer: tmpbuf.data, buflen: tmpbuf.length,
309 result: &tpwd)) != 0
310 && errno == ERANGE)
311 if (!scratch_buffer_grow (buffer: &tmpbuf))
312 return WRDE_NOSPACE;
313
314 if (result == 0 && tpwd != NULL && pwd.pw_dir != NULL)
315 {
316 *word = w_addstr (buffer: *word, actlen: word_length, maxlen: max_length, str: pwd.pw_dir);
317 if (*word == NULL)
318 {
319 scratch_buffer_free (buffer: &tmpbuf);
320 return WRDE_NOSPACE;
321 }
322 }
323 else
324 {
325 *word = w_addchar (buffer: *word, actlen: word_length, maxlen: max_length, ch: '~');
326 if (*word == NULL)
327 {
328 scratch_buffer_free (buffer: &tmpbuf);
329 return WRDE_NOSPACE;
330 }
331 }
332 scratch_buffer_free (buffer: &tmpbuf);
333 }
334 }
335 else
336 {
337 /* Look up user name in database to get home directory */
338 char *user = strndupa (&words[1 + *offset], i - (1 + *offset));
339 struct passwd pwd, *tpwd;
340 int result;
341 struct scratch_buffer tmpbuf;
342 scratch_buffer_init (buffer: &tmpbuf);
343
344 while ((result = __getpwnam_r (name: user, resultbuf: &pwd, buffer: tmpbuf.data, buflen: tmpbuf.length,
345 result: &tpwd)) != 0
346 && errno == ERANGE)
347 if (!scratch_buffer_grow (buffer: &tmpbuf))
348 return WRDE_NOSPACE;
349
350 if (result == 0 && tpwd != NULL && pwd.pw_dir)
351 *word = w_addstr (buffer: *word, actlen: word_length, maxlen: max_length, str: pwd.pw_dir);
352 else
353 {
354 /* (invalid login name) */
355 *word = w_addchar (buffer: *word, actlen: word_length, maxlen: max_length, ch: '~');
356 if (*word != NULL)
357 *word = w_addstr (buffer: *word, actlen: word_length, maxlen: max_length, str: user);
358 }
359
360 scratch_buffer_free (buffer: &tmpbuf);
361
362 *offset = i - 1;
363 }
364 return *word ? 0 : WRDE_NOSPACE;
365}
366
367
368static int
369do_parse_glob (const char *glob_word, char **word, size_t *word_length,
370 size_t *max_length, wordexp_t *pwordexp, const char *ifs,
371 const char *ifs_white)
372{
373 int error;
374 unsigned int match;
375 glob_t globbuf;
376
377 error = glob (glob_word, GLOB_NOCHECK, NULL, &globbuf);
378
379 if (error != 0)
380 {
381 /* We can only run into memory problems. */
382 assert (error == GLOB_NOSPACE);
383 return WRDE_NOSPACE;
384 }
385
386 if (ifs && !*ifs)
387 {
388 /* No field splitting allowed. */
389 assert (globbuf.gl_pathv[0] != NULL);
390 *word = w_addstr (buffer: *word, actlen: word_length, maxlen: max_length, str: globbuf.gl_pathv[0]);
391 for (match = 1; match < globbuf.gl_pathc && *word != NULL; ++match)
392 {
393 *word = w_addchar (buffer: *word, actlen: word_length, maxlen: max_length, ch: ' ');
394 if (*word != NULL)
395 *word = w_addstr (buffer: *word, actlen: word_length, maxlen: max_length,
396 str: globbuf.gl_pathv[match]);
397 }
398
399 globfree (&globbuf);
400 return *word ? 0 : WRDE_NOSPACE;
401 }
402
403 assert (ifs == NULL || *ifs != '\0');
404 if (*word != NULL)
405 {
406 free (ptr: *word);
407 *word = w_newword (actlen: word_length, maxlen: max_length);
408 }
409
410 for (match = 0; match < globbuf.gl_pathc; ++match)
411 {
412 char *matching_word = __strdup (globbuf.gl_pathv[match]);
413 if (matching_word == NULL || w_addword (pwordexp, word: matching_word))
414 {
415 globfree (&globbuf);
416 return WRDE_NOSPACE;
417 }
418 }
419
420 globfree (&globbuf);
421 return 0;
422}
423
424static int
425parse_glob (char **word, size_t *word_length, size_t *max_length,
426 const char *words, size_t *offset, int flags,
427 wordexp_t *pwordexp, const char *ifs, const char *ifs_white)
428{
429 /* We are poised just after a '*', a '[' or a '?'. */
430 int error = WRDE_NOSPACE;
431 int quoted = 0; /* 1 if singly-quoted, 2 if doubly */
432 size_t i;
433 wordexp_t glob_list; /* List of words to glob */
434
435 glob_list.we_wordc = 0;
436 glob_list.we_wordv = NULL;
437 glob_list.we_offs = 0;
438 for (; words[*offset] != '\0'; ++*offset)
439 {
440 if (strchr (ifs, words[*offset]) != NULL)
441 /* Reached IFS */
442 break;
443
444 /* Sort out quoting */
445 if (words[*offset] == '\'')
446 {
447 if (quoted == 0)
448 {
449 quoted = 1;
450 continue;
451 }
452 else if (quoted == 1)
453 {
454 quoted = 0;
455 continue;
456 }
457 }
458 else if (words[*offset] == '"')
459 {
460 if (quoted == 0)
461 {
462 quoted = 2;
463 continue;
464 }
465 else if (quoted == 2)
466 {
467 quoted = 0;
468 continue;
469 }
470 }
471
472 /* Sort out other special characters */
473 if (quoted != 1 && words[*offset] == '$')
474 {
475 error = parse_dollars (word, word_length, max_length, words,
476 offset, flags, pwordexp: &glob_list, ifs, ifs_white,
477 quoted: quoted == 2);
478 if (error)
479 goto tidy_up;
480
481 continue;
482 }
483 else if (words[*offset] == '\\')
484 {
485 if (quoted)
486 error = parse_qtd_backslash (word, word_length, max_length,
487 words, offset);
488 else
489 error = parse_backslash (word, word_length, max_length,
490 words, offset);
491
492 if (error)
493 goto tidy_up;
494
495 continue;
496 }
497
498 *word = w_addchar (buffer: *word, actlen: word_length, maxlen: max_length, ch: words[*offset]);
499 if (*word == NULL)
500 goto tidy_up;
501 }
502
503 /* Don't forget to re-parse the character we stopped at. */
504 --*offset;
505
506 /* Glob the words */
507 error = w_addword (pwordexp: &glob_list, word: *word);
508 *word = w_newword (actlen: word_length, maxlen: max_length);
509 for (i = 0; error == 0 && i < glob_list.we_wordc; i++)
510 error = do_parse_glob (glob_word: glob_list.we_wordv[i], word, word_length,
511 max_length, pwordexp, ifs, ifs_white);
512
513 /* Now tidy up */
514tidy_up:
515 wordfree (&glob_list);
516 return error;
517}
518
519static int
520parse_squote (char **word, size_t *word_length, size_t *max_length,
521 const char *words, size_t *offset)
522{
523 /* We are poised just after a single quote */
524 for (; words[*offset]; ++(*offset))
525 {
526 if (words[*offset] != '\'')
527 {
528 *word = w_addchar (buffer: *word, actlen: word_length, maxlen: max_length, ch: words[*offset]);
529 if (*word == NULL)
530 return WRDE_NOSPACE;
531 }
532 else return 0;
533 }
534
535 /* Unterminated string */
536 return WRDE_SYNTAX;
537}
538
539/* Functions to evaluate an arithmetic expression */
540static int
541eval_expr_val (char **expr, long int *result)
542{
543 char *digit;
544
545 /* Skip white space */
546 for (digit = *expr; digit && *digit && isspace (*digit); ++digit);
547
548 if (*digit == '(')
549 {
550 /* Scan for closing paren */
551 for (++digit; **expr && **expr != ')'; ++(*expr));
552
553 /* Is there one? */
554 if (!**expr)
555 return WRDE_SYNTAX;
556
557 *(*expr)++ = 0;
558
559 if (eval_expr (expr: digit, result))
560 return WRDE_SYNTAX;
561
562 return 0;
563 }
564
565 /* POSIX requires that decimal, octal, and hexadecimal constants are
566 recognized. Therefore we pass 0 as the third parameter to strtol. */
567 *result = strtol (digit, expr, 0);
568 if (digit == *expr)
569 return WRDE_SYNTAX;
570
571 return 0;
572}
573
574static int
575eval_expr_multdiv (char **expr, long int *result)
576{
577 long int arg;
578
579 /* Read a Value */
580 if (eval_expr_val (expr, result) != 0)
581 return WRDE_SYNTAX;
582
583 while (**expr)
584 {
585 /* Skip white space */
586 for (; *expr && **expr && isspace (**expr); ++(*expr));
587
588 if (**expr == '*')
589 {
590 ++(*expr);
591 if (eval_expr_val (expr, result: &arg) != 0)
592 return WRDE_SYNTAX;
593
594 *result *= arg;
595 }
596 else if (**expr == '/')
597 {
598 ++(*expr);
599 if (eval_expr_val (expr, result: &arg) != 0)
600 return WRDE_SYNTAX;
601
602 /* Division by zero or integer overflow. */
603 if (arg == 0 || (arg == -1 && *result == LONG_MIN))
604 return WRDE_SYNTAX;
605
606 *result /= arg;
607 }
608 else break;
609 }
610
611 return 0;
612}
613
614static int
615eval_expr (char *expr, long int *result)
616{
617 long int arg;
618
619 /* Read a Multdiv */
620 if (eval_expr_multdiv (expr: &expr, result) != 0)
621 return WRDE_SYNTAX;
622
623 while (*expr)
624 {
625 /* Skip white space */
626 for (; expr && *expr && isspace (*expr); ++expr);
627
628 if (*expr == '+')
629 {
630 ++expr;
631 if (eval_expr_multdiv (expr: &expr, result: &arg) != 0)
632 return WRDE_SYNTAX;
633
634 *result += arg;
635 }
636 else if (*expr == '-')
637 {
638 ++expr;
639 if (eval_expr_multdiv (expr: &expr, result: &arg) != 0)
640 return WRDE_SYNTAX;
641
642 *result -= arg;
643 }
644 else break;
645 }
646
647 return 0;
648}
649
650static int
651parse_arith (char **word, size_t *word_length, size_t *max_length,
652 const char *words, size_t *offset, int flags, int bracket)
653{
654 /* We are poised just after "$((" or "$[" */
655 int error;
656 int paren_depth = 1;
657 size_t expr_length;
658 size_t expr_maxlen;
659 char *expr;
660
661 expr = w_newword (actlen: &expr_length, maxlen: &expr_maxlen);
662 for (; words[*offset]; ++(*offset))
663 {
664 switch (words[*offset])
665 {
666 case '$':
667 error = parse_dollars (word: &expr, word_length: &expr_length, max_length: &expr_maxlen,
668 words, offset, flags, NULL, NULL, NULL, quoted: 1);
669 /* The ``1'' here is to tell parse_dollars not to
670 * split the fields.
671 */
672 if (error)
673 {
674 free (ptr: expr);
675 return error;
676 }
677 break;
678
679 case '`':
680 (*offset)++;
681 error = parse_backtick (word: &expr, word_length: &expr_length, max_length: &expr_maxlen,
682 words, offset, flags, NULL, NULL, NULL);
683 /* The first NULL here is to tell parse_backtick not to
684 * split the fields.
685 */
686 if (error)
687 {
688 free (ptr: expr);
689 return error;
690 }
691 break;
692
693 case '\\':
694 error = parse_qtd_backslash (word: &expr, word_length: &expr_length, max_length: &expr_maxlen,
695 words, offset);
696 if (error)
697 {
698 free (ptr: expr);
699 return error;
700 }
701 /* I think that a backslash within an
702 * arithmetic expansion is bound to
703 * cause an error sooner or later anyway though.
704 */
705 break;
706
707 case ')':
708 if (--paren_depth == 0)
709 {
710 char result[21]; /* 21 = ceil(log10(2^64)) + 1 */
711 long int numresult = 0;
712 long long int convertme;
713
714 if (bracket || words[1 + *offset] != ')')
715 {
716 free (ptr: expr);
717 return WRDE_SYNTAX;
718 }
719
720 ++(*offset);
721
722 /* Go - evaluate. */
723 if (*expr && eval_expr (expr, result: &numresult) != 0)
724 {
725 free (ptr: expr);
726 return WRDE_SYNTAX;
727 }
728
729 if (numresult < 0)
730 {
731 convertme = -numresult;
732 *word = w_addchar (buffer: *word, actlen: word_length, maxlen: max_length, ch: '-');
733 if (!*word)
734 {
735 free (ptr: expr);
736 return WRDE_NOSPACE;
737 }
738 }
739 else
740 convertme = numresult;
741
742 result[20] = '\0';
743 *word = w_addstr (buffer: *word, actlen: word_length, maxlen: max_length,
744 _itoa (convertme, &result[20], 10, 0));
745 free (ptr: expr);
746 return *word ? 0 : WRDE_NOSPACE;
747 }
748 expr = w_addchar (buffer: expr, actlen: &expr_length, maxlen: &expr_maxlen, ch: words[*offset]);
749 if (expr == NULL)
750 return WRDE_NOSPACE;
751
752 break;
753
754 case ']':
755 if (bracket && paren_depth == 1)
756 {
757 char result[21]; /* 21 = ceil(log10(2^64)) + 1 */
758 long int numresult = 0;
759
760 /* Go - evaluate. */
761 if (*expr && eval_expr (expr, result: &numresult) != 0)
762 {
763 free (ptr: expr);
764 return WRDE_SYNTAX;
765 }
766
767 result[20] = '\0';
768 *word = w_addstr (buffer: *word, actlen: word_length, maxlen: max_length,
769 str: _itoa_word (value: numresult, buflim: &result[20], base: 10, upper_case: 0));
770 free (ptr: expr);
771 return *word ? 0 : WRDE_NOSPACE;
772 }
773
774 free (ptr: expr);
775 return WRDE_SYNTAX;
776
777 case '\n':
778 case ';':
779 case '{':
780 case '}':
781 free (ptr: expr);
782 return WRDE_BADCHAR;
783
784 case '(':
785 ++paren_depth;
786 /* Fall through. */
787 default:
788 expr = w_addchar (buffer: expr, actlen: &expr_length, maxlen: &expr_maxlen, ch: words[*offset]);
789 if (expr == NULL)
790 return WRDE_NOSPACE;
791 }
792 }
793
794 /* Premature end */
795 free (ptr: expr);
796 return WRDE_SYNTAX;
797}
798
799#define DYNARRAY_STRUCT strlist
800#define DYNARRAY_ELEMENT char *
801#define DYNARRAY_PREFIX strlist_
802/* Allocates about 512/1024 (32/64 bit) on stack. */
803#define DYNARRAY_INITIAL_SIZE 128
804#include <malloc/dynarray-skeleton.c>
805
806/* Function called by child process in exec_comm() */
807static pid_t
808exec_comm_child (char *comm, int *fildes, bool showerr, bool noexec)
809{
810 pid_t pid = -1;
811
812 /* Execute the command, or just check syntax? */
813 const char *args[] = { _PATH_BSHELL, noexec ? "-nc" : "-c", comm, NULL };
814
815 posix_spawn_file_actions_t fa;
816 /* posix_spawn_file_actions_init does not fail. */
817 __posix_spawn_file_actions_init (&fa);
818
819 /* Redirect output. For check syntax only (noexec being true), exec_comm
820 explicits sets fildes[1] to -1, so check its value to avoid a failure in
821 __posix_spawn_file_actions_adddup2. */
822 if (fildes[1] != -1)
823 {
824 if (__glibc_likely (fildes[1] != STDOUT_FILENO))
825 {
826 if (__posix_spawn_file_actions_adddup2 (&fa, fildes[1],
827 STDOUT_FILENO) != 0
828 || __posix_spawn_file_actions_addclose (&fa, fildes[1]) != 0)
829 goto out;
830 }
831 else
832 /* Reset the close-on-exec flag (if necessary). */
833 if (__posix_spawn_file_actions_adddup2 (&fa, fildes[1], fildes[1])
834 != 0)
835 goto out;
836 }
837
838 /* Redirect stderr to /dev/null if we have to. */
839 if (!showerr)
840 if (__posix_spawn_file_actions_addopen (&fa, STDERR_FILENO, _PATH_DEVNULL,
841 O_WRONLY, 0) != 0)
842 goto out;
843
844 struct strlist newenv;
845 strlist_init (list: &newenv);
846
847 bool recreate_env = getenv ("IFS") != NULL;
848 if (recreate_env)
849 {
850 for (char **ep = __environ; *ep != NULL; ep++)
851 if (strncmp (*ep, "IFS=", strlen ("IFS=")) != 0)
852 strlist_add (list: &newenv, item: *ep);
853 strlist_add (list: &newenv, NULL);
854 if (strlist_has_failed (list: &newenv))
855 goto out;
856 }
857
858 /* pid is not set if posix_spawn fails, so it keep the original value
859 of -1. */
860 __posix_spawn (&pid, _PATH_BSHELL, &fa, NULL, (char *const *) args,
861 recreate_env ? strlist_begin (list: &newenv) : __environ);
862
863 strlist_free (list: &newenv);
864
865out:
866 __posix_spawn_file_actions_destroy (&fa);
867
868 return pid;
869}
870
871/* Function to execute a command and retrieve the results */
872/* pwordexp contains NULL if field-splitting is forbidden */
873static int
874exec_comm (char *comm, char **word, size_t *word_length, size_t *max_length,
875 int flags, wordexp_t *pwordexp, const char *ifs,
876 const char *ifs_white)
877{
878 int fildes[2];
879#define bufsize 128
880 int buflen;
881 int i;
882 int status = 0;
883 size_t maxnewlines = 0;
884 char buffer[bufsize];
885 pid_t pid;
886 bool noexec = false;
887
888 /* Do nothing if command substitution should not succeed. */
889 if (flags & WRDE_NOCMD)
890 return WRDE_CMDSUB;
891
892 /* Don't posix_spawn unless necessary */
893 if (!comm || !*comm)
894 return 0;
895
896 if (__pipe2 (pipedes: fildes, O_CLOEXEC) < 0)
897 return WRDE_NOSPACE;
898
899 again:
900 pid = exec_comm_child (comm, fildes, showerr: noexec ? false : flags & WRDE_SHOWERR,
901 noexec);
902 if (pid < 0)
903 {
904 __close (fildes[0]);
905 __close (fildes[1]);
906 return WRDE_NOSPACE;
907 }
908
909 /* If we are just testing the syntax, only wait. */
910 if (noexec)
911 return (TEMP_FAILURE_RETRY (__waitpid (pid, &status, 0)) == pid
912 && status != 0) ? WRDE_SYNTAX : 0;
913
914 __close (fildes[1]);
915 fildes[1] = -1;
916
917 if (!pwordexp)
918 /* Quoted - no field splitting */
919 {
920 while (1)
921 {
922 if ((buflen = TEMP_FAILURE_RETRY (__read (fildes[0], buffer,
923 bufsize))) < 1)
924 {
925 /* If read returned 0 then the process has closed its
926 stdout. Don't use WNOHANG in that case to avoid busy
927 looping until the process eventually exits. */
928 if (TEMP_FAILURE_RETRY (__waitpid (pid, &status,
929 buflen == 0 ? 0 : WNOHANG))
930 == 0)
931 continue;
932 if ((buflen = TEMP_FAILURE_RETRY (__read (fildes[0], buffer,
933 bufsize))) < 1)
934 break;
935 }
936
937 maxnewlines += buflen;
938
939 *word = w_addmem (buffer: *word, actlen: word_length, maxlen: max_length, str: buffer, len: buflen);
940 if (*word == NULL)
941 goto no_space;
942 }
943 }
944 else
945 /* Not quoted - split fields */
946 {
947 int copying = 0;
948 /* 'copying' is:
949 * 0 when searching for first character in a field not IFS white space
950 * 1 when copying the text of a field
951 * 2 when searching for possible non-whitespace IFS
952 * 3 when searching for non-newline after copying field
953 */
954
955 while (1)
956 {
957 if ((buflen = TEMP_FAILURE_RETRY (__read (fildes[0], buffer,
958 bufsize))) < 1)
959 {
960 /* If read returned 0 then the process has closed its
961 stdout. Don't use WNOHANG in that case to avoid busy
962 looping until the process eventually exits. */
963 if (TEMP_FAILURE_RETRY (__waitpid (pid, &status,
964 buflen == 0 ? 0 : WNOHANG))
965 == 0)
966 continue;
967 if ((buflen = TEMP_FAILURE_RETRY (__read (fildes[0], buffer,
968 bufsize))) < 1)
969 break;
970 }
971
972 for (i = 0; i < buflen; ++i)
973 {
974 if (strchr (ifs, buffer[i]) != NULL)
975 {
976 /* Current character is IFS */
977 if (strchr (ifs_white, buffer[i]) == NULL)
978 {
979 /* Current character is IFS but not whitespace */
980 if (copying == 2)
981 {
982 /* current character
983 * |
984 * V
985 * eg: text<space><comma><space>moretext
986 *
987 * So, strip whitespace IFS (like at the start)
988 */
989 copying = 0;
990 continue;
991 }
992
993 copying = 0;
994 /* fall through and delimit field.. */
995 }
996 else
997 {
998 if (buffer[i] == '\n')
999 {
1000 /* Current character is (IFS) newline */
1001
1002 /* If copying a field, this is the end of it,
1003 but maybe all that's left is trailing newlines.
1004 So start searching for a non-newline. */
1005 if (copying == 1)
1006 copying = 3;
1007
1008 continue;
1009 }
1010 else
1011 {
1012 /* Current character is IFS white space, but
1013 not a newline */
1014
1015 /* If not either copying a field or searching
1016 for non-newline after a field, ignore it */
1017 if (copying != 1 && copying != 3)
1018 continue;
1019
1020 /* End of field (search for non-ws IFS afterwards) */
1021 copying = 2;
1022 }
1023 }
1024
1025 /* First IFS white space (non-newline), or IFS non-whitespace.
1026 * Delimit the field. Nulls are converted by w_addword. */
1027 if (w_addword (pwordexp, word: *word) == WRDE_NOSPACE)
1028 goto no_space;
1029
1030 *word = w_newword (actlen: word_length, maxlen: max_length);
1031
1032 maxnewlines = 0;
1033 /* fall back round the loop.. */
1034 }
1035 else
1036 {
1037 /* Not IFS character */
1038
1039 if (copying == 3)
1040 {
1041 /* Nothing but (IFS) newlines since the last field,
1042 so delimit it here before starting new word */
1043 if (w_addword (pwordexp, word: *word) == WRDE_NOSPACE)
1044 goto no_space;
1045
1046 *word = w_newword (actlen: word_length, maxlen: max_length);
1047 }
1048
1049 copying = 1;
1050
1051 if (buffer[i] == '\n') /* happens if newline not in IFS */
1052 maxnewlines++;
1053 else
1054 maxnewlines = 0;
1055
1056 *word = w_addchar (buffer: *word, actlen: word_length, maxlen: max_length,
1057 ch: buffer[i]);
1058 if (*word == NULL)
1059 goto no_space;
1060 }
1061 }
1062 }
1063 }
1064
1065 /* Chop off trailing newlines (required by POSIX.2) */
1066 /* Ensure we don't go back further than the beginning of the
1067 substitution (i.e. remove maxnewlines bytes at most) */
1068 while (maxnewlines-- != 0
1069 && *word_length > 0 && (*word)[*word_length - 1] == '\n')
1070 {
1071 (*word)[--*word_length] = '\0';
1072
1073 /* If the last word was entirely newlines, turn it into a new word
1074 * which can be ignored if there's nothing following it. */
1075 if (*word_length == 0)
1076 {
1077 free (ptr: *word);
1078 *word = w_newword (actlen: word_length, maxlen: max_length);
1079 break;
1080 }
1081 }
1082
1083 __close (fildes[0]);
1084 fildes[0] = -1;
1085
1086 /* Check for syntax error (re-execute but with "-n" flag) */
1087 if (buflen < 1 && status != 0)
1088 {
1089 noexec = true;
1090 goto again;
1091 }
1092
1093 return 0;
1094
1095no_space:
1096 __kill (pid, SIGKILL);
1097 TEMP_FAILURE_RETRY (__waitpid (pid, NULL, 0));
1098 __close (fildes[0]);
1099 return WRDE_NOSPACE;
1100}
1101
1102static int
1103parse_comm (char **word, size_t *word_length, size_t *max_length,
1104 const char *words, size_t *offset, int flags, wordexp_t *pwordexp,
1105 const char *ifs, const char *ifs_white)
1106{
1107 /* We are poised just after "$(" */
1108 int paren_depth = 1;
1109 int error = 0;
1110 int quoted = 0; /* 1 for singly-quoted, 2 for doubly-quoted */
1111 size_t comm_length;
1112 size_t comm_maxlen;
1113 char *comm = w_newword (actlen: &comm_length, maxlen: &comm_maxlen);
1114
1115 for (; words[*offset]; ++(*offset))
1116 {
1117 switch (words[*offset])
1118 {
1119 case '\'':
1120 if (quoted == 0)
1121 quoted = 1;
1122 else if (quoted == 1)
1123 quoted = 0;
1124
1125 break;
1126
1127 case '"':
1128 if (quoted == 0)
1129 quoted = 2;
1130 else if (quoted == 2)
1131 quoted = 0;
1132
1133 break;
1134
1135 case ')':
1136 if (!quoted && --paren_depth == 0)
1137 {
1138 /* Go -- give script to the shell */
1139 if (comm)
1140 {
1141 /* posix_spawn already handles thread cancellation. */
1142 error = exec_comm (comm, word, word_length, max_length,
1143 flags, pwordexp, ifs, ifs_white);
1144 free (ptr: comm);
1145 }
1146
1147 return error;
1148 }
1149
1150 /* This is just part of the script */
1151 break;
1152
1153 case '(':
1154 if (!quoted)
1155 ++paren_depth;
1156 }
1157
1158 comm = w_addchar (buffer: comm, actlen: &comm_length, maxlen: &comm_maxlen, ch: words[*offset]);
1159 if (comm == NULL)
1160 return WRDE_NOSPACE;
1161 }
1162
1163 /* Premature end. */
1164 free (ptr: comm);
1165
1166 return WRDE_SYNTAX;
1167}
1168
1169#define CHAR_IN_SET(ch, char_set) \
1170 (memchr (char_set "", ch, sizeof (char_set) - 1) != NULL)
1171
1172static int
1173parse_param (char **word, size_t *word_length, size_t *max_length,
1174 const char *words, size_t *offset, int flags, wordexp_t *pwordexp,
1175 const char *ifs, const char *ifs_white, int quoted)
1176{
1177 /* We are poised just after "$" */
1178 enum action
1179 {
1180 ACT_NONE,
1181 ACT_RP_SHORT_LEFT = '#',
1182 ACT_RP_LONG_LEFT = 'L',
1183 ACT_RP_SHORT_RIGHT = '%',
1184 ACT_RP_LONG_RIGHT = 'R',
1185 ACT_NULL_ERROR = '?',
1186 ACT_NULL_SUBST = '-',
1187 ACT_NONNULL_SUBST = '+',
1188 ACT_NULL_ASSIGN = '='
1189 };
1190 size_t env_length;
1191 size_t env_maxlen;
1192 size_t pat_length;
1193 size_t pat_maxlen;
1194 size_t start = *offset;
1195 char *env;
1196 char *pattern;
1197 char *value = NULL;
1198 enum action action = ACT_NONE;
1199 int depth = 0;
1200 int colon_seen = 0;
1201 int seen_hash = 0;
1202 int free_value = 0;
1203 int pattern_is_quoted = 0; /* 1 for singly-quoted, 2 for doubly-quoted */
1204 int error;
1205 int special = 0;
1206 char buffer[21];
1207 int brace = words[*offset] == '{';
1208
1209 env = w_newword (actlen: &env_length, maxlen: &env_maxlen);
1210 pattern = w_newword (actlen: &pat_length, maxlen: &pat_maxlen);
1211
1212 if (brace)
1213 ++*offset;
1214
1215 /* First collect the parameter name. */
1216
1217 if (words[*offset] == '#')
1218 {
1219 seen_hash = 1;
1220 if (!brace)
1221 goto envsubst;
1222 ++*offset;
1223 }
1224
1225 if (isalpha (words[*offset]) || words[*offset] == '_')
1226 {
1227 /* Normal parameter name. */
1228 do
1229 {
1230 env = w_addchar (buffer: env, actlen: &env_length, maxlen: &env_maxlen,
1231 ch: words[*offset]);
1232 if (env == NULL)
1233 goto no_space;
1234 }
1235 while (isalnum (words[++*offset]) || words[*offset] == '_');
1236 }
1237 else if (isdigit (words[*offset]))
1238 {
1239 /* Numeric parameter name. */
1240 special = 1;
1241 do
1242 {
1243 env = w_addchar (buffer: env, actlen: &env_length, maxlen: &env_maxlen,
1244 ch: words[*offset]);
1245 if (env == NULL)
1246 goto no_space;
1247 if (!brace)
1248 goto envsubst;
1249 }
1250 while (isdigit(words[++*offset]));
1251 }
1252 else if (CHAR_IN_SET (words[*offset], "*@$"))
1253 {
1254 /* Special parameter. */
1255 special = 1;
1256 env = w_addchar (buffer: env, actlen: &env_length, maxlen: &env_maxlen,
1257 ch: words[*offset]);
1258 if (env == NULL)
1259 goto no_space;
1260 ++*offset;
1261 }
1262 else
1263 {
1264 if (brace)
1265 goto syntax;
1266 }
1267
1268 if (brace)
1269 {
1270 /* Check for special action to be applied to the value. */
1271 switch (words[*offset])
1272 {
1273 case '}':
1274 /* Evaluate. */
1275 goto envsubst;
1276
1277 case '#':
1278 action = ACT_RP_SHORT_LEFT;
1279 if (words[1 + *offset] == '#')
1280 {
1281 ++*offset;
1282 action = ACT_RP_LONG_LEFT;
1283 }
1284 break;
1285
1286 case '%':
1287 action = ACT_RP_SHORT_RIGHT;
1288 if (words[1 + *offset] == '%')
1289 {
1290 ++*offset;
1291 action = ACT_RP_LONG_RIGHT;
1292 }
1293 break;
1294
1295 case ':':
1296 if (!CHAR_IN_SET (words[1 + *offset], "-=?+"))
1297 goto syntax;
1298
1299 colon_seen = 1;
1300 action = words[++*offset];
1301 break;
1302
1303 case '-':
1304 case '=':
1305 case '?':
1306 case '+':
1307 action = words[*offset];
1308 break;
1309
1310 default:
1311 goto syntax;
1312 }
1313
1314 /* Now collect the pattern, but don't expand it yet. */
1315 ++*offset;
1316 for (; words[*offset]; ++(*offset))
1317 {
1318 switch (words[*offset])
1319 {
1320 case '{':
1321 if (!pattern_is_quoted)
1322 ++depth;
1323 break;
1324
1325 case '}':
1326 if (!pattern_is_quoted)
1327 {
1328 if (depth == 0)
1329 goto envsubst;
1330 --depth;
1331 }
1332 break;
1333
1334 case '\\':
1335 if (pattern_is_quoted)
1336 /* Quoted; treat as normal character. */
1337 break;
1338
1339 /* Otherwise, it's an escape: next character is literal. */
1340 if (words[++*offset] == '\0')
1341 goto syntax;
1342
1343 pattern = w_addchar (buffer: pattern, actlen: &pat_length, maxlen: &pat_maxlen, ch: '\\');
1344 if (pattern == NULL)
1345 goto no_space;
1346
1347 break;
1348
1349 case '\'':
1350 if (pattern_is_quoted == 0)
1351 pattern_is_quoted = 1;
1352 else if (pattern_is_quoted == 1)
1353 pattern_is_quoted = 0;
1354
1355 break;
1356
1357 case '"':
1358 if (pattern_is_quoted == 0)
1359 pattern_is_quoted = 2;
1360 else if (pattern_is_quoted == 2)
1361 pattern_is_quoted = 0;
1362
1363 break;
1364 }
1365
1366 pattern = w_addchar (buffer: pattern, actlen: &pat_length, maxlen: &pat_maxlen,
1367 ch: words[*offset]);
1368 if (pattern == NULL)
1369 goto no_space;
1370 }
1371 }
1372
1373 /* End of input string -- remember to reparse the character that we
1374 * stopped at. */
1375 --(*offset);
1376
1377envsubst:
1378 if (words[start] == '{' && words[*offset] != '}')
1379 goto syntax;
1380
1381 if (env == NULL)
1382 {
1383 if (seen_hash)
1384 {
1385 /* $# expands to the number of positional parameters */
1386 buffer[20] = '\0';
1387 value = _itoa_word (value: __libc_argc - 1, buflim: &buffer[20], base: 10, upper_case: 0);
1388 seen_hash = 0;
1389 }
1390 else
1391 {
1392 /* Just $ on its own */
1393 *offset = start - 1;
1394 *word = w_addchar (buffer: *word, actlen: word_length, maxlen: max_length, ch: '$');
1395 return *word ? 0 : WRDE_NOSPACE;
1396 }
1397 }
1398 /* Is it a numeric parameter? */
1399 else if (isdigit (env[0]))
1400 {
1401 unsigned long n = strtoul (env, NULL, 10);
1402
1403 if (n >= __libc_argc)
1404 /* Substitute NULL. */
1405 value = NULL;
1406 else
1407 /* Replace with appropriate positional parameter. */
1408 value = __libc_argv[n];
1409 }
1410 /* Is it a special parameter? */
1411 else if (special)
1412 {
1413 /* Is it `$$'? */
1414 if (*env == '$')
1415 {
1416 buffer[20] = '\0';
1417 value = _itoa_word (value: __getpid (), buflim: &buffer[20], base: 10, upper_case: 0);
1418 }
1419 /* Is it `${#*}' or `${#@}'? */
1420 else if ((*env == '*' || *env == '@') && seen_hash)
1421 {
1422 buffer[20] = '\0';
1423 value = _itoa_word (value: __libc_argc > 0 ? __libc_argc - 1 : 0,
1424 buflim: &buffer[20], base: 10, upper_case: 0);
1425 *word = w_addstr (buffer: *word, actlen: word_length, maxlen: max_length, str: value);
1426 free (ptr: env);
1427 free (ptr: pattern);
1428 return *word ? 0 : WRDE_NOSPACE;
1429 }
1430 /* Is it `$*' or `$@' (unquoted) ? */
1431 else if (*env == '*' || (*env == '@' && !quoted))
1432 {
1433 size_t plist_len = 0;
1434 int p;
1435 char *end;
1436
1437 /* Build up value parameter by parameter (copy them) */
1438 for (p = 1; __libc_argv[p]; ++p)
1439 plist_len += strlen (__libc_argv[p]) + 1; /* for space */
1440 value = malloc (size: plist_len);
1441 if (value == NULL)
1442 goto no_space;
1443 end = value;
1444 *end = 0;
1445 for (p = 1; __libc_argv[p]; ++p)
1446 {
1447 if (p > 1)
1448 *end++ = ' ';
1449 end = __stpcpy (end, __libc_argv[p]);
1450 }
1451
1452 free_value = 1;
1453 }
1454 else
1455 {
1456 /* Must be a quoted `$@' */
1457 assert (*env == '@' && quoted);
1458
1459 /* Each parameter is a separate word ("$@") */
1460 if (__libc_argc == 2)
1461 value = __libc_argv[1];
1462 else if (__libc_argc > 2)
1463 {
1464 int p;
1465
1466 /* Append first parameter to current word. */
1467 value = w_addstr (buffer: *word, actlen: word_length, maxlen: max_length,
1468 str: __libc_argv[1]);
1469 if (value == NULL || w_addword (pwordexp, word: value))
1470 goto no_space;
1471
1472 for (p = 2; __libc_argv[p + 1]; p++)
1473 {
1474 char *newword = __strdup (__libc_argv[p]);
1475 if (newword == NULL || w_addword (pwordexp, word: newword))
1476 goto no_space;
1477 }
1478
1479 /* Start a new word with the last parameter. */
1480 *word = w_newword (actlen: word_length, maxlen: max_length);
1481 value = __libc_argv[p];
1482 }
1483 else
1484 {
1485 free (ptr: env);
1486 free (ptr: pattern);
1487 return 0;
1488 }
1489 }
1490 }
1491 else
1492 value = getenv (env);
1493
1494 if (value == NULL && (flags & WRDE_UNDEF))
1495 {
1496 /* Variable not defined. */
1497 error = WRDE_BADVAL;
1498 goto do_error;
1499 }
1500
1501 if (action != ACT_NONE)
1502 {
1503 int expand_pattern = 0;
1504
1505 /* First, find out if we need to expand pattern (i.e. if we will
1506 * use it). */
1507 switch (action)
1508 {
1509 case ACT_RP_SHORT_LEFT:
1510 case ACT_RP_LONG_LEFT:
1511 case ACT_RP_SHORT_RIGHT:
1512 case ACT_RP_LONG_RIGHT:
1513 /* Always expand for these. */
1514 expand_pattern = 1;
1515 break;
1516
1517 case ACT_NULL_ERROR:
1518 case ACT_NULL_SUBST:
1519 case ACT_NULL_ASSIGN:
1520 if (!value || (!*value && colon_seen))
1521 /* If param is unset, or set but null and a colon has been seen,
1522 the expansion of the pattern will be needed. */
1523 expand_pattern = 1;
1524
1525 break;
1526
1527 case ACT_NONNULL_SUBST:
1528 /* Expansion of word will be needed if parameter is set and not null,
1529 or set null but no colon has been seen. */
1530 if (value && (*value || !colon_seen))
1531 expand_pattern = 1;
1532
1533 break;
1534
1535 default:
1536 assert (! "Unrecognised action!");
1537 }
1538
1539 if (expand_pattern)
1540 {
1541 /* We need to perform tilde expansion, parameter expansion,
1542 command substitution, and arithmetic expansion. We also
1543 have to be a bit careful with wildcard characters, as
1544 pattern might be given to fnmatch soon. To do this, we
1545 convert quotes to escapes. */
1546
1547 char *expanded;
1548 size_t exp_len;
1549 size_t exp_maxl;
1550 char *p;
1551 int quoted = 0; /* 1: single quotes; 2: double */
1552
1553 expanded = w_newword (actlen: &exp_len, maxlen: &exp_maxl);
1554 for (p = pattern; p && *p; p++)
1555 {
1556 size_t offset;
1557
1558 switch (*p)
1559 {
1560 case '"':
1561 if (quoted == 2)
1562 quoted = 0;
1563 else if (quoted == 0)
1564 quoted = 2;
1565 else break;
1566
1567 continue;
1568
1569 case '\'':
1570 if (quoted == 1)
1571 quoted = 0;
1572 else if (quoted == 0)
1573 quoted = 1;
1574 else break;
1575
1576 continue;
1577
1578 case '*':
1579 case '?':
1580 if (quoted)
1581 {
1582 /* Convert quoted wildchar to escaped wildchar. */
1583 expanded = w_addchar (buffer: expanded, actlen: &exp_len,
1584 maxlen: &exp_maxl, ch: '\\');
1585
1586 if (expanded == NULL)
1587 goto no_space;
1588 }
1589 break;
1590
1591 case '$':
1592 offset = 0;
1593 error = parse_dollars (word: &expanded, word_length: &exp_len, max_length: &exp_maxl, words: p,
1594 offset: &offset, flags, NULL, NULL, NULL, quoted: 1);
1595 if (error)
1596 {
1597 if (free_value)
1598 free (ptr: value);
1599
1600 free (ptr: expanded);
1601
1602 goto do_error;
1603 }
1604
1605 p += offset;
1606 continue;
1607
1608 case '~':
1609 if (quoted || exp_len)
1610 break;
1611
1612 offset = 0;
1613 error = parse_tilde (word: &expanded, word_length: &exp_len, max_length: &exp_maxl, words: p,
1614 offset: &offset, wordc: 0);
1615 if (error)
1616 {
1617 if (free_value)
1618 free (ptr: value);
1619
1620 free (ptr: expanded);
1621
1622 goto do_error;
1623 }
1624
1625 p += offset;
1626 continue;
1627
1628 case '\\':
1629 expanded = w_addchar (buffer: expanded, actlen: &exp_len, maxlen: &exp_maxl, ch: '\\');
1630 ++p;
1631 assert (*p); /* checked when extracted initially */
1632 if (expanded == NULL)
1633 goto no_space;
1634 }
1635
1636 expanded = w_addchar (buffer: expanded, actlen: &exp_len, maxlen: &exp_maxl, ch: *p);
1637
1638 if (expanded == NULL)
1639 goto no_space;
1640 }
1641
1642 free (ptr: pattern);
1643
1644 pattern = expanded;
1645 }
1646
1647 switch (action)
1648 {
1649 case ACT_RP_SHORT_LEFT:
1650 case ACT_RP_LONG_LEFT:
1651 case ACT_RP_SHORT_RIGHT:
1652 case ACT_RP_LONG_RIGHT:
1653 {
1654 char *p;
1655 char c;
1656 char *end;
1657
1658 if (value == NULL || pattern == NULL || *pattern == '\0')
1659 break;
1660
1661 end = value + strlen (value);
1662
1663 switch (action)
1664 {
1665 case ACT_RP_SHORT_LEFT:
1666 for (p = value; p <= end; ++p)
1667 {
1668 c = *p;
1669 *p = '\0';
1670 if (fnmatch (pattern, value, 0) != FNM_NOMATCH)
1671 {
1672 *p = c;
1673 if (free_value)
1674 {
1675 char *newval = __strdup (p);
1676 if (newval == NULL)
1677 {
1678 free (ptr: value);
1679 goto no_space;
1680 }
1681 free (ptr: value);
1682 value = newval;
1683 }
1684 else
1685 value = p;
1686 break;
1687 }
1688 *p = c;
1689 }
1690
1691 break;
1692
1693 case ACT_RP_LONG_LEFT:
1694 for (p = end; p >= value; --p)
1695 {
1696 c = *p;
1697 *p = '\0';
1698 if (fnmatch (pattern, value, 0) != FNM_NOMATCH)
1699 {
1700 *p = c;
1701 if (free_value)
1702 {
1703 char *newval = __strdup (p);
1704 if (newval == NULL)
1705 {
1706 free (ptr: value);
1707 goto no_space;
1708 }
1709 free (ptr: value);
1710 value = newval;
1711 }
1712 else
1713 value = p;
1714 break;
1715 }
1716 *p = c;
1717 }
1718
1719 break;
1720
1721 case ACT_RP_SHORT_RIGHT:
1722 for (p = end; p >= value; --p)
1723 {
1724 if (fnmatch (pattern, p, 0) != FNM_NOMATCH)
1725 {
1726 char *newval;
1727 newval = malloc (size: p - value + 1);
1728
1729 if (newval == NULL)
1730 {
1731 if (free_value)
1732 free (ptr: value);
1733 goto no_space;
1734 }
1735
1736 *(char *) __mempcpy (newval, value, p - value) = '\0';
1737 if (free_value)
1738 free (ptr: value);
1739 value = newval;
1740 free_value = 1;
1741 break;
1742 }
1743 }
1744
1745 break;
1746
1747 case ACT_RP_LONG_RIGHT:
1748 for (p = value; p <= end; ++p)
1749 {
1750 if (fnmatch (pattern, p, 0) != FNM_NOMATCH)
1751 {
1752 char *newval;
1753 newval = malloc (size: p - value + 1);
1754
1755 if (newval == NULL)
1756 {
1757 if (free_value)
1758 free (ptr: value);
1759 goto no_space;
1760 }
1761
1762 *(char *) __mempcpy (newval, value, p - value) = '\0';
1763 if (free_value)
1764 free (ptr: value);
1765 value = newval;
1766 free_value = 1;
1767 break;
1768 }
1769 }
1770
1771 break;
1772
1773 default:
1774 break;
1775 }
1776
1777 break;
1778 }
1779
1780 case ACT_NULL_ERROR:
1781 if (value && *value)
1782 /* Substitute parameter */
1783 break;
1784
1785 error = 0;
1786 if (!colon_seen && value)
1787 /* Substitute NULL */
1788 ;
1789 else
1790 {
1791 const char *str = pattern;
1792
1793 if (str[0] == '\0')
1794 str = _("parameter null or not set");
1795
1796 __fxprintf (NULL, fmt: "%s: %s\n", env, str);
1797 }
1798
1799 if (free_value)
1800 free (ptr: value);
1801 goto do_error;
1802
1803 case ACT_NULL_SUBST:
1804 if (value && *value)
1805 /* Substitute parameter */
1806 break;
1807
1808 if (free_value)
1809 free (ptr: value);
1810
1811 if (!colon_seen && value)
1812 /* Substitute NULL */
1813 goto success;
1814
1815 value = pattern ? __strdup (pattern) : pattern;
1816 free_value = 1;
1817
1818 if (pattern && !value)
1819 goto no_space;
1820
1821 break;
1822
1823 case ACT_NONNULL_SUBST:
1824 if (value && (*value || !colon_seen))
1825 {
1826 if (free_value)
1827 free (ptr: value);
1828
1829 value = pattern ? __strdup (pattern) : pattern;
1830 free_value = 1;
1831
1832 if (pattern && !value)
1833 goto no_space;
1834
1835 break;
1836 }
1837
1838 /* Substitute NULL */
1839 if (free_value)
1840 free (ptr: value);
1841 goto success;
1842
1843 case ACT_NULL_ASSIGN:
1844 if (value && *value)
1845 /* Substitute parameter */
1846 break;
1847
1848 if (!colon_seen && value)
1849 {
1850 /* Substitute NULL */
1851 if (free_value)
1852 free (ptr: value);
1853 goto success;
1854 }
1855
1856 if (free_value)
1857 free (ptr: value);
1858
1859 value = pattern ? __strdup (pattern) : pattern;
1860 free_value = 1;
1861
1862 if (pattern && !value)
1863 goto no_space;
1864
1865 __setenv (name: env, value: value ?: "", replace: 1);
1866 break;
1867
1868 default:
1869 assert (! "Unrecognised action!");
1870 }
1871 }
1872
1873 free (ptr: env);
1874 env = NULL;
1875 free (ptr: pattern);
1876 pattern = NULL;
1877
1878 if (seen_hash)
1879 {
1880 char param_length[21];
1881 param_length[20] = '\0';
1882 *word = w_addstr (buffer: *word, actlen: word_length, maxlen: max_length,
1883 str: _itoa_word (value: value ? strlen (value) : 0,
1884 buflim: &param_length[20], base: 10, upper_case: 0));
1885 if (free_value)
1886 {
1887 assert (value != NULL);
1888 free (ptr: value);
1889 }
1890
1891 return *word ? 0 : WRDE_NOSPACE;
1892 }
1893
1894 if (value == NULL)
1895 return 0;
1896
1897 if (quoted || !pwordexp)
1898 {
1899 /* Quoted - no field split */
1900 *word = w_addstr (buffer: *word, actlen: word_length, maxlen: max_length, str: value);
1901 if (free_value)
1902 free (ptr: value);
1903
1904 return *word ? 0 : WRDE_NOSPACE;
1905 }
1906 else
1907 {
1908 /* Need to field-split */
1909 char *value_copy = __strdup (value); /* Don't modify value */
1910 char *field_begin = value_copy;
1911 int seen_nonws_ifs = 0;
1912
1913 if (free_value)
1914 free (ptr: value);
1915
1916 if (value_copy == NULL)
1917 goto no_space;
1918
1919 do
1920 {
1921 char *field_end = field_begin;
1922 char *next_field;
1923
1924 /* If this isn't the first field, start a new word */
1925 if (field_begin != value_copy)
1926 {
1927 if (w_addword (pwordexp, word: *word) == WRDE_NOSPACE)
1928 {
1929 free (ptr: value_copy);
1930 goto no_space;
1931 }
1932
1933 *word = w_newword (actlen: word_length, maxlen: max_length);
1934 }
1935
1936 /* Skip IFS whitespace before the field */
1937 field_begin += strspn (field_begin, ifs_white);
1938
1939 if (!seen_nonws_ifs && *field_begin == 0)
1940 /* Nothing but whitespace */
1941 break;
1942
1943 /* Search for the end of the field */
1944 field_end = field_begin + strcspn (field_begin, ifs);
1945
1946 /* Set up pointer to the character after end of field and
1947 skip whitespace IFS after it. */
1948 next_field = field_end + strspn (field_end, ifs_white);
1949
1950 /* Skip at most one non-whitespace IFS character after the field */
1951 seen_nonws_ifs = 0;
1952 if (*next_field && strchr (ifs, *next_field))
1953 {
1954 seen_nonws_ifs = 1;
1955 next_field++;
1956 }
1957
1958 /* Null-terminate it */
1959 *field_end = 0;
1960
1961 /* Tag a copy onto the current word */
1962 *word = w_addstr (buffer: *word, actlen: word_length, maxlen: max_length, str: field_begin);
1963
1964 if (*word == NULL && *field_begin != '\0')
1965 {
1966 free (ptr: value_copy);
1967 goto no_space;
1968 }
1969
1970 field_begin = next_field;
1971 }
1972 while (seen_nonws_ifs || *field_begin);
1973
1974 free (ptr: value_copy);
1975 }
1976
1977 return 0;
1978
1979success:
1980 error = 0;
1981 goto do_error;
1982
1983no_space:
1984 error = WRDE_NOSPACE;
1985 goto do_error;
1986
1987syntax:
1988 error = WRDE_SYNTAX;
1989
1990do_error:
1991 free (ptr: env);
1992
1993 free (ptr: pattern);
1994
1995 return error;
1996}
1997
1998#undef CHAR_IN_SET
1999
2000static int
2001parse_dollars (char **word, size_t *word_length, size_t *max_length,
2002 const char *words, size_t *offset, int flags,
2003 wordexp_t *pwordexp, const char *ifs, const char *ifs_white,
2004 int quoted)
2005{
2006 /* We are poised _at_ "$" */
2007 switch (words[1 + *offset])
2008 {
2009 case '"':
2010 case '\'':
2011 case 0:
2012 *word = w_addchar (buffer: *word, actlen: word_length, maxlen: max_length, ch: '$');
2013 return *word ? 0 : WRDE_NOSPACE;
2014
2015 case '(':
2016 if (words[2 + *offset] == '(')
2017 {
2018 /* Differentiate between $((1+3)) and $((echo);(ls)) */
2019 int i = 3 + *offset;
2020 int depth = 0;
2021 while (words[i] && !(depth == 0 && words[i] == ')'))
2022 {
2023 if (words[i] == '(')
2024 ++depth;
2025 else if (words[i] == ')')
2026 --depth;
2027
2028 ++i;
2029 }
2030
2031 if (words[i] == ')' && words[i + 1] == ')')
2032 {
2033 (*offset) += 3;
2034 /* Call parse_arith -- 0 is for "no brackets" */
2035 return parse_arith (word, word_length, max_length, words, offset,
2036 flags, bracket: 0);
2037 }
2038 }
2039
2040 (*offset) += 2;
2041 return parse_comm (word, word_length, max_length, words, offset, flags,
2042 pwordexp: quoted? NULL : pwordexp, ifs, ifs_white);
2043
2044 case '[':
2045 (*offset) += 2;
2046 /* Call parse_arith -- 1 is for "brackets" */
2047 return parse_arith (word, word_length, max_length, words, offset, flags,
2048 bracket: 1);
2049
2050 case '{':
2051 default:
2052 ++(*offset); /* parse_param needs to know if "{" is there */
2053 return parse_param (word, word_length, max_length, words, offset, flags,
2054 pwordexp, ifs, ifs_white, quoted);
2055 }
2056}
2057
2058static int
2059parse_backtick (char **word, size_t *word_length, size_t *max_length,
2060 const char *words, size_t *offset, int flags,
2061 wordexp_t *pwordexp, const char *ifs, const char *ifs_white)
2062{
2063 /* We are poised just after "`" */
2064 int error;
2065 int squoting = 0;
2066 size_t comm_length;
2067 size_t comm_maxlen;
2068 char *comm = w_newword (actlen: &comm_length, maxlen: &comm_maxlen);
2069
2070 for (; words[*offset]; ++(*offset))
2071 {
2072 switch (words[*offset])
2073 {
2074 case '`':
2075 /* Go -- give the script to the shell */
2076 error = exec_comm (comm, word, word_length, max_length, flags,
2077 pwordexp, ifs, ifs_white);
2078 free (ptr: comm);
2079 return error;
2080
2081 case '\\':
2082 if (squoting)
2083 {
2084 error = parse_qtd_backslash (word: &comm, word_length: &comm_length, max_length: &comm_maxlen,
2085 words, offset);
2086
2087 if (error)
2088 {
2089 free (ptr: comm);
2090 return error;
2091 }
2092
2093 break;
2094 }
2095
2096 error = parse_backslash (word: &comm, word_length: &comm_length, max_length: &comm_maxlen, words,
2097 offset);
2098
2099 if (error)
2100 {
2101 free (ptr: comm);
2102 return error;
2103 }
2104
2105 break;
2106
2107 case '\'':
2108 squoting = 1 - squoting;
2109 /* Fall through. */
2110 default:
2111 comm = w_addchar (buffer: comm, actlen: &comm_length, maxlen: &comm_maxlen, ch: words[*offset]);
2112 if (comm == NULL)
2113 return WRDE_NOSPACE;
2114 }
2115 }
2116
2117 /* Premature end */
2118 free (ptr: comm);
2119 return WRDE_SYNTAX;
2120}
2121
2122static int
2123parse_dquote (char **word, size_t *word_length, size_t *max_length,
2124 const char *words, size_t *offset, int flags,
2125 wordexp_t *pwordexp, const char * ifs, const char * ifs_white)
2126{
2127 /* We are poised just after a double-quote */
2128 int error;
2129
2130 for (; words[*offset]; ++(*offset))
2131 {
2132 switch (words[*offset])
2133 {
2134 case '"':
2135 return 0;
2136
2137 case '$':
2138 error = parse_dollars (word, word_length, max_length, words, offset,
2139 flags, pwordexp, ifs, ifs_white, quoted: 1);
2140 /* The ``1'' here is to tell parse_dollars not to
2141 * split the fields. It may need to, however ("$@").
2142 */
2143 if (error)
2144 return error;
2145
2146 break;
2147
2148 case '`':
2149 ++(*offset);
2150 error = parse_backtick (word, word_length, max_length, words,
2151 offset, flags, NULL, NULL, NULL);
2152 /* The first NULL here is to tell parse_backtick not to
2153 * split the fields.
2154 */
2155 if (error)
2156 return error;
2157
2158 break;
2159
2160 case '\\':
2161 error = parse_qtd_backslash (word, word_length, max_length, words,
2162 offset);
2163
2164 if (error)
2165 return error;
2166
2167 break;
2168
2169 default:
2170 *word = w_addchar (buffer: *word, actlen: word_length, maxlen: max_length, ch: words[*offset]);
2171 if (*word == NULL)
2172 return WRDE_NOSPACE;
2173 }
2174 }
2175
2176 /* Unterminated string */
2177 return WRDE_SYNTAX;
2178}
2179
2180/*
2181 * wordfree() is to be called after pwordexp is finished with.
2182 */
2183
2184void
2185wordfree (wordexp_t *pwordexp)
2186{
2187
2188 /* wordexp can set pwordexp to NULL */
2189 if (pwordexp && pwordexp->we_wordv)
2190 {
2191 char **wordv = pwordexp->we_wordv;
2192
2193 for (wordv += pwordexp->we_offs; *wordv; ++wordv)
2194 free (ptr: *wordv);
2195
2196 free (ptr: pwordexp->we_wordv);
2197 pwordexp->we_wordv = NULL;
2198 }
2199}
2200libc_hidden_def (wordfree)
2201
2202/*
2203 * wordexp()
2204 */
2205
2206int
2207wordexp (const char *words, wordexp_t *pwordexp, int flags)
2208{
2209 size_t words_offset;
2210 size_t word_length;
2211 size_t max_length;
2212 char *word = w_newword (actlen: &word_length, maxlen: &max_length);
2213 int error;
2214 char *ifs;
2215 char ifs_white[4];
2216 wordexp_t old_word = *pwordexp;
2217
2218 if (flags & WRDE_REUSE)
2219 {
2220 /* Minimal implementation of WRDE_REUSE for now */
2221 wordfree (pwordexp);
2222 old_word.we_wordv = NULL;
2223 }
2224
2225 if ((flags & WRDE_APPEND) == 0)
2226 {
2227 pwordexp->we_wordc = 0;
2228
2229 if (flags & WRDE_DOOFFS)
2230 {
2231 pwordexp->we_wordv = calloc (nmemb: 1 + pwordexp->we_offs, size: sizeof (char *));
2232 if (pwordexp->we_wordv == NULL)
2233 {
2234 error = WRDE_NOSPACE;
2235 goto do_error;
2236 }
2237 }
2238 else
2239 {
2240 pwordexp->we_wordv = calloc (nmemb: 1, size: sizeof (char *));
2241 if (pwordexp->we_wordv == NULL)
2242 {
2243 error = WRDE_NOSPACE;
2244 goto do_error;
2245 }
2246
2247 pwordexp->we_offs = 0;
2248 }
2249 }
2250
2251 /* Find out what the field separators are.
2252 * There are two types: whitespace and non-whitespace.
2253 */
2254 ifs = getenv ("IFS");
2255
2256 if (ifs == NULL)
2257 /* IFS unset - use <space><tab><newline>. */
2258 ifs = strcpy (ifs_white, " \t\n");
2259 else
2260 {
2261 char *ifsch = ifs;
2262 char *whch = ifs_white;
2263
2264 while (*ifsch != '\0')
2265 {
2266 if (*ifsch == ' ' || *ifsch == '\t' || *ifsch == '\n')
2267 {
2268 /* Whitespace IFS. See first whether it is already in our
2269 collection. */
2270 char *runp = ifs_white;
2271
2272 while (runp < whch && *runp != *ifsch)
2273 ++runp;
2274
2275 if (runp == whch)
2276 *whch++ = *ifsch;
2277 }
2278
2279 ++ifsch;
2280 }
2281 *whch = '\0';
2282 }
2283
2284 for (words_offset = 0 ; words[words_offset] ; ++words_offset)
2285 switch (words[words_offset])
2286 {
2287 case '\\':
2288 error = parse_backslash (word: &word, word_length: &word_length, max_length: &max_length, words,
2289 offset: &words_offset);
2290
2291 if (error)
2292 goto do_error;
2293
2294 break;
2295
2296 case '$':
2297 error = parse_dollars (word: &word, word_length: &word_length, max_length: &max_length, words,
2298 offset: &words_offset, flags, pwordexp, ifs, ifs_white,
2299 quoted: 0);
2300
2301 if (error)
2302 goto do_error;
2303
2304 break;
2305
2306 case '`':
2307 ++words_offset;
2308 error = parse_backtick (word: &word, word_length: &word_length, max_length: &max_length, words,
2309 offset: &words_offset, flags, pwordexp, ifs,
2310 ifs_white);
2311
2312 if (error)
2313 goto do_error;
2314
2315 break;
2316
2317 case '"':
2318 ++words_offset;
2319 error = parse_dquote (word: &word, word_length: &word_length, max_length: &max_length, words,
2320 offset: &words_offset, flags, pwordexp, ifs, ifs_white);
2321
2322 if (error)
2323 goto do_error;
2324
2325 if (!word_length)
2326 {
2327 error = w_addword (pwordexp, NULL);
2328
2329 if (error)
2330 return error;
2331 }
2332
2333 break;
2334
2335 case '\'':
2336 ++words_offset;
2337 error = parse_squote (word: &word, word_length: &word_length, max_length: &max_length, words,
2338 offset: &words_offset);
2339
2340 if (error)
2341 goto do_error;
2342
2343 if (!word_length)
2344 {
2345 error = w_addword (pwordexp, NULL);
2346
2347 if (error)
2348 return error;
2349 }
2350
2351 break;
2352
2353 case '~':
2354 error = parse_tilde (word: &word, word_length: &word_length, max_length: &max_length, words,
2355 offset: &words_offset, wordc: pwordexp->we_wordc);
2356
2357 if (error)
2358 goto do_error;
2359
2360 break;
2361
2362 case '*':
2363 case '[':
2364 case '?':
2365 error = parse_glob (word: &word, word_length: &word_length, max_length: &max_length, words,
2366 offset: &words_offset, flags, pwordexp, ifs, ifs_white);
2367
2368 if (error)
2369 goto do_error;
2370
2371 break;
2372
2373 default:
2374 /* Is it a word separator? */
2375 if (strchr (" \t", words[words_offset]) == NULL)
2376 {
2377 char ch = words[words_offset];
2378
2379 /* Not a word separator -- but is it a valid word char? */
2380 if (strchr ("\n|&;<>(){}", ch))
2381 {
2382 /* Fail */
2383 error = WRDE_BADCHAR;
2384 goto do_error;
2385 }
2386
2387 /* "Ordinary" character -- add it to word */
2388 word = w_addchar (buffer: word, actlen: &word_length, maxlen: &max_length,
2389 ch);
2390 if (word == NULL)
2391 {
2392 error = WRDE_NOSPACE;
2393 goto do_error;
2394 }
2395
2396 break;
2397 }
2398
2399 /* If a word has been delimited, add it to the list. */
2400 if (word != NULL)
2401 {
2402 error = w_addword (pwordexp, word);
2403 if (error)
2404 goto do_error;
2405 }
2406
2407 word = w_newword (actlen: &word_length, maxlen: &max_length);
2408 }
2409
2410 /* End of string */
2411
2412 /* There was a word separator at the end */
2413 if (word == NULL) /* i.e. w_newword */
2414 return 0;
2415
2416 /* There was no field separator at the end */
2417 return w_addword (pwordexp, word);
2418
2419do_error:
2420 /* Error:
2421 * free memory used (unless error is WRDE_NOSPACE), and
2422 * set pwordexp members back to what they were.
2423 */
2424
2425 free (ptr: word);
2426
2427 if (error == WRDE_NOSPACE)
2428 return WRDE_NOSPACE;
2429
2430 if ((flags & WRDE_APPEND) == 0)
2431 wordfree (pwordexp);
2432
2433 *pwordexp = old_word;
2434 return error;
2435}
2436

source code of glibc/posix/wordexp.c