OSDN Git Service

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