Imported from ../bash-2.05b.tar.gz.
[platform/upstream/bash.git] / braces.c
1 /* braces.c -- code for doing word expansion in curly braces. */
2
3 /* Copyright (C) 1987-2002 Free Software Foundation, Inc.
4
5    This file is part of GNU Bash, the Bourne Again SHell.
6
7    Bash is free software; you can redistribute it and/or modify it
8    under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2, or (at your option)
10    any later version.
11
12    Bash is distributed in the hope that it will be useful, but WITHOUT
13    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
15    License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with Bash; see the file COPYING.  If not, write to the Free
19    Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
20
21 /* Stuff in curly braces gets expanded before all other shell expansions. */
22
23 #include "config.h"
24
25 #if defined (BRACE_EXPANSION)
26
27 #if defined (HAVE_UNISTD_H)
28 #  ifdef _MINIX
29 #    include <sys/types.h>
30 #  endif
31 #  include <unistd.h>
32 #endif
33
34 #include "bashansi.h"
35
36 #if defined (SHELL)
37 #  include "shell.h"
38 #endif /* SHELL */
39
40 #include "general.h"
41 #include "shmbutil.h"
42
43 #define brace_whitespace(c) (!(c) || (c) == ' ' || (c) == '\t' || (c) == '\n')
44
45 /* Basic idea:
46
47    Segregate the text into 3 sections: preamble (stuff before an open brace),
48    postamble (stuff after the matching close brace) and amble (stuff after
49    preamble, and before postamble).  Expand amble, and then tack on the
50    expansions to preamble.  Expand postamble, and tack on the expansions to
51    the result so far.
52  */
53
54 /* The character which is used to separate arguments. */
55 int brace_arg_separator = ',';
56
57 #if defined (__P)
58 static int brace_gobbler __P((char *, size_t, int *, int));
59 static char **expand_amble __P((char *, size_t));
60 static char **array_concat __P((char **, char **));
61 #else
62 static int brace_gobbler ();
63 static char **expand_amble ();
64 static char **array_concat ();
65 #endif
66
67 /* Return an array of strings; the brace expansion of TEXT. */
68 char **
69 brace_expand (text)
70      char *text;
71 {
72   register int start;
73   size_t tlen;
74   char *preamble, *postamble, *amble;
75   size_t alen;
76   char **tack, **result;
77   int i, j, c;
78
79   DECLARE_MBSTATE;
80
81   /* Find the text of the preamble. */
82   tlen = strlen (text);
83   i = 0;
84   c = brace_gobbler (text, tlen, &i, '{');
85
86   preamble = (char *)xmalloc (i + 1);
87   strncpy (preamble, text, i);
88   preamble[i] = '\0';
89
90   result = (char **)xmalloc (2 * sizeof (char *));
91   result[0] = preamble;
92   result[1] = (char *)NULL;
93
94   /* Special case.  If we never found an exciting character, then
95      the preamble is all of the text, so just return that. */
96   if (c != '{')
97     return (result);
98
99   /* Find the amble.  This is the stuff inside this set of braces. */
100   start = ++i;
101   c = brace_gobbler (text, tlen, &i, '}');
102
103   /* What if there isn't a matching close brace? */
104   if (c == 0)
105     {
106 #if defined (NOTDEF)
107       /* Well, if we found an unquoted BRACE_ARG_SEPARATOR between START
108          and I, then this should be an error.  Otherwise, it isn't. */
109       j = start;
110       while (j < i)
111         {
112           if (text[j] == '\\')
113             {
114               j++;
115               ADVANCE_CHAR (text, tlen, j);
116               continue;
117             }
118
119           if (text[j] == brace_arg_separator)
120             {
121               strvec_dispose (result);
122               report_error ("missing `}'");
123               throw_to_top_level ();
124             }
125           ADVANCE_CHAR (text, tlen, j);
126         }
127 #endif
128       free (preamble);          /* Same as result[0]; see initialization. */
129       result[0] = savestring (text);
130       return (result);
131     }
132
133 #if defined (SHELL)
134   amble = substring (text, start, i);
135   alen = i - start;
136 #else
137   amble = (char *)xmalloc (1 + (i - start));
138   strncpy (amble, &text[start], (i - start));
139   alen = i - start;
140   amble[alen] = '\0';
141 #endif
142
143 #if defined (SHELL)
144   INITIALIZE_MBSTATE;
145
146   /* If the amble does not contain an unquoted BRACE_ARG_SEPARATOR, then
147      just return without doing any expansion.  */
148   j = 0;
149   while (amble[j])
150     {
151       if (amble[j] == '\\')
152         {
153           j++;
154           ADVANCE_CHAR (amble, alen, j);
155           continue;
156         }
157
158       if (amble[j] == brace_arg_separator)
159         break;
160
161       ADVANCE_CHAR (amble, alen, j);
162     }
163
164   if (!amble[j])
165     {
166       free (amble);
167       free (preamble);
168       result[0] = savestring (text);
169       return (result);
170     }
171 #endif /* SHELL */
172
173   postamble = &text[i + 1];
174
175   tack = expand_amble (amble, alen);
176   result = array_concat (result, tack);
177   free (amble);
178   strvec_dispose (tack);
179
180   tack = brace_expand (postamble);
181   result = array_concat (result, tack);
182   strvec_dispose (tack);
183
184   return (result);
185 }
186
187 /* Expand the text found inside of braces.  We simply try to split the
188    text at BRACE_ARG_SEPARATORs into separate strings.  We then brace
189    expand each slot which needs it, until there are no more slots which
190    need it. */
191 static char **
192 expand_amble (text, tlen)
193      char *text;
194      size_t tlen;
195 {
196   char **result, **partial;
197   char *tem;
198   int start, i, c;
199
200   DECLARE_MBSTATE;
201
202   result = (char **)NULL;
203
204   start = i = 0;
205   c = 1;
206   while (c)
207     {
208       c = brace_gobbler (text, tlen, &i, brace_arg_separator);
209 #if defined (SHELL)
210       tem = substring (text, start, i);
211 #else
212       tem = (char *)xmalloc (1 + (i - start));
213       strncpy (tem, &text[start], (i - start));
214       tem[i- start] = '\0';
215 #endif
216
217       partial = brace_expand (tem);
218
219       if (!result)
220         result = partial;
221       else
222         {
223           register int lr = strvec_len (result);
224           register int lp = strvec_len (partial);
225           register int j;
226
227           result = strvec_resize (result, lp + lr + 1);
228
229           for (j = 0; j < lp; j++)
230             result[lr + j] = partial[j];
231
232           result[lr + j] = (char *)NULL;
233           free (partial);
234         }
235       free (tem);
236       ADVANCE_CHAR (text, tlen, i);
237       start = i;
238     }
239   return (result);
240 }
241
242 /* Start at INDEX, and skip characters in TEXT. Set INDEX to the
243    index of the character matching SATISFY.  This understands about
244    quoting.  Return the character that caused us to stop searching;
245    this is either the same as SATISFY, or 0. */
246 static int
247 brace_gobbler (text, tlen, indx, satisfy)
248      char *text;
249      size_t tlen;
250      int *indx;
251      int satisfy;
252 {
253   register int i, c, quoted, level, pass_next;
254 #if defined (SHELL)
255   int si;
256   char *t;
257 #endif
258   DECLARE_MBSTATE;
259
260   level = quoted = pass_next = 0;
261
262   i = *indx;
263   while (c = text[i])
264     {
265       if (pass_next)
266         {
267           pass_next = 0;
268           ADVANCE_CHAR (text, tlen, i);
269           continue;
270         }
271
272       /* A backslash escapes the next character.  This allows backslash to
273          escape the quote character in a double-quoted string. */
274       if (c == '\\' && (quoted == 0 || quoted == '"' || quoted == '`'))
275         {
276           pass_next = 1;
277           i++;
278           continue;
279         }
280
281       if (quoted)
282         {
283           if (c == quoted)
284             quoted = 0;
285           ADVANCE_CHAR (text, tlen, i);
286           continue;
287         }
288
289       if (c == '"' || c == '\'' || c == '`')
290         {
291           quoted = c;
292           i++;
293           continue;
294         }
295
296 #if defined (SHELL)
297       /* Pass new-style command substitutions through unchanged. */
298       if (c == '$' && text[i+1] == '(')                 /* ) */
299         {
300           si = i + 2;
301           t = extract_command_subst (text, &si);
302           i = si;
303           free (t);
304           i++;
305           continue;
306         }
307 #endif
308
309       if (c == satisfy && level == 0 && quoted == 0)
310         {
311           /* We ignore an open brace surrounded by whitespace, and also
312              an open brace followed immediately by a close brace preceded
313              by whitespace.  */
314           if (c == '{' &&
315               ((!i || brace_whitespace (text[i - 1])) &&
316                (brace_whitespace (text[i + 1]) || text[i + 1] == '}')))
317             {
318               i++;
319               continue;
320             }
321 #if defined (SHELL)
322           /* If this is being compiled as part of bash, ignore the `{'
323              in a `${}' construct */
324           if ((c != '{') || i == 0 || (text[i - 1] != '$'))
325 #endif /* SHELL */
326             break;
327         }
328
329       if (c == '{')
330         level++;
331       else if (c == '}' && level)
332         level--;
333
334       ADVANCE_CHAR (text, tlen, i);
335     }
336
337   *indx = i;
338   return (c);
339 }
340
341 /* Return a new array of strings which is the result of appending each
342    string in ARR2 to each string in ARR1.  The resultant array is
343    len (arr1) * len (arr2) long.  For convenience, ARR1 (and its contents)
344    are free ()'ed.  ARR1 can be NULL, in that case, a new version of ARR2
345    is returned. */
346 static char **
347 array_concat (arr1, arr2)
348      char **arr1, **arr2;
349 {
350   register int i, j, len, len1, len2;
351   register char **result;
352
353   if (arr1 == 0)
354     return (strvec_copy (arr2));
355
356   if (arr2 == 0)
357     return (strvec_copy (arr1));
358
359   len1 = strvec_len (arr1);
360   len2 = strvec_len (arr2);
361
362   result = (char **)xmalloc ((1 + (len1 * len2)) * sizeof (char *));
363
364   len = 0;
365   for (i = 0; i < len1; i++)
366     {
367       int strlen_1 = strlen (arr1[i]);
368
369       for (j = 0; j < len2; j++)
370         {
371           result[len] =
372             (char *)xmalloc (1 + strlen_1 + strlen (arr2[j]));
373           strcpy (result[len], arr1[i]);
374           strcpy (result[len] + strlen_1, arr2[j]);
375           len++;
376         }
377       free (arr1[i]);
378     }
379   free (arr1);
380
381   result[len] = (char *)NULL;
382   return (result);
383 }
384
385 #if defined (TEST)
386 #include <stdio.h>
387
388 fatal_error (format, arg1, arg2)
389      char *format, *arg1, *arg2;
390 {
391   report_error (format, arg1, arg2);
392   exit (1);
393 }
394
395 report_error (format, arg1, arg2)
396      char *format, *arg1, *arg2;
397 {
398   fprintf (stderr, format, arg1, arg2);
399   fprintf (stderr, "\n");
400 }
401
402 main ()
403 {
404   char example[256];
405
406   for (;;)
407     {
408       char **result;
409       int i;
410
411       fprintf (stderr, "brace_expand> ");
412
413       if ((!fgets (example, 256, stdin)) ||
414           (strncmp (example, "quit", 4) == 0))
415         break;
416
417       if (strlen (example))
418         example[strlen (example) - 1] = '\0';
419
420       result = brace_expand (example);
421
422       for (i = 0; result[i]; i++)
423         printf ("%s\n", result[i]);
424
425       free_array (result);
426     }
427 }
428 \f
429 /*
430  * Local variables:
431  * compile-command: "gcc -g -Bstatic -DTEST -o brace_expand braces.c general.o"
432  * end:
433  */
434
435 #endif /* TEST */
436 #endif /* BRACE_EXPANSION */