/* braces.c -- code for doing word expansion in curly braces. */ /* Copyright (C) 1987,1991 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. Bash is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. Bash is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Bash; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ /* Stuff in curly braces gets expanded before all other shell expansions. */ #include "config.h" #if defined (BRACE_EXPANSION) #if defined (HAVE_UNISTD_H) # ifdef _MINIX # include # endif # include #endif #include "bashansi.h" #if defined (SHELL) # include "shell.h" #endif /* SHELL */ #include "general.h" #define brace_whitespace(c) (!(c) || (c) == ' ' || (c) == '\t' || (c) == '\n') #if defined (SHELL) extern char *extract_command_subst (); #endif /* Basic idea: Segregate the text into 3 sections: preamble (stuff before an open brace), postamble (stuff after the matching close brace) and amble (stuff after preamble, and before postamble). Expand amble, and then tack on the expansions to preamble. Expand postamble, and tack on the expansions to the result so far. */ /* The character which is used to separate arguments. */ int brace_arg_separator = ','; int brace_arg_separator2 = ':'; /* maali */ static int brace_gobbler (); static char **expand_amble (), **array_concat (); /* Return an array of strings; the brace expansion of TEXT. */ char ** brace_expand (text) char *text; { register int start; char *preamble, *postamble, *amble; char **tack, **result; int i, j, c; /* Find the text of the preamble. */ i = 0; c = brace_gobbler (text, &i, '{'); preamble = (char *)xmalloc (i + 1); strncpy (preamble, text, i); preamble[i] = '\0'; result = (char **)xmalloc (2 * sizeof (char *)); result[0] = preamble; result[1] = (char *)NULL; /* Special case. If we never found an exciting character, then the preamble is all of the text, so just return that. */ if (c != '{') return (result); /* Find the amble. This is the stuff inside this set of braces. */ start = ++i; c = brace_gobbler (text, &i, '}'); /* What if there isn't a matching close brace? */ if (c == 0) { #if defined (NOTDEF) /* Well, if we found an unquoted BRACE_ARG_SEPARATOR between START and I, then this should be an error. Otherwise, it isn't. */ for (j = start; j < i; j++) { if (text[j] == '\\') { j++; continue; } if ((text[j] == brace_arg_separator)|| /*maali*/ (text[j] == brace_arg_separator2)) /*maali*/ { free_array (result); report_error ("missing `}'"); throw_to_top_level (); } } #endif free (preamble); /* Same as result[0]; see initialization. */ result[0] = savestring (text); return (result); } #if defined (SHELL) amble = substring (text, start, i); #else amble = (char *)xmalloc (1 + (i - start)); strncpy (amble, &text[start], (i - start)); amble[i - start] = '\0'; #endif #if defined (SHELL) /* If the amble does not contain an unquoted BRACE_ARG_SEPARATOR, then just return without doing any expansion. */ for (j = 0; amble[j]; j++) { if (amble[j] == '\\') { j++; continue; } if ( (amble[j] == brace_arg_separator) /* maali may 25 2000 */ ||(amble[j] == brace_arg_separator2)) /* maali may 25 2000 */ break; } if (!amble[j]) { free (amble); free (preamble); result[0] = savestring (text); return (result); } #endif /* SHELL */ postamble = &text[i + 1]; tack = expand_amble (amble); result = array_concat (result, tack); free (amble); free_array (tack); tack = brace_expand (postamble); result = array_concat (result, tack); free_array (tack); return (result); } /* maali mucking here */ /**************************************************************/ /* maali --- will improve quality once code works. promise. */ #include #include /* from "stringlib.c" line 434 */ void strip_leading (string) char *string; { char *start = string; while (*string && (whitespace (*string) || *string == '\n')) string++; if (string != start) { int len = strlen (string); FASTCOPY (string, start, len); start[len] = '\0'; } } /* remove all chars but [0-9:-,] NOT EFFICIENT */ static void trim (char * s) { int len; char *rest; strip_leading (s); strip_trailing (s, strlen(s), 0); rest=s; while (*s) { while (*s&&(!(isdigit(*s) || *s == ':' || *s == ',' || *s == '-' ))) s++; if (s!=rest) { len = strlen (s); FASTCOPY (s, rest, len); rest[len] = '\0'; } s = ++rest; } } /* this takes the place of log10 */ static int get_number_of_digits(int i) { int d=10; if (i>=1000000000) d=11; /* add one */ else if (i>=100000000) d=9; else if (i>=10000000) d=8; else if (i>=1000000) d=7; else if (i>=100000) d=6; else if (i>=10000) d=5; else if (i>=1000) d=4; else if (i>=100) d=3; else if (i>=10) d=2; return d; } static int estimate_digits_required(int i) { int ai,li; if (i==0) return 1; ai = (i<0)?abs(i):i; li = (get_number_of_digits(i)+1)+(ai==i?0:1)+1; /* mag + sign + safety */ return li; } void brace_range_expand(char **oldtext) { /********************************************************* * here (in theory) we detect strings of the form * "1:10" -> 1,2,3,4,5,6,7,8,9,10 * or "0:2:10" -> 0,2,4,6,8,10 * or "10:0" -> 10,9,8,7,6,5,4,3,2,1,0 * or "10:-2:0" -> 10,8,6,4,2,0 * or "10:-2:0,5" -> 00010,00008,00006,00004,00002,00000 * * and we expand them, which the rest of this code then * parses out *********************************************************/ int i,start,stop,step,ncol,ncom,sign,width,estwidth,textlen,newtextlen; char *text, *newtext, fmt[1024], elem[1024], *sep=","; /* FOR NOW, ANY COLONS ':' IN THE BRACE EXPANSION TEXT TRIGGERS THIS CODE */ textlen = strlen(*oldtext); for(ncol=0, ncom=0, i=0; i2)||(ncom>1)) return; /*****/ /* now we can assume the string is a matlab-style expansion thing */ /**** set up old + new string pointers */ text = malloc(strlen(*oldtext)*sizeof(char)); strncpy(text, *oldtext, textlen+1); trim(text); textlen = strlen(text); newtextlen = 0; newtext = NULL; /**** get 4 parameters: start stop step width */ if (ncol==1){ if (ncom==0) { sscanf(text,"%d:%d", &start, &stop); width = 0; } else if (ncom==1){ sscanf(text,"%d:%d,%d", &start, &stop, &width); } else { sscanf(text,"%d:%d,%d", &start, &stop, &width); } step = (start50*sizeof(int)) width=3*sizeof(int); if (stop==start) step=0; /* check that "step" goes towards stop */ sign=(step>0)?1:-1; if (sign*(start+step-stop)0)?width:(abs(start)>abs(stop))? estimate_digits_required(start):estimate_digits_required(stop); newtextlen = (1+((step==0)?0:abs((stop-start)/step)))*(1+estwidth)+32; if ((step==0)||(newtextlen>1024*1024)||(newtextlen<32)) newtextlen=32; newtext = malloc(newtextlen*sizeof(char)); /**** initialize list */ i=start; snprintf(fmt, 1024, "%%0%dd", width); snprintf(newtext, newtextlen, fmt, i); i+=step; /**** loop to construct list */ sign=(step>0)?1:-1; snprintf(fmt, 1024, "%s%%0%dd", sep, width); while (sign*(i-start) /* fatal_error (format, arg1, arg2) char *format, *arg1, *arg2; { report_error (format, arg1, arg2); exit (1); } report_error (format, arg1, arg2) char *format, *arg1, *arg2; { fprintf (stderr, format, arg1, arg2); fprintf (stderr, "\n"); } */ main () { char example[256]; for (;;) { char **result; int i; fprintf (stderr, "brace_expand> "); if ((!fgets (example, 256, stdin)) || (strncmp (example, "quit", 4) == 0)) break; if (strlen (example)) example[strlen (example) - 1] = '\0'; result = brace_expand (example); for (i = 0; result[i]; i++) printf ("%s\n", result[i]); free_array (result); } } /* * Local variables: * compile-command: "gcc -g -Bstatic -DTEST -o brace_expand braces.c general.o" * end: */ #endif /* TEST */ #endif /* BRACE_EXPANSION */