]>
Commit | Line | Data |
---|---|---|
726f6388 JA |
1 | /* braces.c -- code for doing word expansion in curly braces. */ |
2 | ||
8868edaf | 3 | /* Copyright (C) 1987-2020 Free Software Foundation, Inc. |
726f6388 JA |
4 | |
5 | This file is part of GNU Bash, the Bourne Again SHell. | |
6 | ||
3185942a JA |
7 | Bash is free software: you can redistribute it and/or modify |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation, either version 3 of the License, or | |
10 | (at your option) any later version. | |
726f6388 | 11 | |
3185942a JA |
12 | Bash 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 | |
15 | GNU General Public License for more details. | |
726f6388 JA |
16 | |
17 | You should have received a copy of the GNU General Public License | |
3185942a JA |
18 | along with Bash. If not, see <http://www.gnu.org/licenses/>. |
19 | */ | |
726f6388 | 20 | |
ccc6cda3 | 21 | /* Stuff in curly braces gets expanded before all other shell expansions. */ |
726f6388 | 22 | |
ccc6cda3 JA |
23 | #include "config.h" |
24 | ||
25 | #if defined (BRACE_EXPANSION) | |
26 | ||
27 | #if defined (HAVE_UNISTD_H) | |
cce855bc JA |
28 | # ifdef _MINIX |
29 | # include <sys/types.h> | |
30 | # endif | |
ccc6cda3 JA |
31 | # include <unistd.h> |
32 | #endif | |
726f6388 | 33 | |
ac50fbac CR |
34 | #include <errno.h> |
35 | ||
d166f048 | 36 | #include "bashansi.h" |
ac50fbac | 37 | #include "bashintl.h" |
726f6388 JA |
38 | |
39 | #if defined (SHELL) | |
ccc6cda3 | 40 | # include "shell.h" |
a0c0a00f CR |
41 | #else |
42 | # if defined (TEST) | |
43 | typedef char *WORD_DESC; | |
44 | typedef char **WORD_LIST; | |
45 | #define _(X) X | |
46 | # endif /* TEST */ | |
726f6388 JA |
47 | #endif /* SHELL */ |
48 | ||
ac50fbac | 49 | #include "typemax.h" /* INTMAX_MIN, INTMAX_MAX */ |
726f6388 | 50 | #include "general.h" |
7117c2d2 | 51 | #include "shmbutil.h" |
b80f6443 | 52 | #include "chartypes.h" |
7117c2d2 | 53 | |
ac50fbac CR |
54 | #ifndef errno |
55 | extern int errno; | |
56 | #endif | |
57 | ||
726f6388 JA |
58 | #define brace_whitespace(c) (!(c) || (c) == ' ' || (c) == '\t' || (c) == '\n') |
59 | ||
b80f6443 JA |
60 | #define BRACE_SEQ_SPECIFIER ".." |
61 | ||
8868edaf | 62 | extern int asprintf PARAMS((char **, const char *, ...)) __attribute__((__format__ (printf, 2, 3))); |
ac50fbac | 63 | |
726f6388 JA |
64 | /* Basic idea: |
65 | ||
66 | Segregate the text into 3 sections: preamble (stuff before an open brace), | |
67 | postamble (stuff after the matching close brace) and amble (stuff after | |
68 | preamble, and before postamble). Expand amble, and then tack on the | |
69 | expansions to preamble. Expand postamble, and tack on the expansions to | |
70 | the result so far. | |
71 | */ | |
72 | ||
73 | /* The character which is used to separate arguments. */ | |
3185942a | 74 | static const int brace_arg_separator = ','; |
726f6388 | 75 | |
8868edaf CR |
76 | #if defined (PARAMS) |
77 | static int brace_gobbler PARAMS((char *, size_t, int *, int)); | |
78 | static char **expand_amble PARAMS((char *, size_t, int)); | |
79 | static char **expand_seqterm PARAMS((char *, size_t)); | |
80 | static char **mkseq PARAMS((intmax_t, intmax_t, intmax_t, int, int)); | |
81 | static char **array_concat PARAMS((char **, char **)); | |
f73dda09 | 82 | #else |
726f6388 | 83 | static int brace_gobbler (); |
f73dda09 | 84 | static char **expand_amble (); |
b80f6443 JA |
85 | static char **expand_seqterm (); |
86 | static char **mkseq(); | |
f73dda09 JA |
87 | static char **array_concat (); |
88 | #endif | |
726f6388 | 89 | |
0628567a JA |
90 | #if 0 |
91 | static void | |
92 | dump_result (a) | |
93 | char **a; | |
94 | { | |
95 | int i; | |
96 | ||
97 | for (i = 0; a[i]; i++) | |
98 | printf ("dump_result: a[%d] = -%s-\n", i, a[i]); | |
99 | } | |
100 | #endif | |
101 | ||
726f6388 JA |
102 | /* Return an array of strings; the brace expansion of TEXT. */ |
103 | char ** | |
104 | brace_expand (text) | |
105 | char *text; | |
106 | { | |
107 | register int start; | |
7117c2d2 | 108 | size_t tlen; |
726f6388 | 109 | char *preamble, *postamble, *amble; |
7117c2d2 | 110 | size_t alen; |
726f6388 | 111 | char **tack, **result; |
0628567a | 112 | int i, j, c, c1; |
726f6388 | 113 | |
7117c2d2 JA |
114 | DECLARE_MBSTATE; |
115 | ||
726f6388 | 116 | /* Find the text of the preamble. */ |
7117c2d2 | 117 | tlen = strlen (text); |
726f6388 | 118 | i = 0; |
0628567a JA |
119 | #if defined (CSH_BRACE_COMPAT) |
120 | c = brace_gobbler (text, tlen, &i, '{'); /* } */ | |
121 | #else | |
122 | /* Make sure that when we exit this loop, c == 0 or text[i] begins a | |
123 | valid brace expansion sequence. */ | |
124 | do | |
125 | { | |
126 | c = brace_gobbler (text, tlen, &i, '{'); /* } */ | |
127 | c1 = c; | |
128 | /* Verify that c begins a valid brace expansion word. If it doesn't, we | |
129 | go on. Loop stops when there are no more open braces in the word. */ | |
130 | if (c) | |
131 | { | |
132 | start = j = i + 1; /* { */ | |
133 | c = brace_gobbler (text, tlen, &j, '}'); | |
134 | if (c == 0) /* it's not */ | |
135 | { | |
136 | i++; | |
137 | c = c1; | |
138 | continue; | |
139 | } | |
140 | else /* it is */ | |
141 | { | |
142 | c = c1; | |
143 | break; | |
144 | } | |
145 | } | |
146 | else | |
147 | break; | |
148 | } | |
149 | while (c); | |
150 | #endif /* !CSH_BRACE_COMPAT */ | |
726f6388 JA |
151 | |
152 | preamble = (char *)xmalloc (i + 1); | |
ac50fbac CR |
153 | if (i > 0) |
154 | strncpy (preamble, text, i); | |
726f6388 JA |
155 | preamble[i] = '\0'; |
156 | ||
157 | result = (char **)xmalloc (2 * sizeof (char *)); | |
158 | result[0] = preamble; | |
159 | result[1] = (char *)NULL; | |
ccc6cda3 | 160 | |
726f6388 JA |
161 | /* Special case. If we never found an exciting character, then |
162 | the preamble is all of the text, so just return that. */ | |
163 | if (c != '{') | |
164 | return (result); | |
165 | ||
166 | /* Find the amble. This is the stuff inside this set of braces. */ | |
167 | start = ++i; | |
7117c2d2 | 168 | c = brace_gobbler (text, tlen, &i, '}'); |
726f6388 JA |
169 | |
170 | /* What if there isn't a matching close brace? */ | |
ccc6cda3 | 171 | if (c == 0) |
726f6388 JA |
172 | { |
173 | #if defined (NOTDEF) | |
726f6388 JA |
174 | /* Well, if we found an unquoted BRACE_ARG_SEPARATOR between START |
175 | and I, then this should be an error. Otherwise, it isn't. */ | |
7117c2d2 JA |
176 | j = start; |
177 | while (j < i) | |
726f6388 JA |
178 | { |
179 | if (text[j] == '\\') | |
180 | { | |
181 | j++; | |
7117c2d2 | 182 | ADVANCE_CHAR (text, tlen, j); |
726f6388 JA |
183 | continue; |
184 | } | |
185 | ||
186 | if (text[j] == brace_arg_separator) | |
b80f6443 | 187 | { /* { */ |
7117c2d2 | 188 | strvec_dispose (result); |
8868edaf | 189 | set_exit_status (EXECUTION_FAILURE); |
b80f6443 | 190 | report_error ("no closing `%c' in %s", '}', text); |
726f6388 JA |
191 | throw_to_top_level (); |
192 | } | |
7117c2d2 | 193 | ADVANCE_CHAR (text, tlen, j); |
726f6388 JA |
194 | } |
195 | #endif | |
196 | free (preamble); /* Same as result[0]; see initialization. */ | |
197 | result[0] = savestring (text); | |
198 | return (result); | |
199 | } | |
200 | ||
bb70624e JA |
201 | #if defined (SHELL) |
202 | amble = substring (text, start, i); | |
7117c2d2 | 203 | alen = i - start; |
bb70624e | 204 | #else |
726f6388 JA |
205 | amble = (char *)xmalloc (1 + (i - start)); |
206 | strncpy (amble, &text[start], (i - start)); | |
7117c2d2 JA |
207 | alen = i - start; |
208 | amble[alen] = '\0'; | |
bb70624e | 209 | #endif |
726f6388 JA |
210 | |
211 | #if defined (SHELL) | |
7117c2d2 JA |
212 | INITIALIZE_MBSTATE; |
213 | ||
726f6388 JA |
214 | /* If the amble does not contain an unquoted BRACE_ARG_SEPARATOR, then |
215 | just return without doing any expansion. */ | |
7117c2d2 JA |
216 | j = 0; |
217 | while (amble[j]) | |
ccc6cda3 JA |
218 | { |
219 | if (amble[j] == '\\') | |
220 | { | |
221 | j++; | |
7117c2d2 | 222 | ADVANCE_CHAR (amble, alen, j); |
ccc6cda3 JA |
223 | continue; |
224 | } | |
7117c2d2 | 225 | |
ccc6cda3 JA |
226 | if (amble[j] == brace_arg_separator) |
227 | break; | |
7117c2d2 JA |
228 | |
229 | ADVANCE_CHAR (amble, alen, j); | |
ccc6cda3 JA |
230 | } |
231 | ||
b80f6443 | 232 | if (amble[j] == 0) |
ccc6cda3 | 233 | { |
b80f6443 JA |
234 | tack = expand_seqterm (amble, alen); |
235 | if (tack) | |
236 | goto add_tack; | |
ac50fbac CR |
237 | else if (text[i + 1]) |
238 | { | |
239 | /* If the sequence expansion fails (e.g., because the integers | |
240 | overflow), but there is more in the string, try and process | |
241 | the rest of the string, which may contain additional brace | |
242 | expansions. Treat the unexpanded sequence term as a simple | |
243 | string (including the braces). */ | |
244 | tack = strvec_create (2); | |
245 | tack[0] = savestring (text+start-1); | |
246 | tack[0][i-start+2] = '\0'; | |
247 | tack[1] = (char *)0; | |
248 | goto add_tack; | |
249 | } | |
b80f6443 JA |
250 | else |
251 | { | |
252 | free (amble); | |
253 | free (preamble); | |
254 | result[0] = savestring (text); | |
255 | return (result); | |
256 | } | |
ccc6cda3 | 257 | } |
726f6388 JA |
258 | #endif /* SHELL */ |
259 | ||
b80f6443 JA |
260 | tack = expand_amble (amble, alen, 0); |
261 | add_tack: | |
726f6388 JA |
262 | result = array_concat (result, tack); |
263 | free (amble); | |
ac50fbac CR |
264 | if (tack != result) |
265 | strvec_dispose (tack); | |
726f6388 | 266 | |
b80f6443 JA |
267 | postamble = text + i + 1; |
268 | ||
ac50fbac CR |
269 | if (postamble && *postamble) |
270 | { | |
271 | tack = brace_expand (postamble); | |
272 | result = array_concat (result, tack); | |
273 | if (tack != result) | |
274 | strvec_dispose (tack); | |
275 | } | |
726f6388 JA |
276 | |
277 | return (result); | |
278 | } | |
279 | ||
280 | /* Expand the text found inside of braces. We simply try to split the | |
281 | text at BRACE_ARG_SEPARATORs into separate strings. We then brace | |
282 | expand each slot which needs it, until there are no more slots which | |
283 | need it. */ | |
284 | static char ** | |
b80f6443 | 285 | expand_amble (text, tlen, flags) |
726f6388 | 286 | char *text; |
7117c2d2 | 287 | size_t tlen; |
b80f6443 | 288 | int flags; |
726f6388 | 289 | { |
ac50fbac | 290 | char **result, **partial, **tresult; |
726f6388 JA |
291 | char *tem; |
292 | int start, i, c; | |
293 | ||
a0c0a00f | 294 | #if defined (SHELL) |
7117c2d2 | 295 | DECLARE_MBSTATE; |
a0c0a00f | 296 | #endif |
7117c2d2 | 297 | |
726f6388 JA |
298 | result = (char **)NULL; |
299 | ||
7117c2d2 JA |
300 | start = i = 0; |
301 | c = 1; | |
302 | while (c) | |
726f6388 | 303 | { |
7117c2d2 | 304 | c = brace_gobbler (text, tlen, &i, brace_arg_separator); |
bb70624e JA |
305 | #if defined (SHELL) |
306 | tem = substring (text, start, i); | |
307 | #else | |
726f6388 JA |
308 | tem = (char *)xmalloc (1 + (i - start)); |
309 | strncpy (tem, &text[start], (i - start)); | |
a0c0a00f | 310 | tem[i - start] = '\0'; |
bb70624e | 311 | #endif |
726f6388 JA |
312 | |
313 | partial = brace_expand (tem); | |
314 | ||
315 | if (!result) | |
316 | result = partial; | |
317 | else | |
318 | { | |
b80f6443 JA |
319 | register int lr, lp, j; |
320 | ||
321 | lr = strvec_len (result); | |
322 | lp = strvec_len (partial); | |
726f6388 | 323 | |
ac50fbac CR |
324 | tresult = strvec_mresize (result, lp + lr + 1); |
325 | if (tresult == 0) | |
326 | { | |
327 | internal_error (_("brace expansion: cannot allocate memory for %s"), tem); | |
a0c0a00f CR |
328 | free (tem); |
329 | strvec_dispose (partial); | |
ac50fbac CR |
330 | strvec_dispose (result); |
331 | result = (char **)NULL; | |
332 | return result; | |
333 | } | |
334 | else | |
335 | result = tresult; | |
726f6388 JA |
336 | |
337 | for (j = 0; j < lp; j++) | |
338 | result[lr + j] = partial[j]; | |
339 | ||
340 | result[lr + j] = (char *)NULL; | |
341 | free (partial); | |
342 | } | |
343 | free (tem); | |
a0c0a00f | 344 | #if defined (SHELL) |
7117c2d2 | 345 | ADVANCE_CHAR (text, tlen, i); |
a0c0a00f CR |
346 | #else |
347 | i++; | |
348 | #endif | |
7117c2d2 | 349 | start = i; |
726f6388 JA |
350 | } |
351 | return (result); | |
352 | } | |
353 | ||
b80f6443 JA |
354 | #define ST_BAD 0 |
355 | #define ST_INT 1 | |
356 | #define ST_CHAR 2 | |
3185942a | 357 | #define ST_ZINT 3 |
b80f6443 JA |
358 | |
359 | static char ** | |
3185942a | 360 | mkseq (start, end, incr, type, width) |
ac50fbac CR |
361 | intmax_t start, end, incr; |
362 | int type, width; | |
b80f6443 | 363 | { |
ac50fbac | 364 | intmax_t n, prevn; |
d233b485 | 365 | int i, nelem; |
b80f6443 JA |
366 | char **result, *t; |
367 | ||
0628567a JA |
368 | if (incr == 0) |
369 | incr = 1; | |
ac50fbac | 370 | |
0628567a JA |
371 | if (start > end && incr > 0) |
372 | incr = -incr; | |
373 | else if (start < end && incr < 0) | |
ac50fbac CR |
374 | { |
375 | if (incr == INTMAX_MIN) /* Don't use -INTMAX_MIN */ | |
376 | return ((char **)NULL); | |
377 | incr = -incr; | |
378 | } | |
379 | ||
380 | /* Check that end-start will not overflow INTMAX_MIN, INTMAX_MAX. The +3 | |
381 | and -2, not strictly necessary, are there because of the way the number | |
382 | of elements and value passed to strvec_create() are calculated below. */ | |
383 | if (SUBOVERFLOW (end, start, INTMAX_MIN+3, INTMAX_MAX-2)) | |
384 | return ((char **)NULL); | |
385 | ||
386 | prevn = sh_imaxabs (end - start); | |
387 | /* Need to check this way in case INT_MAX == INTMAX_MAX */ | |
388 | if (INT_MAX == INTMAX_MAX && (ADDOVERFLOW (prevn, 2, INT_MIN, INT_MAX))) | |
389 | return ((char **)NULL); | |
390 | /* Make sure the assignment to nelem below doesn't end up <= 0 due to | |
391 | intmax_t overflow */ | |
392 | else if (ADDOVERFLOW ((prevn/sh_imaxabs(incr)), 1, INTMAX_MIN, INTMAX_MAX)) | |
393 | return ((char **)NULL); | |
394 | ||
395 | /* XXX - TOFIX: potentially allocating a lot of extra memory if | |
396 | imaxabs(incr) != 1 */ | |
397 | /* Instead of a simple nelem = prevn + 1, something like: | |
398 | nelem = (prevn / imaxabs(incr)) + 1; | |
399 | would work */ | |
d233b485 | 400 | if ((prevn / sh_imaxabs (incr)) > INT_MAX - 3) /* check int overflow */ |
ac50fbac | 401 | return ((char **)NULL); |
d233b485 | 402 | nelem = (prevn / sh_imaxabs(incr)) + 1; |
ac50fbac CR |
403 | result = strvec_mcreate (nelem + 1); |
404 | if (result == 0) | |
405 | { | |
d233b485 | 406 | internal_error (_("brace expansion: failed to allocate memory for %u elements"), (unsigned int)nelem); |
ac50fbac CR |
407 | return ((char **)NULL); |
408 | } | |
b80f6443 JA |
409 | |
410 | /* Make sure we go through the loop at least once, so {3..3} prints `3' */ | |
411 | i = 0; | |
412 | n = start; | |
413 | do | |
414 | { | |
0628567a | 415 | #if defined (SHELL) |
a0c0a00f CR |
416 | if (ISINTERRUPT) |
417 | { | |
d233b485 | 418 | result[i] = (char *)NULL; |
a0c0a00f CR |
419 | strvec_dispose (result); |
420 | result = (char **)NULL; | |
421 | } | |
422 | QUIT; | |
0628567a | 423 | #endif |
b80f6443 | 424 | if (type == ST_INT) |
ac50fbac | 425 | result[i++] = t = itos (n); |
3185942a JA |
426 | else if (type == ST_ZINT) |
427 | { | |
495aee44 CR |
428 | int len, arg; |
429 | arg = n; | |
430 | len = asprintf (&t, "%0*d", width, arg); | |
3185942a JA |
431 | result[i++] = t; |
432 | } | |
b80f6443 JA |
433 | else |
434 | { | |
ac50fbac CR |
435 | if (t = (char *)malloc (2)) |
436 | { | |
437 | t[0] = n; | |
438 | t[1] = '\0'; | |
439 | } | |
b80f6443 JA |
440 | result[i++] = t; |
441 | } | |
ac50fbac CR |
442 | |
443 | /* We failed to allocate memory for this number, so we bail. */ | |
444 | if (t == 0) | |
445 | { | |
446 | char *p, lbuf[INT_STRLEN_BOUND(intmax_t) + 1]; | |
447 | ||
448 | /* Easier to do this than mess around with various intmax_t printf | |
449 | formats (%ld? %lld? %jd?) and PRIdMAX. */ | |
450 | p = inttostr (n, lbuf, sizeof (lbuf)); | |
451 | internal_error (_("brace expansion: failed to allocate memory for `%s'"), p); | |
452 | strvec_dispose (result); | |
453 | return ((char **)NULL); | |
454 | } | |
455 | ||
456 | /* Handle overflow and underflow of n+incr */ | |
457 | if (ADDOVERFLOW (n, incr, INTMAX_MIN, INTMAX_MAX)) | |
458 | break; | |
459 | ||
b80f6443 | 460 | n += incr; |
ac50fbac | 461 | |
3185942a JA |
462 | if ((incr < 0 && n < end) || (incr > 0 && n > end)) |
463 | break; | |
b80f6443 JA |
464 | } |
465 | while (1); | |
466 | ||
467 | result[i] = (char *)0; | |
468 | return (result); | |
469 | } | |
470 | ||
471 | static char ** | |
472 | expand_seqterm (text, tlen) | |
473 | char *text; | |
474 | size_t tlen; | |
475 | { | |
476 | char *t, *lhs, *rhs; | |
d233b485 | 477 | int lhs_t, rhs_t, lhs_l, rhs_l, width; |
ac50fbac | 478 | intmax_t lhs_v, rhs_v, incr; |
b80f6443 | 479 | intmax_t tl, tr; |
0001803f | 480 | char **result, *ep, *oep; |
b80f6443 JA |
481 | |
482 | t = strstr (text, BRACE_SEQ_SPECIFIER); | |
483 | if (t == 0) | |
484 | return ((char **)NULL); | |
485 | ||
3185942a JA |
486 | lhs_l = t - text; /* index of start of BRACE_SEQ_SPECIFIER */ |
487 | lhs = substring (text, 0, lhs_l); | |
488 | rhs = substring (text, lhs_l + sizeof(BRACE_SEQ_SPECIFIER) - 1, tlen); | |
b80f6443 JA |
489 | |
490 | if (lhs[0] == 0 || rhs[0] == 0) | |
491 | { | |
492 | free (lhs); | |
493 | free (rhs); | |
494 | return ((char **)NULL); | |
495 | } | |
496 | ||
497 | /* Now figure out whether LHS and RHS are integers or letters. Both | |
498 | sides have to match. */ | |
499 | lhs_t = (legal_number (lhs, &tl)) ? ST_INT : | |
500 | ((ISALPHA (lhs[0]) && lhs[1] == 0) ? ST_CHAR : ST_BAD); | |
3185942a JA |
501 | |
502 | /* Decide on rhs and whether or not it looks like the user specified | |
503 | an increment */ | |
504 | ep = 0; | |
505 | if (ISDIGIT (rhs[0]) || ((rhs[0] == '+' || rhs[0] == '-') && ISDIGIT (rhs[1]))) | |
506 | { | |
507 | rhs_t = ST_INT; | |
ac50fbac | 508 | errno = 0; |
3185942a | 509 | tr = strtoimax (rhs, &ep, 10); |
ac50fbac | 510 | if (errno == ERANGE || (ep && *ep != 0 && *ep != '.')) |
3185942a JA |
511 | rhs_t = ST_BAD; /* invalid */ |
512 | } | |
513 | else if (ISALPHA (rhs[0]) && (rhs[1] == 0 || rhs[1] == '.')) | |
514 | { | |
515 | rhs_t = ST_CHAR; | |
516 | ep = rhs + 1; | |
517 | } | |
518 | else | |
519 | { | |
520 | rhs_t = ST_BAD; | |
521 | ep = 0; | |
522 | } | |
523 | ||
524 | incr = 1; | |
525 | if (rhs_t != ST_BAD) | |
526 | { | |
0001803f | 527 | oep = ep; |
ac50fbac | 528 | errno = 0; |
3185942a JA |
529 | if (ep && *ep == '.' && ep[1] == '.' && ep[2]) |
530 | incr = strtoimax (ep + 2, &ep, 10); | |
ac50fbac CR |
531 | if (*ep != 0 || errno == ERANGE) |
532 | rhs_t = ST_BAD; /* invalid incr or overflow */ | |
0001803f | 533 | tlen -= ep - oep; |
3185942a | 534 | } |
b80f6443 JA |
535 | |
536 | if (lhs_t != rhs_t || lhs_t == ST_BAD || rhs_t == ST_BAD) | |
537 | { | |
538 | free (lhs); | |
539 | free (rhs); | |
540 | return ((char **)NULL); | |
541 | } | |
542 | ||
543 | /* OK, we have something. It's either a sequence of integers, ascending | |
544 | or descending, or a sequence or letters, ditto. Generate the sequence, | |
545 | put it into a string vector, and return it. */ | |
546 | ||
547 | if (lhs_t == ST_CHAR) | |
548 | { | |
eb873671 JA |
549 | lhs_v = (unsigned char)lhs[0]; |
550 | rhs_v = (unsigned char)rhs[0]; | |
3185942a | 551 | width = 1; |
b80f6443 JA |
552 | } |
553 | else | |
554 | { | |
555 | lhs_v = tl; /* integer truncation */ | |
556 | rhs_v = tr; | |
3185942a JA |
557 | |
558 | /* Decide whether or not the terms need zero-padding */ | |
559 | rhs_l = tlen - lhs_l - sizeof (BRACE_SEQ_SPECIFIER) + 1; | |
560 | width = 0; | |
561 | if (lhs_l > 1 && lhs[0] == '0') | |
562 | width = lhs_l, lhs_t = ST_ZINT; | |
563 | if (lhs_l > 2 && lhs[0] == '-' && lhs[1] == '0') | |
564 | width = lhs_l, lhs_t = ST_ZINT; | |
565 | if (rhs_l > 1 && rhs[0] == '0' && width < rhs_l) | |
566 | width = rhs_l, lhs_t = ST_ZINT; | |
567 | if (rhs_l > 2 && rhs[0] == '-' && rhs[1] == '0' && width < rhs_l) | |
568 | width = rhs_l, lhs_t = ST_ZINT; | |
0001803f CR |
569 | |
570 | if (width < lhs_l && lhs_t == ST_ZINT) | |
571 | width = lhs_l; | |
572 | if (width < rhs_l && lhs_t == ST_ZINT) | |
573 | width = rhs_l; | |
b80f6443 JA |
574 | } |
575 | ||
3185942a | 576 | result = mkseq (lhs_v, rhs_v, incr, lhs_t, width); |
b80f6443 JA |
577 | |
578 | free (lhs); | |
579 | free (rhs); | |
580 | ||
581 | return (result); | |
582 | } | |
583 | ||
726f6388 JA |
584 | /* Start at INDEX, and skip characters in TEXT. Set INDEX to the |
585 | index of the character matching SATISFY. This understands about | |
586 | quoting. Return the character that caused us to stop searching; | |
587 | this is either the same as SATISFY, or 0. */ | |
0628567a JA |
588 | /* If SATISFY is `}', we are looking for a brace expression, so we |
589 | should enforce the rules that govern valid brace expansions: | |
590 | 1) to count as an arg separator, a comma or `..' has to be outside | |
591 | an inner set of braces. | |
592 | */ | |
726f6388 | 593 | static int |
7117c2d2 | 594 | brace_gobbler (text, tlen, indx, satisfy) |
726f6388 | 595 | char *text; |
7117c2d2 | 596 | size_t tlen; |
726f6388 JA |
597 | int *indx; |
598 | int satisfy; | |
599 | { | |
0628567a | 600 | register int i, c, quoted, level, commas, pass_next; |
d166f048 JA |
601 | #if defined (SHELL) |
602 | int si; | |
603 | char *t; | |
604 | #endif | |
7117c2d2 | 605 | DECLARE_MBSTATE; |
726f6388 JA |
606 | |
607 | level = quoted = pass_next = 0; | |
0628567a JA |
608 | #if defined (CSH_BRACE_COMPAT) |
609 | commas = 1; | |
610 | #else | |
611 | commas = (satisfy == '}') ? 0 : 1; | |
612 | #endif | |
726f6388 | 613 | |
7117c2d2 JA |
614 | i = *indx; |
615 | while (c = text[i]) | |
726f6388 JA |
616 | { |
617 | if (pass_next) | |
618 | { | |
619 | pass_next = 0; | |
a0c0a00f | 620 | #if defined (SHELL) |
7117c2d2 | 621 | ADVANCE_CHAR (text, tlen, i); |
a0c0a00f CR |
622 | #else |
623 | i++; | |
624 | #endif | |
726f6388 JA |
625 | continue; |
626 | } | |
627 | ||
628 | /* A backslash escapes the next character. This allows backslash to | |
629 | escape the quote character in a double-quoted string. */ | |
630 | if (c == '\\' && (quoted == 0 || quoted == '"' || quoted == '`')) | |
28ef6c31 JA |
631 | { |
632 | pass_next = 1; | |
7117c2d2 | 633 | i++; |
28ef6c31 JA |
634 | continue; |
635 | } | |
726f6388 | 636 | |
b80f6443 JA |
637 | #if defined (SHELL) |
638 | /* If compiling for the shell, treat ${...} like \{...} */ | |
639 | if (c == '$' && text[i+1] == '{' && quoted != '\'') /* } */ | |
640 | { | |
641 | pass_next = 1; | |
642 | i++; | |
eb873671 JA |
643 | if (quoted == 0) |
644 | level++; | |
b80f6443 JA |
645 | continue; |
646 | } | |
647 | #endif | |
648 | ||
726f6388 JA |
649 | if (quoted) |
650 | { | |
651 | if (c == quoted) | |
652 | quoted = 0; | |
ac50fbac CR |
653 | #if defined (SHELL) |
654 | /* The shell allows quoted command substitutions */ | |
655 | if (quoted == '"' && c == '$' && text[i+1] == '(') /*)*/ | |
656 | goto comsub; | |
657 | #endif | |
a0c0a00f | 658 | #if defined (SHELL) |
7117c2d2 | 659 | ADVANCE_CHAR (text, tlen, i); |
a0c0a00f CR |
660 | #else |
661 | i++; | |
662 | #endif | |
726f6388 JA |
663 | continue; |
664 | } | |
665 | ||
666 | if (c == '"' || c == '\'' || c == '`') | |
667 | { | |
668 | quoted = c; | |
7117c2d2 | 669 | i++; |
726f6388 JA |
670 | continue; |
671 | } | |
ccc6cda3 | 672 | |
d166f048 | 673 | #if defined (SHELL) |
3185942a JA |
674 | /* Pass new-style command and process substitutions through unchanged. */ |
675 | if ((c == '$' || c == '<' || c == '>') && text[i+1] == '(') /* ) */ | |
d166f048 | 676 | { |
ac50fbac | 677 | comsub: |
d166f048 | 678 | si = i + 2; |
3185942a | 679 | t = extract_command_subst (text, &si, 0); |
d166f048 JA |
680 | i = si; |
681 | free (t); | |
7117c2d2 | 682 | i++; |
d166f048 JA |
683 | continue; |
684 | } | |
685 | #endif | |
686 | ||
0628567a | 687 | if (c == satisfy && level == 0 && quoted == 0 && commas > 0) |
726f6388 JA |
688 | { |
689 | /* We ignore an open brace surrounded by whitespace, and also | |
ccc6cda3 JA |
690 | an open brace followed immediately by a close brace preceded |
691 | by whitespace. */ | |
726f6388 JA |
692 | if (c == '{' && |
693 | ((!i || brace_whitespace (text[i - 1])) && | |
694 | (brace_whitespace (text[i + 1]) || text[i + 1] == '}'))) | |
7117c2d2 JA |
695 | { |
696 | i++; | |
697 | continue; | |
698 | } | |
b80f6443 | 699 | |
726f6388 JA |
700 | break; |
701 | } | |
702 | ||
703 | if (c == '{') | |
704 | level++; | |
705 | else if (c == '}' && level) | |
706 | level--; | |
0628567a JA |
707 | #if !defined (CSH_BRACE_COMPAT) |
708 | else if (satisfy == '}' && c == brace_arg_separator && level == 0) | |
709 | commas++; | |
710 | else if (satisfy == '}' && STREQN (text+i, BRACE_SEQ_SPECIFIER, 2) && | |
711 | text[i+2] != satisfy && level == 0) | |
712 | commas++; | |
713 | #endif | |
7117c2d2 | 714 | |
a0c0a00f | 715 | #if defined (SHELL) |
7117c2d2 | 716 | ADVANCE_CHAR (text, tlen, i); |
a0c0a00f CR |
717 | #else |
718 | i++; | |
719 | #endif | |
726f6388 JA |
720 | } |
721 | ||
722 | *indx = i; | |
723 | return (c); | |
724 | } | |
725 | ||
726 | /* Return a new array of strings which is the result of appending each | |
727 | string in ARR2 to each string in ARR1. The resultant array is | |
728 | len (arr1) * len (arr2) long. For convenience, ARR1 (and its contents) | |
729 | are free ()'ed. ARR1 can be NULL, in that case, a new version of ARR2 | |
730 | is returned. */ | |
731 | static char ** | |
732 | array_concat (arr1, arr2) | |
733 | char **arr1, **arr2; | |
734 | { | |
735 | register int i, j, len, len1, len2; | |
736 | register char **result; | |
737 | ||
ccc6cda3 | 738 | if (arr1 == 0) |
ac50fbac | 739 | return (arr2); /* XXX - see if we can get away without copying? */ |
726f6388 | 740 | |
ccc6cda3 | 741 | if (arr2 == 0) |
ac50fbac CR |
742 | return (arr1); /* XXX - caller expects us to free arr1 */ |
743 | ||
744 | /* We can only short-circuit if the array consists of a single null element; | |
745 | otherwise we need to replicate the contents of the other array and | |
746 | prefix (or append, below) an empty element to each one. */ | |
747 | if (arr1[0] && arr1[0][0] == 0 && arr1[1] == 0) | |
748 | { | |
749 | strvec_dispose (arr1); | |
750 | return (arr2); /* XXX - use flags to see if we can avoid copying here */ | |
751 | } | |
752 | ||
753 | if (arr2[0] && arr2[0][0] == 0 && arr2[1] == 0) | |
754 | return (arr1); /* XXX - rather than copying and freeing it */ | |
726f6388 | 755 | |
7117c2d2 JA |
756 | len1 = strvec_len (arr1); |
757 | len2 = strvec_len (arr2); | |
726f6388 | 758 | |
d233b485 CR |
759 | result = (char **)malloc ((1 + (len1 * len2)) * sizeof (char *)); |
760 | if (result == 0) | |
761 | return (result); | |
726f6388 JA |
762 | |
763 | len = 0; | |
764 | for (i = 0; i < len1; i++) | |
765 | { | |
766 | int strlen_1 = strlen (arr1[i]); | |
767 | ||
768 | for (j = 0; j < len2; j++) | |
769 | { | |
b80f6443 | 770 | result[len] = (char *)xmalloc (1 + strlen_1 + strlen (arr2[j])); |
726f6388 JA |
771 | strcpy (result[len], arr1[i]); |
772 | strcpy (result[len] + strlen_1, arr2[j]); | |
773 | len++; | |
774 | } | |
775 | free (arr1[i]); | |
776 | } | |
777 | free (arr1); | |
778 | ||
779 | result[len] = (char *)NULL; | |
780 | return (result); | |
781 | } | |
782 | ||
783 | #if defined (TEST) | |
784 | #include <stdio.h> | |
785 | ||
a0c0a00f CR |
786 | void * |
787 | xmalloc(n) | |
788 | size_t n; | |
726f6388 | 789 | { |
a0c0a00f | 790 | return (malloc (n)); |
726f6388 JA |
791 | } |
792 | ||
a0c0a00f CR |
793 | void * |
794 | xrealloc(p, n) | |
795 | void *p; | |
796 | size_t n; | |
797 | { | |
798 | return (realloc (p, n)); | |
799 | } | |
800 | ||
801 | int | |
802 | internal_error (format, arg1, arg2) | |
726f6388 JA |
803 | char *format, *arg1, *arg2; |
804 | { | |
805 | fprintf (stderr, format, arg1, arg2); | |
806 | fprintf (stderr, "\n"); | |
807 | } | |
a0c0a00f | 808 | |
726f6388 JA |
809 | main () |
810 | { | |
811 | char example[256]; | |
812 | ||
813 | for (;;) | |
814 | { | |
815 | char **result; | |
816 | int i; | |
817 | ||
818 | fprintf (stderr, "brace_expand> "); | |
819 | ||
820 | if ((!fgets (example, 256, stdin)) || | |
821 | (strncmp (example, "quit", 4) == 0)) | |
822 | break; | |
823 | ||
824 | if (strlen (example)) | |
825 | example[strlen (example) - 1] = '\0'; | |
826 | ||
827 | result = brace_expand (example); | |
828 | ||
829 | for (i = 0; result[i]; i++) | |
830 | printf ("%s\n", result[i]); | |
831 | ||
a0c0a00f | 832 | strvec_dispose (result); |
726f6388 JA |
833 | } |
834 | } | |
835 | \f | |
836 | /* | |
837 | * Local variables: | |
838 | * compile-command: "gcc -g -Bstatic -DTEST -o brace_expand braces.c general.o" | |
839 | * end: | |
840 | */ | |
841 | ||
842 | #endif /* TEST */ | |
ccc6cda3 | 843 | #endif /* BRACE_EXPANSION */ |