Imported from ../bash-2.01.1.tar.gz.
[platform/upstream/bash.git] / braces.c
1 /* braces.c -- code for doing word expansion in curly braces. */
2
3 /* Copyright (C) 1987,1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, 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 #  include <unistd.h>
29 #endif
30
31 #include "bashansi.h"
32
33 #if defined (SHELL)
34 #  include "shell.h"
35 #endif /* SHELL */
36
37 #include "general.h"
38 #define brace_whitespace(c) (!(c) || (c) == ' ' || (c) == '\t' || (c) == '\n')
39
40 #if defined (SHELL)
41 extern char *extract_command_subst ();
42 #endif
43
44 /* Basic idea:
45
46    Segregate the text into 3 sections: preamble (stuff before an open brace),
47    postamble (stuff after the matching close brace) and amble (stuff after
48    preamble, and before postamble).  Expand amble, and then tack on the
49    expansions to preamble.  Expand postamble, and tack on the expansions to
50    the result so far.
51  */
52
53 /* The character which is used to separate arguments. */
54 int brace_arg_separator = ',';
55
56 static int brace_gobbler ();
57 static char **expand_amble (), **array_concat ();
58
59 /* Return an array of strings; the brace expansion of TEXT. */
60 char **
61 brace_expand (text)
62      char *text;
63 {
64   register int start;
65   char *preamble, *postamble, *amble;
66   char **tack, **result;
67   int i, j, c;
68
69   /* Find the text of the preamble. */
70   i = 0;
71   c = brace_gobbler (text, &i, '{');
72
73   preamble = (char *)xmalloc (i + 1);
74   strncpy (preamble, text, i);
75   preamble[i] = '\0';
76
77   result = (char **)xmalloc (2 * sizeof (char *));
78   result[0] = preamble;
79   result[1] = (char *)NULL;
80
81   /* Special case.  If we never found an exciting character, then
82      the preamble is all of the text, so just return that. */
83   if (c != '{')
84     return (result);
85
86   /* Find the amble.  This is the stuff inside this set of braces. */
87   start = ++i;
88   c = brace_gobbler (text, &i, '}');
89
90   /* What if there isn't a matching close brace? */
91   if (c == 0)
92     {
93 #if defined (NOTDEF)
94       /* Well, if we found an unquoted BRACE_ARG_SEPARATOR between START
95          and I, then this should be an error.  Otherwise, it isn't. */
96       for (j = start; j < i; j++)
97         {
98           if (text[j] == '\\')
99             {
100               j++;
101               continue;
102             }
103
104           if (text[j] == brace_arg_separator)
105             {
106               free_array (result);
107               report_error ("missing `}'");
108               throw_to_top_level ();
109             }
110         }
111 #endif
112       free (preamble);          /* Same as result[0]; see initialization. */
113       result[0] = savestring (text);
114       return (result);
115     }
116
117   amble = (char *)xmalloc (1 + (i - start));
118   strncpy (amble, &text[start], (i - start));
119   amble[i - start] = '\0';
120
121 #if defined (SHELL)
122   /* If the amble does not contain an unquoted BRACE_ARG_SEPARATOR, then
123      just return without doing any expansion.  */
124   for (j = 0; amble[j]; j++)
125     {
126       if (amble[j] == '\\')
127         {
128           j++;
129           continue;
130         }
131       if (amble[j] == brace_arg_separator)
132         break;
133     }
134
135   if (!amble[j])
136     {
137       free (amble);
138       free (preamble);
139       result[0] = savestring (text);
140       return (result);
141     }
142 #endif /* SHELL */
143
144   postamble = &text[i + 1];
145
146   tack = expand_amble (amble);
147   result = array_concat (result, tack);
148   free (amble);
149   free_array (tack);
150
151   tack = brace_expand (postamble);
152   result = array_concat (result, tack);
153   free_array (tack);
154
155   return (result);
156 }
157
158 /* Expand the text found inside of braces.  We simply try to split the
159    text at BRACE_ARG_SEPARATORs into separate strings.  We then brace
160    expand each slot which needs it, until there are no more slots which
161    need it. */
162 static char **
163 expand_amble (text)
164      char *text;
165 {
166   char **result, **partial;
167   char *tem;
168   int start, i, c;
169
170   result = (char **)NULL;
171
172   for (start = 0, i = 0, c = 1; c; start = ++i)
173     {
174       c = brace_gobbler (text, &i, brace_arg_separator);
175       tem = (char *)xmalloc (1 + (i - start));
176       strncpy (tem, &text[start], (i - start));
177       tem[i- start] = '\0';
178
179       partial = brace_expand (tem);
180
181       if (!result)
182         result = partial;
183       else
184         {
185           register int lr = array_len (result);
186           register int lp = array_len (partial);
187           register int j;
188
189           result = (char **)xrealloc (result, (1 + lp + lr) * sizeof (char *));
190
191           for (j = 0; j < lp; j++)
192             result[lr + j] = partial[j];
193
194           result[lr + j] = (char *)NULL;
195           free (partial);
196         }
197       free (tem);
198     }
199   return (result);
200 }
201
202 /* Start at INDEX, and skip characters in TEXT. Set INDEX to the
203    index of the character matching SATISFY.  This understands about
204    quoting.  Return the character that caused us to stop searching;
205    this is either the same as SATISFY, or 0. */
206 static int
207 brace_gobbler (text, indx, satisfy)
208      char *text;
209      int *indx;
210      int satisfy;
211 {
212   register int i, c, quoted, level, pass_next;
213 #if defined (SHELL)
214   int si;
215   char *t;
216 #endif
217
218   level = quoted = pass_next = 0;
219
220   for (i = *indx; c = text[i]; i++)
221     {
222       if (pass_next)
223         {
224           pass_next = 0;
225           continue;
226         }
227
228       /* A backslash escapes the next character.  This allows backslash to
229          escape the quote character in a double-quoted string. */
230       if (c == '\\' && (quoted == 0 || quoted == '"' || quoted == '`'))
231         {
232           pass_next = 1;
233           continue;
234         }
235
236       if (quoted)
237         {
238           if (c == quoted)
239             quoted = 0;
240           continue;
241         }
242
243       if (c == '"' || c == '\'' || c == '`')
244         {
245           quoted = c;
246           continue;
247         }
248
249 #if defined (SHELL)
250       /* Pass new-style command substitutions through unchanged. */
251       if (c == '$' && text[i+1] == '(')                 /* ) */
252         {
253           si = i + 2;
254           t = extract_command_subst (text, &si);
255           i = si;
256           free (t);
257           continue;
258         }
259 #endif
260
261       if (c == satisfy && level == 0 && quoted == 0)
262         {
263           /* We ignore an open brace surrounded by whitespace, and also
264              an open brace followed immediately by a close brace preceded
265              by whitespace.  */
266           if (c == '{' &&
267               ((!i || brace_whitespace (text[i - 1])) &&
268                (brace_whitespace (text[i + 1]) || text[i + 1] == '}')))
269             continue;
270 #if defined (SHELL)
271           /* If this is being compiled as part of bash, ignore the `{'
272              in a `${}' construct */
273           if ((c != '{') || i == 0 || (text[i - 1] != '$'))
274 #endif /* SHELL */
275             break;
276         }
277
278       if (c == '{')
279         level++;
280       else if (c == '}' && level)
281         level--;
282     }
283
284   *indx = i;
285   return (c);
286 }
287
288 /* Return a new array of strings which is the result of appending each
289    string in ARR2 to each string in ARR1.  The resultant array is
290    len (arr1) * len (arr2) long.  For convenience, ARR1 (and its contents)
291    are free ()'ed.  ARR1 can be NULL, in that case, a new version of ARR2
292    is returned. */
293 static char **
294 array_concat (arr1, arr2)
295      char **arr1, **arr2;
296 {
297   register int i, j, len, len1, len2;
298   register char **result;
299
300   if (arr1 == 0)
301     return (copy_array (arr2));
302
303   if (arr2 == 0)
304     return (copy_array (arr1));
305
306   len1 = array_len (arr1);
307   len2 = array_len (arr2);
308
309   result = (char **)xmalloc ((1 + (len1 * len2)) * sizeof (char *));
310
311   len = 0;
312   for (i = 0; i < len1; i++)
313     {
314       int strlen_1 = strlen (arr1[i]);
315
316       for (j = 0; j < len2; j++)
317         {
318           result[len] =
319             (char *)xmalloc (1 + strlen_1 + strlen (arr2[j]));
320           strcpy (result[len], arr1[i]);
321           strcpy (result[len] + strlen_1, arr2[j]);
322           len++;
323         }
324       free (arr1[i]);
325     }
326   free (arr1);
327
328   result[len] = (char *)NULL;
329   return (result);
330 }
331
332 #if defined (TEST)
333 #include <stdio.h>
334
335 fatal_error (format, arg1, arg2)
336      char *format, *arg1, *arg2;
337 {
338   report_error (format, arg1, arg2);
339   exit (1);
340 }
341
342 report_error (format, arg1, arg2)
343      char *format, *arg1, *arg2;
344 {
345   fprintf (stderr, format, arg1, arg2);
346   fprintf (stderr, "\n");
347 }
348
349 main ()
350 {
351   char example[256];
352
353   for (;;)
354     {
355       char **result;
356       int i;
357
358       fprintf (stderr, "brace_expand> ");
359
360       if ((!fgets (example, 256, stdin)) ||
361           (strncmp (example, "quit", 4) == 0))
362         break;
363
364       if (strlen (example))
365         example[strlen (example) - 1] = '\0';
366
367       result = brace_expand (example);
368
369       for (i = 0; result[i]; i++)
370         printf ("%s\n", result[i]);
371
372       free_array (result);
373     }
374 }
375 \f
376 /*
377  * Local variables:
378  * compile-command: "gcc -g -Bstatic -DTEST -o brace_expand braces.c general.o"
379  * end:
380  */
381
382 #endif /* TEST */
383 #endif /* BRACE_EXPANSION */