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