summaryrefslogtreecommitdiff
path: root/Build/source/texk/contrib/variables.c
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2006-01-17 21:41:51 +0000
committerKarl Berry <karl@freefriends.org>2006-01-17 21:41:51 +0000
commit487ca4806cc046076293cf6cc5fbba0db282bac7 (patch)
tree847b412ab5158dd7bdd7ed7e5a4cc3fbca94be32 /Build/source/texk/contrib/variables.c
parenta3d3111bfe26b8e5f5bc6049dfb2a4ca2edc7881 (diff)
texk 1
git-svn-id: svn://tug.org/texlive/trunk@1485 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/contrib/variables.c')
-rw-r--r--Build/source/texk/contrib/variables.c472
1 files changed, 472 insertions, 0 deletions
diff --git a/Build/source/texk/contrib/variables.c b/Build/source/texk/contrib/variables.c
new file mode 100644
index 00000000000..d1837bd6b4b
--- /dev/null
+++ b/Build/source/texk/contrib/variables.c
@@ -0,0 +1,472 @@
+/* variables.c: this package provides symbols and dynamic strings
+
+Copyright (C) 1997 Fabrice POPINEAU.
+
+Time-stamp: <03/02/21 11:54:23 popineau>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "stackenv.h"
+#include "fileutils.h"
+#include "variables.h"
+#include <regex.h>
+
+/*
+ We will use kpathsea's hash tables
+ */
+
+/* The symbol table */
+hash_table_type *symtab = NULL;
+
+/* Initialize a symbol table to store all the vars used by
+ the various shell scripts. */
+void init_vars()
+{
+
+ symtab = hash_create(719, hashtable_symtab);
+
+}
+
+void
+hash_remove_all (hash_table_type *table, string key)
+{
+ string *val;
+ while ((val = hash_lookup(table, key))) {
+ hash_remove(table, key, *val);
+ free (val);
+ }
+}
+
+string getval(string name)
+{
+ string *lookup = NULL;
+ string res = NULL;
+
+ /*
+ Either the name is already in the symbol table ...
+ */
+ if ((lookup = hash_lookup(symtab, name)) != NULL) {
+ /* FIXME: we return the first value ... */
+ res = lookup[0];
+ free(lookup);
+ }
+#if 0
+ else if ((lookup = hash_lookup(symtab, vname)) != NULL) {
+ /* FIXME: we return the first value ... */
+ res = lookup[0];
+ free(lookup);
+ }
+ else {
+ string dollar_name = concat("$", vname);
+ res = kpse_cnf_get(vname);
+ free(dollar_name);
+ }
+#endif
+ if (!res) {
+#if 0
+ /*
+ ... Either it is known from kpathsea, in which case we put it
+ in the symbol table.
+ */
+ string dollar_name = concat("$", vname);
+ res = expand_var(dollar_name);
+
+ if (test_file('n', res)) {
+ setval(vname, res);
+ }
+ else {
+ /* res = "", so return NULL */
+ res = NULL;
+ }
+ free (dollar_name);
+#endif
+ res = getenv(name);
+ }
+
+ if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) {
+ fprintf(stderr, "GetVal %s => %s\n", name, res);
+ }
+
+ /* In any case ... */
+ return res;
+}
+
+string setval(string name, string value)
+{
+ /* Be safe ... */
+ if (!value) value = "";
+ hash_remove_all(symtab, name);
+ hash_insert(symtab, name, value);
+ if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) {
+ fprintf(stderr, "SetVal %s <= %s\n", name, value);
+ }
+
+ return value;
+}
+
+string setval_default(string name, string value)
+{
+ string old_value;
+ /* Be safe ... */
+ if (!value) {
+ if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) {
+ fprintf(stderr, "setval_default: inserting NULL value for %s!\n", name);
+ }
+ value = "";
+ }
+
+ if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) {
+ fprintf(stderr, "SetVal default %s ... ", name);
+ }
+
+ /* The mktex... scripts NEVER use ${foo:=bar}, always ${foo=bar}.
+ So we only need to set NAME=VALUE if $NAME is unset. */
+ if ((old_value = getval(name)) == NULL) {
+ hash_insert(symtab, name, value);
+ if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) {
+ fprintf(stderr, "SetVal %s <= %s\n", name, value);
+ }
+ }
+ else
+ value = old_value;
+
+ return value;
+}
+
+string *grep(char *regexp, char *line, int num_vars)
+{
+ struct re_pattern_buffer *rc;
+ struct re_registers *p;
+ const_string ok;
+ string *vars = NULL;
+ string *lookup;
+ int i;
+
+ if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) {
+ fprintf(stderr, "Grep\n\t%s\n\tin\n\t%s\n", regexp, line);
+ }
+
+ if (test_file('z', line))
+ return NULL;
+
+ /* This will retrieve the precompiled regexp or compile it and
+ remember it. vars contains the strings matched, num_vars the number
+ of these strings. */
+#if 0
+ if ((lookup = hash_lookup(symtab, regexp)))
+ rc = (struct re_pattern_buffer *)lookup[0];
+ else
+ rc = NULL;
+ if (rc == NULL) {
+#endif
+ /* Compile the regexp and stores the result */
+
+ if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) {
+ fprintf(stderr, "\tCompiling the regexp\n");
+ }
+
+ re_syntax_options = RE_SYNTAX_POSIX_EGREP;
+ rc = (struct re_pattern_buffer *) calloc(1, sizeof(struct re_pattern_buffer));
+ rc->regs_allocated = REGS_UNALLOCATED;
+ if ((ok = re_compile_pattern(regexp, strlen(regexp), rc)) != 0)
+ FATAL1("Can't compile regex %s\n", regexp);
+#if 0
+ hash_remove_all(symtab, regexp);
+ hash_insert(symtab, regexp, (char *)rc);
+ }
+ else if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) {
+ fprintf(stderr, "\tAlready compiled\n");
+ }
+#endif
+
+ p = (struct re_registers *) calloc(1, sizeof(struct re_registers));
+ p->num_regs = num_vars;
+ if ((re_match(rc, line, strlen(line), 0, p)) > 0) {
+ vars = (char **) xmalloc ((num_vars+1) * sizeof(char *));
+ for (i = 0; i <= num_vars; i++) {
+ vars[i] = malloc((p->end[i] - p->start[i] + 1)*sizeof(char));
+ strncpy(vars[i], line+p->start[i], p->end[i] - p->start[i]);
+ vars[i][p->end[i] - p->start[i]] = '\0';
+ }
+ }
+ free (p);
+ if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) {
+ if (vars)
+ for(i = 0; i <= num_vars; i++)
+ fprintf(stderr, "String %d matches %s\n", i, vars[i]);
+ }
+ return vars;
+}
+
+/* read a line, trying to find 'variable = value'
+ Currently groks two constructs:
+ VAR=value
+ : {VAR=value}
+ using regex to analyze them.
+ The variable is added to the symbol table. In the first case, it
+ overrides an old value, not in the second case.
+ */
+boolean parse_variable(string line)
+{
+ char **vars;
+
+ static char *r1 = "^[[:space:]]*([[:alnum:]_]+)=[[:space:]]*([^[:space:]]*)[[:space:]]*.*$";
+ static char *r2 = "^:[[:space:]]+\\$\\{([[:alnum:]_]+)=([^\\}]*)\\}[[:space:]]*.*$";
+
+ if ((vars = grep(r1, line, 2))) {
+ setval(vars[1], vars[2]);
+ return true;
+ }
+ else if ((vars = grep(r2, line, 2))) {
+ setval_default(vars[1], vars[2]);
+ return true;
+ }
+ else
+ WARNING1("The following line has not been parsed:\n%s\n", line);
+ return false;
+}
+
+/* Substitute from by to in line */
+string subst(string line, string from, string to)
+{
+ string before, after, p;
+
+ if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) {
+ fprintf(stderr, "Substituting %s by %s in %s\n", from, to, line);
+ }
+
+ if ((p = strstr(line, from))) {
+ before = xstrdup(line);
+ before[p-line] = '\0';
+ after = p+strlen(from);
+ p = concat3(before, to , after);
+ free (before);
+ }
+ else
+ p = xstrdup(line);
+
+ if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) {
+ fprintf(stderr, "=> gives %s\n", p);
+ }
+
+ return p;
+}
+
+/* kpse_expand_var does not remove leading !!, but we do not need
+ them in mktex*
+ (Parts of code shamelessly stolen from remove_dbonly on tex-file.c.)
+ */
+string expand_var(const_string s)
+{
+ string p, q;
+ string res = NULL;
+
+ if (s) {
+ boolean new_elt = true;
+
+ res = mktex_var_expand(s);
+
+ for (p = q = res; *p; ) {
+ if (new_elt && *p == '!' && p[1] == '!')
+ p += 2;
+ else {
+ new_elt = *p == ENV_SEP;
+ *q++ = *p++;
+ }
+ }
+ *q = '\0';
+
+ if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) {
+ fprintf(stderr, "Expanding variable %s into %s\n", s, res);
+ }
+ }
+
+ return res;
+}
+
+/*
+ This is shamelessly stolen from kpathsea/variable.c .
+ We want to do expansion without having to put variables in the
+ environment.
+ */
+
+/* We have to keep track of variables being expanded, otherwise
+ constructs like TEXINPUTS = $TEXINPUTS result in an infinite loop.
+ (Or indirectly recursive variables, etc.) Our simple solution is to
+ add to a list each time an expansion is started, and check the list
+ before expanding. */
+
+typedef struct {
+ const_string var;
+ boolean expanding;
+} expansion_type;
+static expansion_type *expansions; /* The sole variable of this type. */
+static unsigned expansion_len = 0;
+
+static void
+expanding P2C(const_string, var, boolean, xp)
+{
+ unsigned e;
+ for (e = 0; e < expansion_len; e++) {
+ if (STREQ (expansions[e].var, var)) {
+ expansions[e].expanding = xp;
+ return;
+ }
+ }
+
+ /* New variable, add it to the list. */
+ expansion_len++;
+ XRETALLOC (expansions, expansion_len, expansion_type);
+ expansions[expansion_len - 1].var = xstrdup (var);
+ expansions[expansion_len - 1].expanding = xp;
+}
+
+
+/* Return whether VAR is currently being expanding. */
+
+static boolean
+expanding_p P1C(const_string, var)
+{
+ unsigned e;
+ for (e = 0; e < expansion_len; e++) {
+ if (STREQ (expansions[e].var, var))
+ return expansions[e].expanding;
+ }
+
+ return false;
+}
+
+/* Append the result of value of `var' to EXPANSION, where `var' begins
+ at START and ends at END. If `var' is not set, do not complain.
+ This is a subroutine for the more complicated expansion function. */
+
+static void
+expand P3C(fn_type *, expansion, const_string, start, const_string, end)
+{
+ string value;
+ unsigned len = end - start + 1;
+ string var = xmalloc (len + 1);
+ strncpy (var, start, len);
+ var[len] = 0;
+
+ if (expanding_p (var)) {
+ WARNING1 ("kpathsea: variable `%s' references itself (eventually)", var);
+ } else {
+#if 1
+ /* The mktex.c replacement for shell scripts needs a more sophisticated
+ lookup for variables */
+ value = getval(var);
+#else
+ /* Check for an environment variable. */
+ value = getenv (var);
+#endif
+ /* If no envvar, check the config files. */
+ if (!value)
+ value = kpse_cnf_get (var);
+
+ if (value) {
+ expanding (var, true);
+ value = mktex_var_expand (value);
+ expanding (var, false);
+ fn_grow (expansion, value, strlen (value));
+ free (value);
+ }
+
+ free (var);
+ }
+}
+
+/* Can't think of when it would be useful to change these (and the
+ diagnostic messages assume them), but ... */
+#ifndef IS_VAR_START /* starts all variable references */
+#define IS_VAR_START(c) ((c) == '$')
+#endif
+#ifndef IS_VAR_CHAR /* variable name constituent */
+#define IS_VAR_CHAR(c) (ISALNUM (c) || (c) == '_')
+#endif
+#ifndef IS_VAR_BEGIN_DELIMITER /* start delimited variable name (after $) */
+#define IS_VAR_BEGIN_DELIMITER(c) ((c) == '{')
+#endif
+#ifndef IS_VAR_END_DELIMITER
+#define IS_VAR_END_DELIMITER(c) ((c) == '}')
+#endif
+
+
+/* Maybe we should support some or all of the various shell ${...}
+ constructs, especially ${var-value}. */
+
+string
+mktex_var_expand P1C(const_string, src)
+{
+ const_string s;
+ string ret;
+ fn_type expansion;
+ expansion = fn_init ();
+
+ /* Copy everything but variable constructs. */
+ for (s = src; *s; s++) {
+ if (IS_VAR_START (*s)) {
+ s++;
+
+ /* Three cases: `$VAR', `${VAR}', `$<anything-else>'. */
+ if (IS_VAR_CHAR (*s)) {
+ /* $V: collect name constituents, then expand. */
+ const_string var_end = s;
+
+ do {
+ var_end++;
+ } while (IS_VAR_CHAR (*var_end));
+
+ var_end--; /* had to go one past */
+ expand (&expansion, s, var_end);
+ s = var_end;
+
+ } else if (IS_VAR_BEGIN_DELIMITER (*s)) {
+ /* ${: scan ahead for matching delimiter, then expand. */
+ const_string var_end = ++s;
+
+ while (*var_end && !IS_VAR_END_DELIMITER (*var_end))
+ var_end++;
+
+ if (! *var_end) {
+ WARNING1 ("%s: No matching } for ${", src);
+ s = var_end - 1; /* will incr to null at top of loop */
+ } else {
+ expand (&expansion, s, var_end - 1);
+ s = var_end; /* will incr past } at top of loop*/
+ }
+
+ } else {
+ /* $<something-else>: error. */
+ WARNING2 ("%s: Unrecognized variable construct `$%c'", src, *s);
+ /* Just ignore those chars and keep going. */
+ }
+ } else
+ fn_1grow (&expansion, *s);
+ }
+ fn_1grow (&expansion, 0);
+
+ ret = FN_STRING (expansion);
+ return ret;
+}
+
+#ifdef TEST
+
+main()
+{
+ char buf[256];
+ while (gets(buf)) parse_variable(buf);
+}
+#endif