Back to index

tetex-bin  3.0
expand.c
Go to the documentation of this file.
00001 /* expand.c: general expansion.
00002 
00003 Copyright (C) 1993, 94, 95, 96, 97, 98 Karl Berry & Olaf Weber.
00004 
00005 This library is free software; you can redistribute it and/or
00006 modify it under the terms of the GNU Library General Public
00007 License as published by the Free Software Foundation; either
00008 version 2 of the License, or (at your option) any later version.
00009 
00010 This library is distributed in the hope that it will be useful,
00011 but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 Library General Public License for more details.
00014 
00015 You should have received a copy of the GNU Library General Public
00016 License along with this library; if not, write to the Free Software
00017 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
00018 
00019 #include <kpathsea/config.h>
00020 
00021 #include <kpathsea/c-pathch.h>
00022 #include <kpathsea/expand.h>
00023 #include <kpathsea/pathsearch.h>
00024 #include <kpathsea/tilde.h>
00025 #include <kpathsea/variable.h>
00026 #include <kpathsea/concatn.h>
00027 #include <kpathsea/absolute.h>
00028 #include <kpathsea/str-list.h>
00029 
00030 /* Do variable expansion first so ~${USER} works.  (Besides, it's what the
00031    shells do.)  */
00032 
00033 string
00034 kpse_expand P1C(const_string, s)
00035 {
00036   string var_expansion = kpse_var_expand (s);
00037   string tilde_expansion = kpse_tilde_expand (var_expansion);
00038   
00039   /* `kpse_var_expand' always gives us new memory; `kpse_tilde_expand'
00040      doesn't, necessarily.  So be careful that we don't free what we are
00041      about to return.  */
00042   if (tilde_expansion != var_expansion)
00043     free (var_expansion);
00044   
00045   return tilde_expansion;
00046 }
00047 
00048 
00049 /* Forward declarations of functions from the original expand.c  */
00050 static str_list_type brace_expand P1H(const_string*);
00051 
00052 /* If $KPSE_DOT is defined in the environment, prepend it to any relative
00053    path components. */
00054 
00055 static string
00056 kpse_expand_kpse_dot P1C(string, path)
00057 {
00058   string ret, elt;
00059   string kpse_dot = getenv("KPSE_DOT");
00060 #ifdef MSDOS
00061   boolean malloced_kpse_dot = false;
00062 #endif
00063   
00064   if (kpse_dot == NULL)
00065     return path;
00066   ret = (string)xmalloc(1);
00067   *ret = 0;
00068 
00069 #ifdef MSDOS
00070   /* Some setups of ported Bash force $KPSE_DOT to have the //d/foo/bar
00071      form (when `pwd' is used), which is not understood by libc and the OS.
00072      Convert them back to the usual d:/foo/bar form.  */
00073   if (kpse_dot[0] == '/' && kpse_dot[1] == '/'
00074       && kpse_dot[2] >= 'A' && kpse_dot[2] <= 'z' && kpse_dot[3] == '/') {
00075     kpse_dot++;
00076     kpse_dot = xstrdup (kpse_dot);
00077     kpse_dot[0] = kpse_dot[1];  /* drive letter */
00078     kpse_dot[1] = ':';
00079     malloced_kpse_dot = true;
00080   }
00081 #endif
00082 
00083   for (elt = kpse_path_element (path); elt; elt = kpse_path_element (NULL)) {
00084     string save_ret = ret;
00085     /* We assume that the !! magic is only used on absolute components.
00086        Single "." gets special treatment, as does "./" or its equivalent. */
00087     if (kpse_absolute_p (elt, false) || (elt[0] == '!' && elt[1] == '!')) {
00088       ret = concat3(ret, elt, ENV_SEP_STRING);
00089     } else if (elt[0] == '.' && elt[1] == 0) {
00090       ret = concat3 (ret, kpse_dot, ENV_SEP_STRING);
00091 #ifndef VMS
00092     } else if (elt[0] == '.' && IS_DIR_SEP(elt[1])) {
00093       ret = concatn (ret, kpse_dot, elt + 1, ENV_SEP_STRING, NULL);
00094     } else {
00095       ret = concatn (ret, kpse_dot, DIR_SEP_STRING, elt, ENV_SEP_STRING, NULL);
00096 #endif
00097     }
00098     free (save_ret);
00099   }
00100 
00101 #ifdef MSDOS
00102   if (malloced_kpse_dot) free (kpse_dot);
00103 #endif
00104 
00105   ret[strlen (ret) - 1] = 0;
00106   return ret;
00107 }
00108 
00109 /* Do brace expansion on ELT; then do variable and ~ expansion on each
00110    element of the result; then do brace expansion again, in case a
00111    variable definition contained braces (e.g., $TEXMF).  Return a
00112    string comprising all of the results separated by ENV_SEP_STRING.  */
00113 
00114 static string
00115 kpse_brace_expand_element P1C(const_string, elt)
00116 {
00117   unsigned i;
00118   str_list_type expansions = brace_expand (&elt);
00119   string ret = (string)xmalloc (1);
00120   *ret = 0;
00121 
00122   for (i = 0; i != STR_LIST_LENGTH(expansions); i++) {
00123     /* Do $ and ~ expansion on each element.  */
00124     string x = kpse_expand (STR_LIST_ELT(expansions,i));
00125     string save_ret = ret;
00126     if (!STREQ (x, STR_LIST_ELT(expansions,i))) {
00127       /* If we did any expansions, do brace expansion again.  Since
00128          recursive variable definitions are not allowed, this recursion
00129          must terminate.  (In practice, it's unlikely there will ever be
00130          more than one level of recursion.)  */
00131       string save_x = x;
00132       x = kpse_brace_expand_element (x);
00133       free (save_x);
00134     }
00135     ret = concat3 (ret, x, ENV_SEP_STRING);
00136     free (save_ret);
00137     free (x);
00138   }
00139   for (i = 0; i != STR_LIST_LENGTH(expansions); ++i) {
00140       free(STR_LIST_ELT(expansions,i));
00141   }
00142   str_list_free(&expansions);
00143   ret[strlen (ret) - 1] = 0; /* waste the trailing null */
00144   return ret;
00145 }
00146 
00147 /* Be careful to not waste all the memory we allocate for each element.  */
00148 
00149 string
00150 kpse_brace_expand P1C(const_string, path)
00151 {
00152   string kpse_dot_expansion;
00153   string elt;
00154   unsigned len;
00155   /* Must do variable expansion first because if we have
00156        foo = .:~
00157        TEXINPUTS = $foo
00158      we want to end up with TEXINPUTS = .:/home/karl.
00159      Since kpse_path_element is not reentrant, we must get all
00160      the path elements before we start the loop.  */
00161   string xpath = kpse_var_expand (path);
00162   string ret = (string)xmalloc (1);
00163   *ret = 0;
00164 
00165   for (elt = kpse_path_element (xpath); elt; elt = kpse_path_element (NULL)) {
00166     string save_ret = ret;
00167     /* Do brace expansion first, so tilde expansion happens in {~ka,~kb}.  */
00168     string expansion = kpse_brace_expand_element (elt);
00169     ret = concat3 (ret, expansion, ENV_SEP_STRING);
00170     free (expansion);
00171     free (save_ret);
00172   }
00173 
00174   /* Waste the last byte by overwriting the trailing env_sep with a null.  */
00175   len = strlen (ret);
00176   if (len != 0)
00177     ret[len - 1] = 0;
00178   free (xpath);
00179 
00180   kpse_dot_expansion = kpse_expand_kpse_dot (ret);
00181   if (kpse_dot_expansion != ret)
00182     free (ret);
00183 
00184   return kpse_dot_expansion;
00185 }
00186 
00187 /* Expand all special constructs in a path, and include only the actually
00188    existing directories in the result. */
00189 string
00190 kpse_path_expand P1C(const_string, path)
00191 {
00192   string ret;
00193   string xpath;
00194   string elt;
00195   unsigned len;
00196 
00197   /* Initialise ret to the empty string. */
00198   ret = (string)xmalloc (1);
00199   *ret = 0;
00200   len = 0;
00201   
00202   /* Expand variables and braces first.  */
00203   xpath = kpse_brace_expand (path);
00204 
00205   /* Now expand each of the path elements, printing the results */
00206   for (elt = kpse_path_element (xpath); elt; elt = kpse_path_element (NULL)) {
00207     str_llist_type *dirs;
00208 
00209     /* Skip and ignore magic leading chars.  */
00210     if (*elt == '!' && *(elt + 1) == '!')
00211       elt += 2;
00212 
00213     /* Search the disk for all dirs in the component specified.
00214        Be faster to check the database, but this is more reliable.  */
00215     dirs = kpse_element_dirs (elt); 
00216     if (dirs && *dirs) {
00217       str_llist_elt_type *dir;
00218 
00219       for (dir = *dirs; dir; dir = STR_LLIST_NEXT (*dir)) {
00220         string thedir = STR_LLIST (*dir);
00221         unsigned dirlen = strlen (thedir);
00222         string save_ret = ret;
00223         /* We need to retain trailing slash if that's the root directory.
00224          * On unix, "/" is root dir, "" often taken to be current dir.
00225          * On windows, "C:/" is root dir of drive C, and "C:" is current
00226          * on drive C.  There's no need to look at other cases, like UNC
00227          * names.
00228          */
00229         if (dirlen == 1 || (dirlen == 3 && NAME_BEGINS_WITH_DEVICE (thedir)
00230                             && IS_DIR_SEP (thedir[2]))) {
00231           ret = concat3 (ret, thedir, ENV_SEP_STRING);
00232           len += dirlen + 1;
00233           ret[len - 1] = ENV_SEP;
00234         } else {
00235           ret = concat (ret, thedir);
00236           len += dirlen;
00237           ret [len - 1] = ENV_SEP;
00238         }
00239         free (save_ret);
00240       }
00241     }
00242   }
00243   /* Get rid of trailing ':', if any. */
00244   if (len != 0)
00245     ret[len - 1] = 0;
00246   return ret;
00247 }
00248 
00249 /* ... */
00250 static void expand_append P3C(str_list_type*, partial,
00251                               const_string, text, const_string, p)
00252 {
00253     string new_string;
00254     unsigned len;
00255     str_list_type tmp;
00256     tmp = str_list_init();
00257     len = p - text;
00258     new_string = (string)xmalloc(len+1);
00259     strncpy(new_string, text, len);
00260     new_string[len]=0;
00261     str_list_add(&tmp, new_string);
00262     str_list_concat_elements(partial, tmp);
00263 }
00264                               
00265 
00266 
00267 static str_list_type brace_expand P1C(const_string *, text)
00268 {
00269     str_list_type result, partial, recurse;
00270     const_string p;
00271     result = str_list_init();
00272     partial = str_list_init();
00273     for (p = *text; *p && *p != '}'; ++p) {
00274         if (*p == ENV_SEP || *p == ',') {
00275             expand_append(&partial, *text, p);
00276             str_list_concat(&result, partial);
00277             str_list_free(&partial);
00278             *text = p+1;
00279             partial = str_list_init();
00280         } else if (*p == '{') {
00281             expand_append(&partial, *text, p);
00282             ++p;
00283             recurse = brace_expand(&p);
00284             str_list_concat_elements(&partial, recurse);
00285             str_list_free(&recurse);
00286             /* Check for missing closing brace. */
00287             if (*p != '}') {
00288                 WARNING1 ("%s: Unmatched {", *text);
00289             }
00290             *text = p+1;
00291         } else if (*p == '$') {
00292             /* Skip ${VAR} */
00293             if (*(p+1) == '{')
00294                 for (p+=2; *p!='}';++p);
00295         }
00296     }
00297     expand_append(&partial, *text, p);
00298     str_list_concat(&result, partial);
00299     str_list_free(&partial);
00300     *text = p;
00301     return result;
00302 }
00303 
00304 
00305 
00306 #if defined (TEST)
00307 #include <stdio.h>
00308 
00309 fatal_error (format, arg1, arg2)
00310      char *format, *arg1, *arg2;
00311 {
00312   report_error (format, arg1, arg2);
00313   exit (1);
00314 }
00315 
00316 report_error (format, arg1, arg2)
00317      char *format, *arg1, *arg2;
00318 {
00319   fprintf (stderr, format, arg1, arg2);
00320   fprintf (stderr, "\n");
00321 }
00322 
00323 main ()
00324 {
00325   char example[256];
00326 
00327   for (;;)
00328     {
00329       char **result;
00330       int i;
00331 
00332       fprintf (stderr, "brace_expand> ");
00333 
00334       if ((!fgets (example, 256, stdin)) ||
00335          (strncmp (example, "quit", 4) == 0))
00336        break;
00337 
00338       if (strlen (example))
00339        example[strlen (example) - 1] = 0;
00340 
00341       result = brace_expand (example);
00342 
00343       for (i = 0; result[i]; i++)
00344        printf ("%s\n", result[i]);
00345 
00346       free_array (result);
00347     }
00348 }
00349 
00350 /*
00351  * Local variables:
00352  * test-compile-command: "gcc -g -DTEST -I.. -I. -o brace_expand braces.c -L. -lkpathsea"
00353  * end:
00354  */
00355 
00356 #endif /* TEST */