1 /* expand.c: general expansion.
3 Copyright 1993, 1994, 1995, 1996, 1997, 1998, 2005, 2008 Karl Berry &
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.
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.
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/>. */
19 #include <kpathsea/config.h>
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>
30 /* Do variable expansion first so ~${USER} works. (Besides, it's what the
34 kpse_expand P1C(const_string, s)
36 string var_expansion = kpse_var_expand (s);
37 string tilde_expansion = kpse_tilde_expand (var_expansion);
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
42 if (tilde_expansion != var_expansion)
45 return tilde_expansion;
49 /* Forward declarations of functions from the original expand.c */
50 static str_list_type brace_expand P1H(const_string*);
52 /* If $KPSE_DOT is defined in the environment, prepend it to any relative
56 kpse_expand_kpse_dot P1C(string, path)
59 string kpse_dot = getenv("KPSE_DOT");
61 boolean malloced_kpse_dot = false;
66 ret = (string)xmalloc(1);
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] == '/') {
76 kpse_dot = xstrdup (kpse_dot);
77 kpse_dot[0] = kpse_dot[1]; /* drive letter */
79 malloced_kpse_dot = true;
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);
93 } else if (elt[0] == '.' && IS_DIR_SEP(elt[1])) {
94 ret = concatn (ret, kpse_dot, elt + 1, ENV_SEP_STRING, NULL);
96 ret = concatn (ret, kpse_dot, DIR_SEP_STRING, elt, ENV_SEP_STRING, NULL);
99 /* omit empty path elements from TEXMFCNF.
100 See http://bugs.debian.org/358330. */
108 if (malloced_kpse_dot) free (kpse_dot);
111 ret[strlen (ret) - 1] = 0;
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. */
121 kpse_brace_expand_element P1C(const_string, elt)
124 str_list_type expansions = brace_expand (&elt);
125 string ret = (string)xmalloc (1);
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.) */
138 x = kpse_brace_expand_element (x);
141 ret = concat3 (ret, x, ENV_SEP_STRING);
145 for (i = 0; i != STR_LIST_LENGTH(expansions); ++i) {
146 free(STR_LIST_ELT(expansions,i));
148 str_list_free(&expansions);
149 ret[strlen (ret) - 1] = 0; /* waste the trailing null */
153 /* Be careful to not waste all the memory we allocate for each element. */
156 kpse_brace_expand P1C(const_string, path)
158 string kpse_dot_expansion;
161 /* Must do variable expansion first because if we have
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);
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);
180 /* Waste the last byte by overwriting the trailing env_sep with a null. */
186 kpse_dot_expansion = kpse_expand_kpse_dot (ret);
187 if (kpse_dot_expansion != ret)
190 return kpse_dot_expansion;
193 /* Expand all special constructs in a path, and include only the actually
194 existing directories in the result. */
196 kpse_path_expand P1C(const_string, path)
203 /* Initialise ret to the empty string. */
204 ret = (string)xmalloc (1);
208 /* Expand variables and braces first. */
209 xpath = kpse_brace_expand (path);
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;
215 /* Skip and ignore magic leading chars. */
216 if (*elt == '!' && *(elt + 1) == '!')
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);
223 str_llist_elt_type *dir;
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
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);
239 ret[len - 1] = ENV_SEP;
241 ret = concat (ret, thedir);
243 ret [len - 1] = ENV_SEP;
249 /* Get rid of trailing ':', if any. */
256 static void expand_append P3C(str_list_type*, partial,
257 const_string, text, const_string, p)
262 tmp = str_list_init();
264 new_string = (string)xmalloc(len+1);
265 strncpy(new_string, text, len);
267 str_list_add(&tmp, new_string);
268 str_list_concat_elements(partial, tmp);
273 static str_list_type brace_expand P1C(const_string *, text)
275 str_list_type result, partial, recurse;
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);
286 partial = str_list_init();
287 } else if (*p == '{') {
288 expand_append(&partial, *text, p);
290 recurse = brace_expand(&p);
291 str_list_concat_elements(&partial, recurse);
292 str_list_free(&recurse);
293 /* Check for missing closing brace. */
295 WARNING1 ("%s: Unmatched {", *text);
298 } else if (*p == '$') {
301 for (p+=2; *p!='}';++p);
304 expand_append(&partial, *text, p);
305 str_list_concat(&result, partial);
306 str_list_free(&partial);
316 fatal_error (format, arg1, arg2)
317 char *format, *arg1, *arg2;
319 report_error (format, arg1, arg2);
323 report_error (format, arg1, arg2)
324 char *format, *arg1, *arg2;
326 fprintf (stderr, format, arg1, arg2);
327 fprintf (stderr, "\n");
339 fprintf (stderr, "brace_expand> ");
341 if ((!fgets (example, 256, stdin)) ||
342 (strncmp (example, "quit", 4) == 0))
345 if (strlen (example))
346 example[strlen (example) - 1] = 0;
348 result = brace_expand (example);
350 for (i = 0; result[i]; i++)
351 printf ("%s\n", result[i]);
359 * test-compile-command: "gcc -g -DTEST -I.. -I. -o brace_expand braces.c -L. -lkpathsea"