Completely reworked buffer handling for much better performance.
[kopensolaris-gnu/glibc.git] / posix / wordexp.c
1 /* POSIX.2 wordexp implementation.
2    Copyright (C) 1997 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Tim Waugh <tim@cyberelk.demon.co.uk>.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public
17    License along with the GNU C Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 #include <wordexp.h>
22 #include <signal.h>
23 #include <stdlib.h>
24 #include <pwd.h>
25 #include <sys/types.h>
26 #include <string.h>
27 #include <glob.h>
28 #include <ctype.h>
29 #include <sys/time.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <sys/stat.h>
35 #include <paths.h>
36 #include <errno.h>
37 #include <sys/param.h>
38 #include <stdio.h>
39
40 #include <assert.h>
41
42 /*
43  * This is a recursive-descent-style word expansion routine.
44  */
45
46 /* Some forward declarations */
47 static int parse_dollars (char **word, size_t *word_length, size_t *max_length,
48                           const char *words, size_t *offset, int flags,
49                           wordexp_t *pwordexp);
50 static int parse_backtick (char **word, size_t *word_length,
51                            size_t *max_length, const char *words,
52                            size_t *offset, int flags, wordexp_t *pwordexp);
53 static int eval_expr (char *expr, int *result);
54
55 /* The w_*() functions manipulate word lists. */
56
57 #define W_CHUNK (100)
58
59 static inline char *
60 w_addchar (char *buffer, size_t *actlen, size_t *maxlen, char ch)
61      /* (lengths exclude trailing zero) */
62 {
63   /* Add a character to the buffer, allocating room for it if needed.
64    */
65
66   if (*actlen == *maxlen)
67     {
68       char *old_buffer = buffer;
69       assert (buffer == NULL || *maxlen != 0);
70       *maxlen += W_CHUNK;
71       buffer = realloc (buffer, 1 + *maxlen);
72
73       if (buffer == NULL)
74         free (old_buffer);
75     }
76
77   if (buffer != NULL)
78     {
79       buffer[*actlen] = ch;
80       buffer[++(*actlen)] = '\0';
81     }
82
83   return buffer;
84 }
85
86 static char *
87 w_addstr (char *buffer, size_t *actlen, size_t *maxlen, const char *str)
88      /* (lengths exclude trailing zero) */
89 {
90   /* Add a string to the buffer, allocating room for it if needed.
91    */
92   size_t len;
93
94   assert (str != NULL); /* w_addstr only called from this file */
95   len = strlen (str);
96
97   if (*actlen + len > *maxlen)
98     {
99       char *old_buffer = buffer;
100       assert (buffer == NULL || *maxlen != 0);
101       *maxlen += MAX (2 * len, W_CHUNK);
102       buffer = realloc (old_buffer, 1 + *maxlen);
103
104       if (buffer == NULL)
105         free (old_buffer);
106     }
107
108   if (buffer != NULL)
109     {
110       memcpy (&buffer[*actlen], str, len);
111       *actlen += len;
112       buffer[*actlen] = '\0';
113     }
114
115   return buffer;
116 }
117
118 static int
119 w_addword (wordexp_t *pwordexp, char *word)
120 {
121   /* Add a word to the wordlist */
122   size_t num_p;
123
124   num_p = 2 + pwordexp->we_wordc + pwordexp->we_offs;
125   pwordexp->we_wordv = realloc (pwordexp->we_wordv, sizeof (char *) * num_p);
126   if (pwordexp->we_wordv != NULL)
127     {
128       pwordexp->we_wordv[pwordexp->we_wordc++] = word;
129       pwordexp->we_wordv[pwordexp->we_wordc] = NULL;
130       return 0;
131     }
132
133   return WRDE_NOSPACE;
134 }
135
136 /* The parse_*() functions should leave *offset being the offset in 'words'
137  * to the last character processed.
138  */
139
140 static int
141 parse_backslash (char **word, size_t *word_length, size_t *max_length,
142                  const char *words, size_t *offset)
143 {
144   /* We are poised _at_ a backslash, not in quotes */
145
146   switch (words[1 + *offset])
147     {
148     case 0:
149       /* Backslash is last character of input words */
150       return WRDE_SYNTAX;
151
152     case '\n':
153       (*offset)++;
154       break;
155
156     default:
157       *word = w_addchar (*word, word_length, max_length, words[1 + *offset]);
158       if (*word == NULL)
159         return WRDE_NOSPACE;
160
161       (*offset)++;
162       break;
163     }
164
165   return 0;
166 }
167
168 static int
169 parse_qtd_backslash (char **word, size_t *word_length, size_t *max_length,
170                      const char *words, size_t *offset)
171 {
172   /* We are poised _at_ a backslash, inside quotes */
173
174   switch (words[1 + *offset])
175     {
176     case 0:
177       /* Backslash is last character of input words */
178       return WRDE_SYNTAX;
179
180     case '\n':
181       ++(*offset);
182       break;
183
184     case '$':
185     case '`':
186     case '"':
187     case '\\':
188       *word = w_addchar (*word, word_length, max_length, words[1 + *offset]);
189       if (*word == NULL)
190         return WRDE_NOSPACE;
191
192       ++(*offset);
193       break;
194
195     default:
196       *word = w_addchar (*word, word_length, max_length, words[*offset]);
197       if (*word != NULL)
198         *word = w_addchar (*word, word_length, max_length, words[1 + *offset]);
199
200       if (*word == NULL)
201         return WRDE_NOSPACE;
202
203       ++(*offset);
204       break;
205     }
206
207   return 0;
208 }
209
210 static int
211 parse_tilde (char **word, size_t *word_length, size_t *max_length,
212              const char *words, size_t *offset, size_t wordc)
213 {
214   /* We are poised _at_ a tilde */
215   size_t i;
216
217   if (*word_length != 0)
218     {
219       if (!((*word)[*word_length - 1] == '=' && wordc == 0))
220         {
221           if (!((*word)[*word_length - 1] == ':' &&
222                 strchr (*word, '=') && wordc == 0))
223             {
224               *word = w_addchar (*word, word_length, max_length, '~');
225               return *word ? 0 : WRDE_NOSPACE;
226             }
227         }
228     }
229
230   for (i = 1 + *offset; words[i]; i++)
231     {
232       if (words[i] == ':' || words[i] == '/' || words[i] == ' ' ||
233           words[i] == '\t' || words[i] == 0 )
234         break;
235
236       if (words[i] == '\\')
237         {
238           *word = w_addchar (*word, word_length, max_length, '~');
239           return *word ? 0 : WRDE_NOSPACE;
240         }
241     }
242
243   if (i == 1 + *offset)
244     {
245       /* Tilde appears on its own */
246       uid_t uid;
247       struct passwd pwd, *tpwd;
248       int buflen = 1000;
249       char* buffer = __alloca (buflen);
250       int result;
251
252       uid = getuid ();
253
254       while ((result = __getpwuid_r (uid, &pwd, buffer, buflen, &tpwd)) != 0
255              && errno == ERANGE)
256         {
257           buflen += 1000;
258           buffer = __alloca (buflen);
259         }
260
261       if (result == 0 && pwd.pw_dir != NULL)
262         {
263           *word = w_addstr (*word, word_length, max_length, pwd.pw_dir);
264           if (*word == NULL)
265             return WRDE_NOSPACE;
266         }
267       else
268         {
269           *word = w_addchar (*word, word_length, max_length, '~');
270           if (*word == NULL)
271             return WRDE_NOSPACE;
272         }
273     }
274   else
275     {
276       /* Look up user name in database to get home directory */
277       char *user = __strndup (&words[1 + *offset], i - *offset);
278       struct passwd pwd, *tpwd;
279       int buflen = 1000;
280       char* buffer = __alloca (buflen);
281       int result;
282
283       while ((result = __getpwnam_r (user, &pwd, buffer, buflen, &tpwd)) != 0
284              && errno == ERANGE)
285         {
286           buflen += 1000;
287           buffer = __alloca (buflen);
288         }
289
290       if (result == 0 && pwd.pw_dir)
291         *word = w_addstr (*word, word_length, max_length, pwd.pw_dir);
292       else
293         {
294           /* (invalid login name) */
295           *word = w_addchar (*word, word_length, max_length, '~');
296           if (*word != NULL)
297             *word = w_addstr (*word, word_length, max_length, user);
298         }
299
300       *offset = i - 1;
301     }
302   return *word ? 0 : WRDE_NOSPACE;
303 }
304
305 static int
306 parse_glob (char **word, size_t *word_length, size_t *max_length,
307             const char *words, size_t *offset, int flags,
308             wordexp_t *pwordexp, char *ifs)
309 {
310   /* We are poised just after a '*' or a '{'. */
311   int error;
312   glob_t globbuf;
313   int match;
314   char *matching_word;
315
316   for (; words[*offset]; (*offset)++)
317     switch (words[*offset])
318       {
319       case ' ':
320       case '\t':
321         break;
322
323       case '$':
324         error = parse_dollars (word, word_length, max_length, words, offset,
325                                flags, pwordexp);
326         if (error)
327           return error;
328
329         continue;
330
331       default:
332         if (ifs == NULL || strchr (ifs, words[*offset]) == NULL)
333           {
334             *word = w_addchar (*word, word_length, max_length, words[*offset]);
335             if (*word == NULL)
336               return WRDE_NOSPACE;
337
338             continue;
339           }
340
341         break;
342       }
343
344   error = glob (*word, GLOB_NOCHECK, NULL, &globbuf);
345
346   if (error != 0)
347     {
348       /* We can only run into memory problems.  */
349       assert (error == GLOB_NOSPACE);
350
351       return WRDE_NOSPACE;
352     }
353
354   if (ifs && !*ifs)
355     {
356       /* No field splitting allowed */
357       *word_length = strlen (globbuf.gl_pathv[0]);
358       *word = realloc (*word, 1 + *word_length);
359       if (*word == NULL)
360         goto no_space;
361
362       strcpy (*word, globbuf.gl_pathv[0]);
363
364       for (match = 1; match < globbuf.gl_pathc && *word != NULL; ++match)
365         {
366           *word = w_addchar (*word, word_length, max_length, ' ');
367           if (*word != NULL)
368             *word = w_addstr (*word, word_length, max_length,
369                               globbuf.gl_pathv[match]);
370         }
371
372       /* Re-parse white space on return */
373       globfree (&globbuf);
374       --(*offset);
375       return *word ? 0 : WRDE_NOSPACE;
376     }
377
378   /* here ifs != "" */
379   free (*word);
380   *word = NULL;
381   *word_length = 0;
382
383   matching_word = malloc (1 + strlen (globbuf.gl_pathv[0]));
384   if (matching_word == NULL)
385     goto no_space;
386
387   strcpy (matching_word, globbuf.gl_pathv[0]);
388   if (w_addword (pwordexp, matching_word) == WRDE_NOSPACE)
389     goto no_space;
390
391   for (match = 1; match < globbuf.gl_pathc; ++match)
392     {
393       matching_word = __strdup (globbuf.gl_pathv[match]);
394       if (matching_word == NULL)
395         goto no_space;
396
397       if (w_addword (pwordexp, matching_word) == WRDE_NOSPACE)
398         goto no_space;
399     }
400
401   globfree (&globbuf);
402
403   /* Re-parse white space on return */
404   --(*offset);
405   return 0;
406
407 no_space:
408   globfree (&globbuf);
409   return WRDE_NOSPACE;
410 }
411
412 static int
413 parse_squote (char **word, size_t *word_length, size_t *max_length,
414               const char *words, size_t *offset)
415 {
416   /* We are poised just after a single quote */
417   for (; words[*offset]; ++(*offset))
418     {
419       if (words[*offset] != '\'')
420         {
421           *word = w_addchar (*word, word_length, max_length, words[*offset]);
422           if (*word == NULL)
423             return WRDE_NOSPACE;
424         }
425       else return 0;
426     }
427
428   /* Unterminated string */
429   return WRDE_SYNTAX;
430 }
431
432 /* Functions to evaluate an arithmetic expression */
433 static int
434 eval_expr_val (char **expr, int *result)
435 {
436   int sgn = +1;
437   char *digit;
438
439   /* Skip white space */
440   for (digit = *expr; digit && *digit && isspace (*digit); ++digit);
441
442   switch (*digit)
443     {
444     case '(':
445
446       /* Scan for closing paren */
447       for (++digit; **expr && **expr != ')'; ++(*expr));
448
449       /* Is there one? */
450       if (!**expr)
451         return WRDE_SYNTAX;
452
453       *(*expr)++ = 0;
454
455       if (eval_expr (digit, result))
456         return WRDE_SYNTAX;
457
458       return 0;
459
460     case '+':   /* Positive value */
461       ++digit;
462       break;
463
464     case '-':   /* Negative value */
465       ++digit;
466       sgn = -1;
467       break;
468
469     default:
470       if (!isdigit (*digit))
471         return WRDE_SYNTAX;
472     }
473
474   *result = 0;
475   for (; *digit && isdigit (*digit); ++digit)
476     *result = (*result * 10) + (*digit - '0');
477
478   *expr = digit;
479   *result *= sgn;
480   return 0;
481 }
482
483 static int
484 eval_expr_multdiv (char **expr, int *result)
485 {
486   int arg;
487
488   /* Read a Value */
489   if (eval_expr_val (expr, result))
490     return WRDE_SYNTAX;
491
492   while (**expr)
493     {
494       /* Skip white space */
495       for (; *expr && **expr && isspace (**expr); ++(*expr));
496
497       if (**expr == '*')
498         {
499           (*expr)++;
500           if ((eval_expr_val (expr, &arg)) != 0)
501             return WRDE_SYNTAX;
502
503           *result *= arg;
504         }
505       else if (**expr == '/')
506         {
507           (*expr)++;
508           if ((eval_expr_val (expr, &arg)) != 0)
509             return WRDE_SYNTAX;
510
511           *result /= arg;
512         }
513       else break;
514     }
515
516   return 0;
517 }
518
519 static int
520 eval_expr (char *expr, int *result)
521 {
522   int arg;
523
524   /* Read a Multdiv */
525   if ((eval_expr_multdiv (&expr, result)) != 0)
526     return WRDE_SYNTAX;
527
528   while (*expr)
529     {
530       /* Skip white space */
531       for (; expr && *expr && isspace (*expr); ++expr);
532
533       if (*expr == '+')
534         {
535           expr++;
536           if ((eval_expr_multdiv (&expr, &arg)) != 0)
537             return WRDE_SYNTAX;
538
539           *result += arg;
540         }
541       else if (*expr == '-')
542         {
543           expr++;
544           if ((eval_expr_multdiv (&expr, &arg)) != 0)
545             return WRDE_SYNTAX;
546
547           *result -= arg;
548         }
549       else break;
550     }
551
552   return 0;
553 }
554
555 static int
556 parse_arith (char **word, size_t *word_length, size_t *max_length,
557              const char *words, size_t *offset, int flags, int bracket)
558 {
559   /* We are poised just after "$((" or "$[" */
560   int error;
561   int paren_depth = 1;
562   size_t expr_length = 0;
563   size_t expr_maxlen = 0;
564   char *expr = NULL;
565
566   for (; words[*offset]; ++(*offset))
567     {
568       switch (words[*offset])
569         {
570         case '$':
571           error = parse_dollars (&expr, &expr_length, &expr_maxlen,
572                                  words, offset, flags, NULL);
573           /* The NULL here is to tell parse_dollars not to
574            * split the fields.
575            */
576           if (error)
577             {
578               free (expr);
579               return error;
580             }
581           break;
582
583         case '`':
584           (*offset)++;
585           error = parse_backtick (&expr, &expr_length, &expr_maxlen,
586                                   words, offset, flags, NULL);
587           /* The NULL here is to tell parse_backtick not to
588            * split the fields.
589            */
590           if (error)
591             {
592               free (expr);
593               return error;
594             }
595           break;
596
597         case '\\':
598           error = parse_qtd_backslash (&expr, &expr_length, &expr_maxlen,
599                                        words, offset);
600           if (error)
601             {
602               free (expr);
603               return error;
604             }
605           /* I think that a backslash within an
606            * arithmetic expansion is bound to
607            * cause an error sooner or later anyway though.
608            */
609           break;
610
611         case ')':
612           if (--paren_depth == 0)
613             {
614               char *result;
615               int numresult = 0;
616
617               if (bracket || words[1 + *offset] != ')')
618                 return WRDE_SYNTAX;
619
620               ++(*offset);
621
622               /* Go - evaluate. */
623               if (*expr &&
624                   eval_expr (expr, &numresult) != 0)
625                 return WRDE_SYNTAX;
626
627               result = __alloca (100);
628               __snprintf (result, 100, "%d", numresult);
629               *word = w_addstr (*word, word_length, max_length, result);
630               free (expr);
631               return *word ? 0 : WRDE_NOSPACE;
632             }
633           expr = w_addchar (expr, &expr_length, &expr_maxlen, words[*offset]);
634           if (expr == NULL)
635             return WRDE_NOSPACE;
636
637           break;
638
639         case ']':
640           if (bracket && paren_depth == 1)
641             {
642               char *result;
643               int numresult = 0;
644
645               /* Go - evaluate. */
646               if (*expr && eval_expr (expr, &numresult) != 0)
647                 return WRDE_SYNTAX;
648
649               result = __alloca (100);
650               __snprintf (result, 100, "%d", numresult);
651               *word = w_addstr (*word, word_length, max_length, result);
652               free (expr);
653               return *word ? 0 : WRDE_NOSPACE;
654             }
655
656           free (expr);
657           return WRDE_SYNTAX;
658
659         case '\n':
660         case ';':
661         case '{':
662         case '}':
663           free (expr);
664           return WRDE_BADCHAR;
665
666         case '(':
667           ++paren_depth;
668         default:
669           expr = w_addchar (expr, &expr_length, &expr_maxlen, words[*offset]);
670           if (expr == NULL)
671             return WRDE_NOSPACE;
672         }
673     }
674
675   /* Premature end */
676   free (expr);
677   return WRDE_SYNTAX;
678 }
679
680 /* Function to execute a command and retrieve the results */
681 /* pwordexp contains NULL if field-splitting is forbidden */
682 static int
683 exec_comm (char *comm, char **word, size_t *word_length, size_t *max_length,
684            int flags, wordexp_t *pwordexp)
685 {
686   int fildes[2];
687   int bufsize = 128;
688   int buflen;
689   int state = 0;
690   int i;
691   char *buffer;
692   pid_t pid;
693   /* 'state' is:
694    *  0 until first non-(whitespace-ifs)
695    *  1 after a non-ifs
696    *  2 after non-(whitespace-ifs)
697    */
698
699   /* Don't fork() unless necessary */
700   if (!comm || !*comm)
701     return 0;
702
703   if (pipe (fildes))
704     /* Bad */
705     return WRDE_NOSPACE;
706
707   if ((pid = fork ()) < 0)
708     {
709       /* Bad */
710       return WRDE_NOSPACE;
711     }
712
713   if (pid == 0)
714     {
715       /* Child */
716       /* Redirect input and output */
717       dup2 (fildes[1], 1);
718
719       /* Close stderr if we have to */
720       if ((flags & WRDE_SHOWERR) == 0)
721         close (2);
722
723       execl (_PATH_BSHELL, _PATH_BSHELL, "-c", comm, NULL);
724
725       /* Bad. What now? */
726       exit (1);
727     }
728
729   /* Parent */
730
731   close (fildes[1]);
732   buffer = __alloca (bufsize);
733
734   if (!pwordexp)
735     { /* Quoted - no field splitting */
736
737       while (1)
738         {
739           if ((buflen = read (fildes[0], buffer, bufsize)) < 1)
740             {
741               if (waitpid (pid, NULL, WNOHANG) == 0)
742                 continue;
743               if ((buflen = read (fildes[0], buffer, bufsize)) < 1)
744                 break;
745             }
746
747           for (i = 0; i < buflen; ++i)
748             {
749               *word = w_addchar (*word, word_length, max_length, buffer[i]);
750               if (*word == NULL)
751                 {
752                   close (fildes[0]);
753                   return WRDE_NOSPACE;
754                 }
755             }
756         }
757
758       close (fildes[0]);
759       return 0;
760     }
761
762   /* Not quoted - split fields.
763    * NB. This isn't done properly yet.
764    */
765   while (1)
766     {
767       if ((buflen = read (fildes[0], buffer, bufsize)) < 1)
768         {
769           if (waitpid (pid, NULL, WNOHANG) == 0)
770             continue;
771           if ((read (fildes[0], buffer, bufsize)) < 1)
772             break;
773         }
774
775       for (i = 0; i < buflen; ++i)
776         {
777           /* What if these aren't field separators? FIX */
778           if (buffer[i] == ' ' || buffer[i] == '\t' || buffer[i] == '\n')
779             {
780               if (state != 0)
781                 state = 2;
782               continue;
783             }
784
785           if (state == 2)
786             {
787               /* End of word */
788               if (w_addword (pwordexp, *word) == WRDE_NOSPACE)
789                 {
790                   close (fildes[0]);
791                   return WRDE_NOSPACE;
792                 }
793
794               *word = NULL;
795               *word_length = 0;
796             }
797
798           state = 1;
799           *word = w_addchar (*word, word_length, max_length, buffer[i]);
800           if (*word == NULL)
801             {
802               close (fildes[0]);
803               return WRDE_NOSPACE;
804             }
805         }
806     }
807
808   close (fildes[0]);
809   return 0;
810 }
811
812 static int
813 parse_comm (char **word, size_t *word_length, size_t *max_length,
814             const char *words, size_t *offset, int flags, wordexp_t *pwordexp)
815 {
816   /* We are poised just after "$(" */
817   int paren_depth = 1;
818   int error;
819   size_t comm_length = 0;
820   size_t comm_maxlen = 0;
821   char *comm = NULL;
822
823   for (; words[*offset]; ++(*offset))
824     {
825       switch (words[*offset])
826         {
827         case ')':
828           if (--paren_depth == 0)
829             {
830               /* Go -- give script to the shell */
831               error = exec_comm (comm, word, word_length, max_length, flags,
832                                  pwordexp);
833               free (comm);
834               return error;
835             }
836
837           /* This is just part of the script */
838           comm = w_addchar (comm, &comm_length, &comm_maxlen, words[*offset]);
839           if (comm == NULL)
840             return WRDE_NOSPACE;
841
842           break;
843
844         case '(':
845           paren_depth++;
846         default:
847           comm = w_addchar (comm, &comm_length, &comm_maxlen, words[*offset]);
848           if (comm == NULL)
849             return WRDE_NOSPACE;
850
851           break;
852         }
853     }
854
855   /* Premature end */
856   free (comm);
857   return WRDE_SYNTAX;
858 }
859
860 static int
861 parse_param (char **word, size_t *word_length, size_t *max_length,
862              const char *words, size_t *offset, int flags, wordexp_t *pwordexp)
863 {
864   /* We are poised just after "$" */
865   size_t start = *offset;
866   size_t env_length = 0;
867   size_t env_maxlen = 0;
868   size_t pat_length = 0;
869   size_t pat_maxlen = 0;
870   char *env = NULL;
871   char *pattern = NULL;
872   char *value;
873   char action = 0;
874   int prefix = 0;
875   int suffix = 0;
876   int colon_seen = 0;
877   int depth = 0;
878   int error;
879
880   for (; words[*offset]; ++(*offset))
881     {
882       switch (words[*offset])
883         {
884         case '{':
885           if (action || prefix || suffix)
886             {
887               ++depth;
888               pattern = w_addchar (pattern, &pat_length, &pat_maxlen,
889                                    words[*offset]);
890               if (pattern == NULL)
891                 goto no_space;
892
893               break;
894             }
895
896           if (*offset == start)
897             break;
898           /* Otherwise evaluate */
899           /* (and re-parse this character) */
900           --(*offset);
901           goto envsubst;
902
903         case '}':
904           if (words[start] != '{')
905             {
906               --(*offset);
907             }
908
909           if (action || prefix || suffix)
910             {
911               if (--depth)
912                 {
913                   pattern = w_addchar (pattern, &pat_length, &pat_maxlen,
914                                        words[*offset]);
915                   if (pattern == NULL)
916                     goto no_space;
917
918                   break;
919                 }
920             }
921
922           /* Evaluate */
923           goto envsubst;
924
925         case '#':
926         case '%':
927           if (words[start] == '{')
928             {
929               /* At the start?  (ie. 'string length') */
930               if (*offset == start + 1)
931                 break;
932
933               /* Separating variable name from prefix pattern? */
934               if (words[*offset] == '#')
935                 {
936                   if (prefix < 2 && !suffix)
937                     {
938                       ++prefix;
939                       break;
940                     }
941                 }
942               else
943                 {
944                   if (suffix < 2 && !prefix)
945                     {
946                       ++suffix;
947                       break;
948                     }
949                 }
950
951               /* Must be part of prefix/suffix pattern. */
952               pattern = w_addchar (pattern, &pat_length, &pat_maxlen,
953                                    words[*offset]);
954               if (pattern == NULL)
955                 goto no_space;
956
957               break;
958             }
959           /* Otherwise evaluate */
960           /* (and re-parse this character) */
961           --(*offset);
962           goto envsubst;
963
964         case ':':
965           if (!*env)
966             {
967               free (env);
968               free (pattern);
969               return WRDE_SYNTAX;
970             }
971
972           if (action || prefix || suffix)
973             {
974               pattern = w_addchar (pattern, &pat_length, &pat_maxlen,
975                                    words[*offset]);
976               if (pattern == NULL)
977                 goto no_space;
978
979               break;
980             }
981
982           if ((words[1 + *offset] == '-') || (words[1 + *offset] == '=') ||
983               (words[1 + *offset] == '?') || (words[1 + *offset] == '+'))
984             {
985               colon_seen = 1;
986               break;
987             }
988
989           free (env);
990           free (pattern);
991           return WRDE_SYNTAX;
992
993         case '-':
994         case '=':
995         case '?':
996         case '+':
997           if (!*env)
998             {
999               free (env);
1000               free (pattern);
1001               return WRDE_SYNTAX;
1002             }
1003
1004           if (action || prefix || suffix)
1005             {
1006               pattern = w_addchar (pattern, &pat_length, &pat_maxlen,
1007                                    words[*offset]);
1008               if (pattern == NULL)
1009                 {
1010                   free (env);
1011                   return WRDE_NOSPACE;
1012                 }
1013
1014               break;
1015             }
1016
1017           action = words[*offset];
1018           break;
1019
1020         case '\\':
1021           if (action || prefix || suffix)
1022             {
1023               error = parse_qtd_backslash (word, word_length, max_length,
1024                                            words, offset);
1025               if (error == 0)
1026                 break;
1027             }
1028           else
1029             {
1030               error = WRDE_SYNTAX;
1031             }
1032
1033           free (env);
1034           free (pattern);
1035           return error;
1036
1037         default:
1038           if (action || prefix || suffix)
1039             {
1040               pattern = w_addchar (pattern, &pat_length, &pat_maxlen,
1041                                    words[*offset]);
1042               if (pattern == NULL)
1043                 goto no_space;
1044
1045               break;
1046             }
1047
1048           if ((words[start] == '{') || isalpha (words[*offset]))
1049             {
1050               env = w_addchar (env, &env_length, &env_maxlen, words[*offset]);
1051               if (env == NULL)
1052                 goto no_space;
1053
1054               break;
1055             }
1056
1057           --(*offset);
1058           goto envsubst;
1059         }
1060     }
1061
1062   /* End of input string */
1063   --(*offset);
1064
1065 envsubst:
1066   if (words[start] == '{' && words[*offset] != '}')
1067     {
1068       free (env);
1069       free (pattern);
1070       return WRDE_SYNTAX;
1071     }
1072
1073   if (!env || !*env)
1074     {
1075       *offset = start - 1;
1076       *word = w_addchar (*word, word_length, max_length, '$');
1077       free (env);
1078       free (pattern);
1079       return *word ? 0 : WRDE_NOSPACE;
1080     }
1081
1082   value = getenv (env);
1083
1084   if (action || prefix || suffix)
1085     {
1086       switch (action)
1087         {
1088         case 0:
1089           /* For the time being, pattern is ignored */
1090           printf ("Pattern: %s\nPrefix: %d\nSuffix: %d\n", pattern, prefix, suffix);
1091           break;
1092
1093         case '?':
1094           if (value && *value)
1095             break;
1096
1097           if (!colon_seen && value)
1098             {
1099               /* Substitute NULL */
1100               free (env);
1101               free (pattern);
1102               return 0;
1103             }
1104
1105           /* Error - exit */
1106           fprintf (stderr, "%s: ", env);
1107
1108           if (*pattern)
1109             {
1110               /* Expand 'pattern' and write it to stderr */
1111               wordexp_t we;
1112
1113               error = wordexp (pattern, &we, flags);
1114
1115               if (error == 0)
1116                 {
1117                   int i;
1118
1119                   for (i = 0; i < we.we_wordc; ++i)
1120                     {
1121                       fprintf (stderr, "%s%s", i ? " " : "", we.we_wordv[i]);
1122                     }
1123
1124                   fprintf (stderr, "\n");
1125                   error = WRDE_BADVAL;
1126                 }
1127
1128               wordfree (&we);
1129               free (env);
1130               free (pattern);
1131               return error;
1132             }
1133
1134           fprintf (stderr, "parameter null or not set\n");
1135           free (env);
1136           free (pattern);
1137           return WRDE_BADVAL;
1138
1139         default:
1140           printf ("warning: parameter substitution does not yet support \"%s%c\"\n", colon_seen?":":"", action);
1141         }
1142     }
1143
1144   free (env);
1145   free (pattern);
1146
1147   if (value == NULL)
1148     {
1149       /* Variable not defined */
1150       if (flags & WRDE_UNDEF)
1151         return WRDE_SYNTAX;
1152
1153       return 0;
1154     }
1155
1156   if (pwordexp == NULL)
1157     /* Quoted - no field split */
1158     *word = w_addstr (*word, word_length, max_length, value);
1159   else
1160     /* Should field-split here - FIX */
1161     *word = w_addstr (*word, word_length, max_length, value);
1162
1163   return *word ? 0 : WRDE_NOSPACE;
1164
1165 no_space:
1166   if (env)
1167     free (env);
1168
1169   if (pattern)
1170     free (pattern);
1171
1172   return WRDE_NOSPACE;
1173 }
1174
1175 static int
1176 parse_dollars (char **word, size_t *word_length, size_t *max_length,
1177                const char *words, size_t *offset, int flags,
1178                wordexp_t *pwordexp)
1179 {
1180   /* We are poised _at_ "$" */
1181   switch (words[1 + *offset])
1182     {
1183     case '"':
1184     case '\'':
1185     case 0:
1186       *word = w_addchar (*word, word_length, max_length, '$');
1187       return *word ? 0 : WRDE_NOSPACE;
1188
1189     case '(':
1190       if (words[2 + *offset] == '(')
1191         {
1192           (*offset) += 3;
1193           /* Call parse_arith -- 0 is for "no brackets" */
1194           return parse_arith (word, word_length, max_length, words, offset,
1195                               flags, 0);
1196         }
1197
1198       if (flags & WRDE_NOCMD)
1199         return WRDE_CMDSUB;
1200
1201       (*offset) += 2;
1202       return parse_comm (word, word_length, max_length, words, offset, flags,
1203                          pwordexp);
1204
1205     case '[':
1206       (*offset) += 2;
1207       /* Call parse_arith -- 1 is for "brackets" */
1208       return parse_arith (word, word_length, max_length, words, offset, flags,
1209                           1);
1210
1211     case '{':
1212     default:
1213       ++(*offset);      /* parse_param needs to know if "{" is there */
1214       return parse_param (word, word_length, max_length, words, offset, flags,
1215                           pwordexp);
1216     }
1217 }
1218
1219 static int
1220 parse_backtick (char **word, size_t *word_length, size_t *max_length,
1221                 const char *words, size_t *offset, int flags,
1222                 wordexp_t *pwordexp)
1223 {
1224   /* We are poised just after "`" */
1225   int error;
1226   size_t comm_length = 0;
1227   size_t comm_maxlen = 0;
1228   char *comm = NULL;
1229   int squoting = 0;
1230
1231   for (; words[*offset]; ++(*offset))
1232     {
1233       switch (words[*offset])
1234         {
1235         case '`':
1236           /* Go -- give the script to the shell */
1237           error = exec_comm (comm, word, word_length, max_length, flags,
1238                              pwordexp);
1239           free (comm);
1240           return error;
1241
1242         case '\\':
1243           if (squoting)
1244             {
1245               error = parse_qtd_backslash (&comm, &comm_length, &comm_maxlen,
1246                                            words, offset);
1247
1248               if (error)
1249                 {
1250                   free (comm);
1251                   return error;
1252                 }
1253
1254               break;
1255             }
1256
1257           ++(*offset);
1258           error = parse_backslash (&comm, &comm_length, &comm_maxlen, words,
1259                                    offset);
1260
1261           if (error)
1262             {
1263               free (comm);
1264               return error;
1265             }
1266
1267           break;
1268
1269         case '\'':
1270           squoting = 1 - squoting;
1271         default:
1272           comm = w_addchar (comm, &comm_length, &comm_maxlen, words[*offset]);
1273           if (comm == NULL)
1274             return WRDE_NOSPACE;
1275         }
1276     }
1277
1278   /* Premature end */
1279   free (comm);
1280   return WRDE_SYNTAX;
1281 }
1282
1283 static int
1284 parse_dquote (char **word, size_t *word_length, size_t *max_length,
1285               const char *words, size_t *offset, int flags)
1286 {
1287   /* We are poised just after a double-quote */
1288   int error;
1289
1290   for (; words[*offset]; ++(*offset))
1291     {
1292       switch (words[*offset])
1293         {
1294         case '"':
1295           return 0;
1296
1297         case '$':
1298           error = parse_dollars (word, word_length, max_length, words, offset,
1299                                  flags, NULL);
1300           /* The NULL here is to tell parse_dollars not to
1301            * split the fields.
1302            */
1303           if (error)
1304             return error;
1305
1306           break;
1307
1308         case '`':
1309           if (flags & WRDE_NOCMD)
1310             return WRDE_CMDSUB;
1311
1312           ++(*offset);
1313           error = parse_backtick (word, word_length, max_length, words,
1314                                   offset, flags, NULL);
1315           /* The NULL here is to tell parse_backtick not to
1316            * split the fields.
1317            */
1318           if (error)
1319             return error;
1320
1321           break;
1322
1323         case '\\':
1324           error = parse_qtd_backslash (word, word_length, max_length, words,
1325                                        offset);
1326
1327           if (error)
1328             return error;
1329
1330           break;
1331
1332         default:
1333           *word = w_addchar (*word, word_length, max_length, words[*offset]);
1334           if (*word == NULL)
1335             return WRDE_NOSPACE;
1336         }
1337     }
1338
1339   /* Unterminated string */
1340   return WRDE_SYNTAX;
1341 }
1342
1343 /*
1344  * wordfree() is to be called after pwordexp is finished with.
1345  */
1346
1347 void
1348 wordfree (wordexp_t *pwordexp)
1349 {
1350
1351   /* wordexp can set pwordexp to NULL */
1352   if (pwordexp && pwordexp->we_wordv)
1353     {
1354       char **wordv = pwordexp->we_wordv;
1355
1356       for (wordv += pwordexp->we_offs; *wordv; ++wordv)
1357         free (*wordv);
1358
1359       free (pwordexp->we_wordv);
1360       pwordexp->we_wordv = NULL;
1361     }
1362 }
1363
1364 /*
1365  * wordexp()
1366  */
1367
1368 int
1369 wordexp (const char *words, wordexp_t *pwordexp, int flags)
1370 {
1371   size_t wordv_offset;
1372   size_t words_offset;
1373   size_t word_length = 0;
1374   size_t max_length = 0;
1375   char *word = NULL;
1376   int error;
1377   char *ifs;
1378   char ifs_white[4];
1379   char **old_wordv = pwordexp->we_wordv;
1380   size_t old_wordc = pwordexp->we_wordc;
1381
1382   if (flags & WRDE_REUSE)
1383     /* Minimal implementation of WRDE_REUSE for now */
1384     wordfree (pwordexp);
1385
1386   if (flags & WRDE_DOOFFS)
1387     {
1388       pwordexp->we_wordv = calloc (1 + pwordexp->we_offs, sizeof (char *));
1389       if (pwordexp->we_wordv == NULL)
1390         return WRDE_NOSPACE;
1391     }
1392   else
1393     {
1394       pwordexp->we_wordv = calloc (1, sizeof (char *));
1395       if (pwordexp->we_wordv == NULL)
1396         return WRDE_NOSPACE;
1397
1398       pwordexp->we_offs = 0;
1399     }
1400
1401   if ((flags & WRDE_APPEND) == 0)
1402     pwordexp->we_wordc = 0;
1403
1404   wordv_offset = pwordexp->we_offs + pwordexp->we_wordc;
1405
1406   /* Find out what the field separators are.
1407    * There are two types: whitespace and non-whitespace.
1408    */
1409   ifs = getenv ("IFS");
1410
1411   if (!ifs)
1412     ifs = strcpy (ifs_white, " \t\n");
1413   else
1414     {
1415       char *ifsch = ifs;
1416       char *whch = ifs_white;
1417
1418       while (*ifsch != '\0')
1419         if ((*ifsch == ' ') || (*ifsch == '\t') || (*ifsch == '\n'))
1420           {
1421             /* White space IFS.  See first whether it is already in our
1422                collection.  */
1423             char *runp = ifs_white;
1424
1425             while (runp < whch && *runp != '\0' && *runp != *ifsch)
1426               ++runp;
1427
1428             if (runp == whch)
1429               *whch++ = *ifsch;
1430           }
1431       *whch = '\0';
1432     }
1433
1434   for (words_offset = 0 ; words[words_offset] ; ++words_offset)
1435     switch (words[words_offset])
1436       {
1437       case '\n':
1438       case '|':
1439       case '&':
1440       case ';':
1441       case '<':
1442       case '>':
1443       case '(':
1444       case ')':
1445       case '}':
1446         /* Fail */
1447         wordfree (pwordexp);
1448         pwordexp->we_wordc = 0;
1449         pwordexp->we_wordv = old_wordv;
1450         return WRDE_BADCHAR;
1451
1452       case '\\':
1453         error = parse_backslash (&word, &word_length, &max_length, words,
1454                                  &words_offset);
1455
1456         if (error)
1457           goto do_error;
1458
1459         break;
1460
1461       case '$':
1462         error = parse_dollars (&word, &word_length, &max_length, words,
1463                                &words_offset, flags, pwordexp);
1464
1465         if (error)
1466           goto do_error;
1467
1468         break;
1469
1470       case '`':
1471         if (flags & WRDE_NOCMD)
1472           return WRDE_CMDSUB;
1473
1474         ++words_offset;
1475         error = parse_backtick (&word, &word_length, &max_length, words,
1476                                 &words_offset, flags, pwordexp);
1477
1478         if (error)
1479           goto do_error;
1480
1481         break;
1482
1483       case '"':
1484         ++words_offset;
1485         error = parse_dquote (&word, &word_length, &max_length, words,
1486                               &words_offset, flags);
1487
1488         if (error)
1489           goto do_error;
1490
1491         break;
1492
1493       case '\'':
1494         ++words_offset;
1495         error = parse_squote (&word, &word_length, &max_length, words,
1496                               &words_offset);
1497
1498         if (error)
1499           goto do_error;
1500
1501         break;
1502
1503       case '~':
1504         error = parse_tilde (&word, &word_length, &max_length, words,
1505                              &words_offset, pwordexp->we_wordc);
1506
1507         if (error)
1508           goto do_error;
1509
1510         break;
1511
1512       case '*':
1513       case '{':
1514         error = parse_glob (&word, &word_length, &max_length, words,
1515                             &words_offset, flags, pwordexp, ifs);
1516
1517         if (error)
1518           goto do_error;
1519
1520         break;
1521
1522       default:
1523         /* Is it a field separator? */
1524         if (strchr (ifs, words[words_offset]) == NULL)
1525           {
1526             /* "Ordinary" character -- add it to word */
1527
1528             word = w_addchar (word, &word_length, &max_length,
1529                               words[words_offset]);
1530             if (word == NULL)
1531               {
1532                 error = WRDE_NOSPACE;
1533                 goto do_error;
1534               }
1535
1536             break;
1537           }
1538
1539         /* Field separator */
1540         if (strchr (ifs_white, words[words_offset]))
1541           {
1542             /* It's a whitespace IFS char.  Ignore it at the beginning
1543                of a line and ignore multiple instances.  */
1544             if (!word || !*word)
1545               break;
1546
1547             if (w_addword (pwordexp, word) == WRDE_NOSPACE)
1548               {
1549                 error = WRDE_NOSPACE;
1550                 goto do_error;
1551               }
1552
1553             word = NULL;
1554             word_length = 0;
1555             break;
1556           }
1557
1558         /* It's a non-whitespace IFS char */
1559
1560         /* Multiple non-whitespace IFS chars are treated as one;
1561          * IS THIS CORRECT?
1562          */
1563         if (word != NULL)
1564           {
1565             if (w_addword (pwordexp, word) == WRDE_NOSPACE)
1566               {
1567                 error = WRDE_NOSPACE;
1568                 goto do_error;
1569               }
1570           }
1571
1572         word = NULL;
1573         word_length = 0;
1574         max_length = 0;
1575       }
1576
1577   /* End of string */
1578
1579   /* There was a field separator at the end */
1580   if (word == NULL)
1581     return 0;
1582
1583   /* There was no field separator at the end */
1584   return w_addword (pwordexp, word);
1585
1586 do_error:
1587   /* Error:
1588         free memory used, set we_wordc and wd_wordv back to what they were.
1589    */
1590   if (word != NULL)
1591     free (word);
1592
1593   wordfree (pwordexp);
1594   pwordexp->we_wordv = old_wordv;
1595   pwordexp->we_wordc = old_wordc;
1596   return error;
1597 }