dvitomp fix from Akira
[mplib] / src / texk / kpathsea / expand.c
1 /* expand.c: general expansion.
2
3    Copyright 1993, 1994, 1995, 1996, 1997, 1998, 2005, 2008 Karl Berry &
4    Olaf Weber.
5
6    This library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    This library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public License
17    along with this library; if not, see <http://www.gnu.org/licenses/>.  */
18
19 #include <kpathsea/config.h>
20
21 #include <kpathsea/c-pathch.h>
22 #include <kpathsea/expand.h>
23 #include <kpathsea/pathsearch.h>
24 #include <kpathsea/tilde.h>
25 #include <kpathsea/variable.h>
26 #include <kpathsea/concatn.h>
27 #include <kpathsea/absolute.h>
28 #include <kpathsea/str-list.h>
29
30 /* Do variable expansion first so ~${USER} works.  (Besides, it's what the
31    shells do.)  */
32
33 string
34 kpse_expand P1C(const_string, s)
35 {
36   string var_expansion = kpse_var_expand (s);
37   string tilde_expansion = kpse_tilde_expand (var_expansion);
38   
39   /* `kpse_var_expand' always gives us new memory; `kpse_tilde_expand'
40      doesn't, necessarily.  So be careful that we don't free what we are
41      about to return.  */
42   if (tilde_expansion != var_expansion)
43     free (var_expansion);
44   
45   return tilde_expansion;
46 }
47
48 \f
49 /* Forward declarations of functions from the original expand.c  */
50 static str_list_type brace_expand P1H(const_string*);
51
52 /* If $KPSE_DOT is defined in the environment, prepend it to any relative
53    path components. */
54
55 static string
56 kpse_expand_kpse_dot P1C(string, path)
57 {
58   string ret, elt;
59   string kpse_dot = getenv("KPSE_DOT");
60 #ifdef MSDOS
61   boolean malloced_kpse_dot = false;
62 #endif
63   
64   if (kpse_dot == NULL)
65     return path;
66   ret = (string)xmalloc(1);
67   *ret = 0;
68
69 #ifdef MSDOS
70   /* Some setups of ported Bash force $KPSE_DOT to have the //d/foo/bar
71      form (when `pwd' is used), which is not understood by libc and the OS.
72      Convert them back to the usual d:/foo/bar form.  */
73   if (kpse_dot[0] == '/' && kpse_dot[1] == '/'
74       && kpse_dot[2] >= 'A' && kpse_dot[2] <= 'z' && kpse_dot[3] == '/') {
75     kpse_dot++;
76     kpse_dot = xstrdup (kpse_dot);
77     kpse_dot[0] = kpse_dot[1];  /* drive letter */
78     kpse_dot[1] = ':';
79     malloced_kpse_dot = true;
80   }
81 #endif
82
83   for (elt = kpse_path_element (path); elt; elt = kpse_path_element (NULL)) {
84     string save_ret = ret;
85     boolean ret_copied = true;
86     /* We assume that the !! magic is only used on absolute components.
87        Single "." gets special treatment, as does "./" or its equivalent. */
88     if (kpse_absolute_p (elt, false) || (elt[0] == '!' && elt[1] == '!')) {
89       ret = concat3(ret, elt, ENV_SEP_STRING);
90     } else if (elt[0] == '.' && elt[1] == 0) {
91       ret = concat3 (ret, kpse_dot, ENV_SEP_STRING);
92 #ifndef VMS
93     } else if (elt[0] == '.' && IS_DIR_SEP(elt[1])) {
94       ret = concatn (ret, kpse_dot, elt + 1, ENV_SEP_STRING, NULL);
95     } else if (*elt) {
96       ret = concatn (ret, kpse_dot, DIR_SEP_STRING, elt, ENV_SEP_STRING, NULL);
97 #endif
98     } else {
99       /* omit empty path elements from TEXMFCNF.
100          See http://bugs.debian.org/358330.  */
101       ret_copied = false;
102     }
103     if (ret_copied)
104       free (save_ret);
105   }
106
107 #ifdef MSDOS
108   if (malloced_kpse_dot) free (kpse_dot);
109 #endif
110
111   ret[strlen (ret) - 1] = 0;
112   return ret;
113 }
114
115 /* Do brace expansion on ELT; then do variable and ~ expansion on each
116    element of the result; then do brace expansion again, in case a
117    variable definition contained braces (e.g., $TEXMF).  Return a
118    string comprising all of the results separated by ENV_SEP_STRING.  */
119
120 static string
121 kpse_brace_expand_element P1C(const_string, elt)
122 {
123   unsigned i;
124   str_list_type expansions = brace_expand (&elt);
125   string ret = (string)xmalloc (1);
126   *ret = 0;
127
128   for (i = 0; i != STR_LIST_LENGTH(expansions); i++) {
129     /* Do $ and ~ expansion on each element.  */
130     string x = kpse_expand (STR_LIST_ELT(expansions,i));
131     string save_ret = ret;
132     if (!STREQ (x, STR_LIST_ELT(expansions,i))) {
133       /* If we did any expansions, do brace expansion again.  Since
134          recursive variable definitions are not allowed, this recursion
135          must terminate.  (In practice, it's unlikely there will ever be
136          more than one level of recursion.)  */
137       string save_x = x;
138       x = kpse_brace_expand_element (x);
139       free (save_x);
140     }
141     ret = concat3 (ret, x, ENV_SEP_STRING);
142     free (save_ret);
143     free (x);
144   }
145   for (i = 0; i != STR_LIST_LENGTH(expansions); ++i) {
146       free(STR_LIST_ELT(expansions,i));
147   }
148   str_list_free(&expansions);
149   ret[strlen (ret) - 1] = 0; /* waste the trailing null */
150   return ret;
151 }
152
153 /* Be careful to not waste all the memory we allocate for each element.  */
154
155 string
156 kpse_brace_expand P1C(const_string, path)
157 {
158   string kpse_dot_expansion;
159   string elt;
160   unsigned len;
161   /* Must do variable expansion first because if we have
162        foo = .:~
163        TEXINPUTS = $foo
164      we want to end up with TEXINPUTS = .:/home/karl.
165      Since kpse_path_element is not reentrant, we must get all
166      the path elements before we start the loop.  */
167   string xpath = kpse_var_expand (path);
168   string ret = (string)xmalloc (1);
169   *ret = 0;
170
171   for (elt = kpse_path_element (xpath); elt; elt = kpse_path_element (NULL)) {
172     string save_ret = ret;
173     /* Do brace expansion first, so tilde expansion happens in {~ka,~kb}.  */
174     string expansion = kpse_brace_expand_element (elt);
175     ret = concat3 (ret, expansion, ENV_SEP_STRING);
176     free (expansion);
177     free (save_ret);
178   }
179
180   /* Waste the last byte by overwriting the trailing env_sep with a null.  */
181   len = strlen (ret);
182   if (len != 0)
183     ret[len - 1] = 0;
184   free (xpath);
185
186   kpse_dot_expansion = kpse_expand_kpse_dot (ret);
187   if (kpse_dot_expansion != ret)
188     free (ret);
189
190   return kpse_dot_expansion;
191 }
192 \f
193 /* Expand all special constructs in a path, and include only the actually
194    existing directories in the result. */
195 string
196 kpse_path_expand P1C(const_string, path)
197 {
198   string ret;
199   string xpath;
200   string elt;
201   unsigned len;
202
203   /* Initialise ret to the empty string. */
204   ret = (string)xmalloc (1);
205   *ret = 0;
206   len = 0;
207   
208   /* Expand variables and braces first.  */
209   xpath = kpse_brace_expand (path);
210
211   /* Now expand each of the path elements, printing the results */
212   for (elt = kpse_path_element (xpath); elt; elt = kpse_path_element (NULL)) {
213     str_llist_type *dirs;
214
215     /* Skip and ignore magic leading chars.  */
216     if (*elt == '!' && *(elt + 1) == '!')
217       elt += 2;
218
219     /* Search the disk for all dirs in the component specified.
220        Be faster to check the database, but this is more reliable.  */
221     dirs = kpse_element_dirs (elt); 
222     if (dirs && *dirs) {
223       str_llist_elt_type *dir;
224
225       for (dir = *dirs; dir; dir = STR_LLIST_NEXT (*dir)) {
226         string thedir = STR_LLIST (*dir);
227         unsigned dirlen = strlen (thedir);
228         string save_ret = ret;
229         /* We need to retain trailing slash if that's the root directory.
230          * On unix, "/" is root dir, "" often taken to be current dir.
231          * On windows, "C:/" is root dir of drive C, and "C:" is current
232          * on drive C.  There's no need to look at other cases, like UNC
233          * names.
234          */
235         if (dirlen == 1 || (dirlen == 3 && NAME_BEGINS_WITH_DEVICE (thedir)
236                             && IS_DIR_SEP (thedir[2]))) {
237           ret = concat3 (ret, thedir, ENV_SEP_STRING);
238           len += dirlen + 1;
239           ret[len - 1] = ENV_SEP;
240         } else {
241           ret = concat (ret, thedir);
242           len += dirlen;
243           ret [len - 1] = ENV_SEP;
244         }
245         free (save_ret);
246       }
247     }
248   }
249   /* Get rid of trailing ':', if any. */
250   if (len != 0)
251     ret[len - 1] = 0;
252   return ret;
253 }
254
255 /* ... */
256 static void expand_append P3C(str_list_type*, partial,
257                               const_string, text, const_string, p)
258 {
259     string new_string;
260     unsigned len;
261     str_list_type tmp;
262     tmp = str_list_init();
263     len = p - text;
264     new_string = (string)xmalloc(len+1);
265     strncpy(new_string, text, len);
266     new_string[len]=0;
267     str_list_add(&tmp, new_string);
268     str_list_concat_elements(partial, tmp);
269 }
270                               
271
272
273 static str_list_type brace_expand P1C(const_string *, text)
274 {
275     str_list_type result, partial, recurse;
276     const_string p;
277     result = str_list_init();
278     partial = str_list_init();
279     for (p = *text; *p && *p != '}'; ++p) {
280         /* FIXME: Should be IS_ENV_SEP(*p) */
281         if (*p == ENV_SEP || *p == ',') {
282             expand_append(&partial, *text, p);
283             str_list_concat(&result, partial);
284             str_list_free(&partial);
285             *text = p+1;
286             partial = str_list_init();
287         } else if (*p == '{') {
288             expand_append(&partial, *text, p);
289             ++p;
290             recurse = brace_expand(&p);
291             str_list_concat_elements(&partial, recurse);
292             str_list_free(&recurse);
293             /* Check for missing closing brace. */
294             if (*p != '}') {
295                 WARNING1 ("%s: Unmatched {", *text);
296             }
297             *text = p+1;
298         } else if (*p == '$') {
299             /* Skip ${VAR} */
300             if (*(p+1) == '{')
301                 for (p+=2; *p!='}';++p);
302         }
303     }
304     expand_append(&partial, *text, p);
305     str_list_concat(&result, partial);
306     str_list_free(&partial);
307     *text = p;
308     return result;
309 }
310
311
312 \f
313 #if defined (TEST)
314 #include <stdio.h>
315
316 fatal_error (format, arg1, arg2)
317      char *format, *arg1, *arg2;
318 {
319   report_error (format, arg1, arg2);
320   exit (1);
321 }
322
323 report_error (format, arg1, arg2)
324      char *format, *arg1, *arg2;
325 {
326   fprintf (stderr, format, arg1, arg2);
327   fprintf (stderr, "\n");
328 }
329
330 main ()
331 {
332   char example[256];
333
334   for (;;)
335     {
336       char **result;
337       int i;
338
339       fprintf (stderr, "brace_expand> ");
340
341       if ((!fgets (example, 256, stdin)) ||
342           (strncmp (example, "quit", 4) == 0))
343         break;
344
345       if (strlen (example))
346         example[strlen (example) - 1] = 0;
347
348       result = brace_expand (example);
349
350       for (i = 0; result[i]; i++)
351         printf ("%s\n", result[i]);
352
353       free_array (result);
354     }
355 }
356 \f
357 /*
358  * Local variables:
359  * test-compile-command: "gcc -g -DTEST -I.. -I. -o brace_expand braces.c -L. -lkpathsea"
360  * end:
361  */
362
363 #endif /* TEST */