diff options
author | Karl Berry <karl@freefriends.org> | 2021-02-25 19:22:25 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2021-02-25 19:22:25 +0000 |
commit | ad547a6b5986815fda458221149728d9d9ab1d87 (patch) | |
tree | 16296910eb3eca724371474ea9aea3994dc69614 /Build/source/texk/kpathsea/expand.c | |
parent | 947b43de3dd21d58ccc2ffadefc4441ea1c2a813 (diff) |
restore Build,TODO from r57911
git-svn-id: svn://tug.org/texlive/trunk@57915 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/kpathsea/expand.c')
-rw-r--r-- | Build/source/texk/kpathsea/expand.c | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/Build/source/texk/kpathsea/expand.c b/Build/source/texk/kpathsea/expand.c new file mode 100644 index 00000000000..dc01d9edd41 --- /dev/null +++ b/Build/source/texk/kpathsea/expand.c @@ -0,0 +1,417 @@ +/* expand.c: general expansion. + + Copyright 1993, 1994, 1995, 1996, 1997, 2005, 2008, 2009, 2011, + 2012, 2016, 2017 Karl Berry. + Copyright 1997-2005 Olaf Weber. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, see <http://www.gnu.org/licenses/>. */ + +#include <kpathsea/config.h> + +#include <kpathsea/c-pathch.h> +#include <kpathsea/expand.h> +#include <kpathsea/pathsearch.h> +#include <kpathsea/tilde.h> +#include <kpathsea/variable.h> +#include <kpathsea/concatn.h> +#include <kpathsea/absolute.h> +#include <kpathsea/str-list.h> + +/* Do variable expansion first so ~${USER} works. (Besides, it's what the + shells do.) */ + +string +kpathsea_expand (kpathsea kpse, const_string s) +{ + string var_expansion = kpathsea_var_expand (kpse, s); + string tilde_expansion = kpathsea_tilde_expand (kpse, var_expansion); + + /* `kpse_var_expand' always gives us new memory; `kpse_tilde_expand' + doesn't, necessarily. So be careful that we don't free what we are + about to return. */ + if (tilde_expansion != var_expansion) + free (var_expansion); + + return tilde_expansion; +} + +/* Forward declarations of functions from the original expand.c */ +static str_list_type brace_expand (kpathsea, const_string*); + +/* If $KPSE_DOT is defined in the environment, prepend it to any relative + path components. */ + +static string +kpathsea_expand_kpse_dot (kpathsea kpse, string path) +{ + string ret, elt; + string kpse_dot = getenv("KPSE_DOT"); +#ifdef MSDOS + boolean malloced_kpse_dot = false; +#endif + + if (kpse_dot == NULL) + return path; + ret = (string)xmalloc(1); + *ret = 0; + +#ifdef MSDOS + /* Some setups of ported Bash force $KPSE_DOT to have the //d/foo/bar + form (when `pwd' is used), which is not understood by libc and the OS. + Convert them back to the usual d:/foo/bar form. */ + if (kpse_dot[0] == '/' && kpse_dot[1] == '/' + && kpse_dot[2] >= 'A' && kpse_dot[2] <= 'z' && kpse_dot[3] == '/') { + kpse_dot++; + kpse_dot = xstrdup (kpse_dot); + kpse_dot[0] = kpse_dot[1]; /* drive letter */ + kpse_dot[1] = ':'; + malloced_kpse_dot = true; + } +#endif + + for (elt = kpathsea_path_element (kpse, path); elt; + elt = kpathsea_path_element (kpse, NULL)) { + string save_ret = ret; + boolean ret_copied = true; + /* We assume that the !! magic is only used on absolute components. + Single "." gets special treatment, as does "./" or its equivalent. */ + if (kpathsea_absolute_p (kpse, elt, false) + || (elt[0] == '!' && elt[1] == '!')) { + ret = concat3(ret, elt, ENV_SEP_STRING); + } else if (elt[0] == '.' && elt[1] == 0) { + ret = concat3 (ret, kpse_dot, ENV_SEP_STRING); +#ifndef VMS + } else if (elt[0] == '.' && IS_DIR_SEP(elt[1])) { + ret = concatn (ret, kpse_dot, elt + 1, ENV_SEP_STRING, NULL); + } else if (*elt) { + ret = concatn (ret, kpse_dot, DIR_SEP_STRING, elt, ENV_SEP_STRING, NULL); +#endif + } else { + /* omit empty path elements from TEXMFCNF. + See http://bugs.debian.org/358330. */ + ret_copied = false; + } + if (ret_copied) + free (save_ret); + } + +#ifdef MSDOS + if (malloced_kpse_dot) free (kpse_dot); +#endif + + ret[strlen (ret) - 1] = 0; + return ret; +} + +/* Do brace expansion on ELT; then do variable and ~ expansion on each + element of the result; then do brace expansion again, in case a + variable definition contained braces (e.g., $TEXMF). Return a + string comprising all of the results separated by ENV_SEP_STRING. */ + +static string +kpathsea_brace_expand_element (kpathsea kpse, const_string elt) +{ + unsigned i; + str_list_type expansions = brace_expand (kpse, &elt); + string ret = (string)xmalloc (1); + *ret = 0; + + for (i = 0; i != STR_LIST_LENGTH(expansions); i++) { + /* Do $ and ~ expansion on each element. */ + string x = kpathsea_expand (kpse, STR_LIST_ELT(expansions,i)); + string save_ret = ret; + if (!STREQ (x, STR_LIST_ELT(expansions,i))) { + /* If we did any expansions, do brace expansion again. Since + recursive variable definitions are not allowed, this recursion + must terminate. (In practice, it's unlikely there will ever be + more than one level of recursion.) */ + string save_x = x; + x = kpathsea_brace_expand_element (kpse, x); + free (save_x); + } + ret = concat3 (ret, x, ENV_SEP_STRING); + free (save_ret); + free (x); + } + for (i = 0; i != STR_LIST_LENGTH(expansions); ++i) { + free(STR_LIST_ELT(expansions,i)); + } + str_list_free(&expansions); + ret[strlen (ret) - 1] = 0; /* waste the trailing null */ + return ret; +} + +/* Be careful to not waste all the memory we allocate for each element. */ + +string +kpathsea_brace_expand (kpathsea kpse, const_string path) +{ + string kpse_dot_expansion; + string elt; + unsigned len; + /* Must do variable expansion first because if we have + foo = .:~ + TEXINPUTS = $foo + we want to end up with TEXINPUTS = .:/home/karl. + Since kpse_path_element is not reentrant, we must get all + the path elements before we start the loop. */ + string xpath = kpathsea_var_expand (kpse, path); + string ret = (string)xmalloc (1); + *ret = 0; + + for (elt = kpathsea_path_element (kpse, xpath); elt; + elt = kpathsea_path_element (kpse, NULL)) { + string save_ret = ret; + /* Do brace expansion first, so tilde expansion happens in {~ka,~kb}. */ + string expansion = kpathsea_brace_expand_element (kpse, elt); + ret = concat3 (ret, expansion, ENV_SEP_STRING); + free (expansion); + free (save_ret); + } + + /* Waste the last byte by overwriting the trailing env_sep with a null. */ + len = strlen (ret); + if (len != 0) + ret[len - 1] = 0; + free (xpath); + + kpse_dot_expansion = kpathsea_expand_kpse_dot (kpse, ret); + if (kpse_dot_expansion != ret) + free (ret); + + return kpse_dot_expansion; +} + +#if defined(KPSE_COMPAT_API) +string +kpse_brace_expand (const_string path) +{ + return kpathsea_brace_expand (kpse_def, path); +} +#endif + +/* Expand all special constructs in a path, and include only the actually + existing directories in the result. */ +string +kpathsea_path_expand (kpathsea kpse, const_string path) +{ + string ret; + string xpath; + string elt; + unsigned len; + const_string ypath; +#if defined(WIN32) + string zpath, p; +#endif + + /* Initialise ret to the empty string. */ + ret = (string)xmalloc (1); + *ret = 0; + len = 0; + +#if defined(WIN32) + zpath = xstrdup (path); + + for (p = zpath; *p; p++) + if (*p == '\\') + *p = '/'; + else if (kpathsea_IS_KANJI(kpse, p)) + p++; + + ypath = zpath; +#else + ypath = path; +#endif + + /* Expand variables and braces first. */ + xpath = kpathsea_brace_expand (kpse, ypath); + +#if defined(WIN32) + free (zpath); +#endif + + /* Now expand each of the path elements, printing the results */ + for (elt = kpathsea_path_element (kpse, xpath); elt; + elt = kpathsea_path_element (kpse, NULL)) { + str_llist_type *dirs; + + /* Skip and ignore magic leading chars. */ + if (*elt == '!' && *(elt + 1) == '!') + elt += 2; + + /* Search the disk for all dirs in the component specified. + Be faster to check the database, but this is more reliable. */ + dirs = kpathsea_element_dirs (kpse, elt); + if (dirs && *dirs) { + str_llist_elt_type *dir; + + for (dir = *dirs; dir; dir = STR_LLIST_NEXT (*dir)) { + string thedir = STR_LLIST (*dir); + unsigned dirlen = strlen (thedir); + string save_ret = ret; + /* We need to retain trailing slash if that's the root directory. + * On unix, "/" is root dir, "" often taken to be current dir. + * On windows, "C:/" is root dir of drive C, and "C:" is current + * on drive C. There's no need to look at other cases, like UNC + * names. + */ + if (dirlen == 1 || (dirlen == 3 && NAME_BEGINS_WITH_DEVICE (thedir) + && IS_DIR_SEP (thedir[2]))) { + ret = concat3 (ret, thedir, ENV_SEP_STRING); + len += dirlen + 1; + ret[len - 1] = ENV_SEP; + } else { + ret = concat (ret, thedir); + len += dirlen; + ret [len - 1] = ENV_SEP; + } + free (save_ret); + } + } + } + /* Get rid of trailing ':', if any. */ + if (len != 0) + ret[len - 1] = 0; + return ret; +} + +#if defined(KPSE_COMPAT_API) +string +kpse_path_expand (const_string path) +{ + return kpathsea_path_expand (kpse_def, path); +} +#endif + +/* ... */ +static void expand_append (str_list_type* partial, + const_string text, const_string p) +{ + string new_string; + unsigned len; + str_list_type tmp; + tmp = str_list_init(); + len = p - text; + new_string = (string)xmalloc(len+1); + strncpy(new_string, text, len); + new_string[len]=0; + str_list_add(&tmp, new_string); + str_list_concat_elements(partial, tmp); +} + + +static str_list_type +brace_expand (kpathsea kpse, const_string *text) +{ + str_list_type result, partial, recurse; + const_string p; + result = str_list_init(); + partial = str_list_init(); + for (p = *text; *p && *p != '}'; ++p) { + /* FIXME: Should be IS_ENV_SEP(*p) */ + if (*p == ENV_SEP || *p == ',') { + expand_append(&partial, *text, p); + str_list_concat(&result, partial); + str_list_free(&partial); + *text = p+1; + partial = str_list_init(); + } else if (*p == '{') { + expand_append(&partial, *text, p); + ++p; + recurse = brace_expand(kpse, &p); + str_list_concat_elements(&partial, recurse); + str_list_free(&recurse); + /* Check for missing closing brace. */ + if (*p != '}') { + WARNING1 ("kpathsea: %s: Unmatched {", *text); + --p; /* undo ++p above for the next iteration, + to avoid potential buffer overrun */ + } + *text = p+1; + } else if (*p == '$') { + /* Skip ${VAR} */ + if (*(p+1) == '{') + for (p+=2; *p!='}';++p); + } +#if defined(WIN32) + else if (kpathsea_IS_KANJI(kpse, p)) + p++; +#endif + } + expand_append(&partial, *text, p); + str_list_concat(&result, partial); + str_list_free(&partial); + *text = p; + return result; +} + + + +#if defined (TEST) +#include <stdio.h> + +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"); +} + +int +main (int argc, char **argv) +{ + char example[256]; + char *result; + + kpse_set_program_name(argv[0], NULL); + result = kpse_brace_expand ("a{\0exebad"); + printf ("%s\n", result); + +#if 0 /* if you want an interactive loop */ + for (;;) + { + 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 = kpse_brace_expand (example); + printf ("%s\n", result); + } +#endif + return 0; + +} + + +#endif /* TEST */ + +/* +Local variables: +standalone-compile-command: "gcc -g -DMAKE_KPSE_DLL -I. -I.. -I$kp -I$kp/.. -DTEST $kp/expand.c .libs/libkpathsea.a" +end: +*/ |