summaryrefslogtreecommitdiff
path: root/Build/source/texk/kpathsea/expand.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/kpathsea/expand.c')
-rw-r--r--Build/source/texk/kpathsea/expand.c417
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:
+*/