Back to index

tetex-bin  3.0
variable.c
Go to the documentation of this file.
00001 /* variable.c: variable expansion.
00002 
00003 Copyright (C) 1993, 94, 95, 96 Karl Berry.
00004 
00005 This library is free software; you can redistribute it and/or
00006 modify it under the terms of the GNU Library General Public
00007 License as published by the Free Software Foundation; either
00008 version 2 of the License, or (at your option) any later version.
00009 
00010 This library is distributed in the hope that it will be useful,
00011 but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 Library General Public License for more details.
00014 
00015 You should have received a copy of the GNU Library General Public
00016 License along with this library; if not, write to the Free Software
00017 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
00018 
00019 #include <kpathsea/config.h>
00020 
00021 #include <kpathsea/c-ctype.h>
00022 #include <kpathsea/cnf.h>
00023 #include <kpathsea/fn.h>
00024 #include <kpathsea/variable.h>
00025 
00026 
00027 /* Here's the simple one, when a program just wants a value.  */
00028 
00029 string
00030 kpse_var_value P1C(const_string, var)
00031 {
00032   string vtry, ret;
00033 
00034   assert(kpse_program_name);
00035 
00036   /* First look for VAR.progname. */
00037   vtry = concat3(var, ".", kpse_program_name);
00038   ret = getenv (vtry);
00039   free (vtry);
00040 
00041   if (!ret || !*ret) {
00042     /* Now look for VAR_progname. */
00043     vtry = concat3(var, "_", kpse_program_name);
00044     ret = getenv (vtry);
00045     free (vtry);
00046   }
00047 
00048   if (!ret || !*ret)
00049     ret = getenv (var);
00050 
00051   if (!ret || !*ret)
00052     ret = kpse_cnf_get (var);
00053 
00054   if (ret)
00055     ret = kpse_var_expand (ret);
00056 
00057 #ifdef KPSE_DEBUG
00058   if (KPSE_DEBUG_P (KPSE_DEBUG_VARS))
00059     DEBUGF2("variable: %s = %s\n", var, ret ? ret : "(nil)");
00060 #endif
00061 
00062   return ret;
00063 }
00064 
00065 /* We have to keep track of variables being expanded, otherwise
00066    constructs like TEXINPUTS = $TEXINPUTS result in an infinite loop.
00067    (Or indirectly recursive variables, etc.)  Our simple solution is to
00068    add to a list each time an expansion is started, and check the list
00069    before expanding.  */
00070 
00071 typedef struct {
00072   const_string var;
00073   boolean expanding;
00074 } expansion_type;
00075 static expansion_type *expansions; /* The sole variable of this type.  */
00076 static unsigned expansion_len = 0;
00077 
00078 static void
00079 expanding P2C(const_string, var,  boolean, xp)
00080 {
00081   unsigned e;
00082   for (e = 0; e < expansion_len; e++) {
00083     if (STREQ (expansions[e].var, var)) {
00084       expansions[e].expanding = xp;
00085       return;
00086     }
00087   }
00088 
00089   /* New variable, add it to the list.  */
00090   expansion_len++;
00091   XRETALLOC (expansions, expansion_len, expansion_type);
00092   expansions[expansion_len - 1].var = xstrdup (var);
00093   expansions[expansion_len - 1].expanding = xp;
00094 }
00095 
00096 
00097 /* Return whether VAR is currently being expanding.  */
00098 
00099 static boolean
00100 expanding_p P1C(const_string, var)
00101 {
00102   unsigned e;
00103   for (e = 0; e < expansion_len; e++) {
00104     if (STREQ (expansions[e].var, var))
00105       return expansions[e].expanding;
00106   }
00107   
00108   return false;
00109 }
00110 
00111 /* Append the result of value of `var' to EXPANSION, where `var' begins
00112    at START and ends at END.  If `var' is not set, do not complain.
00113    This is a subroutine for the more complicated expansion function.  */
00114 
00115 static void
00116 expand P3C(fn_type *, expansion,  const_string, start,  const_string, end)
00117 {
00118   string value;
00119   unsigned len = end - start + 1;
00120   string var = (string)xmalloc (len + 1);
00121   strncpy (var, start, len);
00122   var[len] = 0;
00123   
00124   if (expanding_p (var)) {
00125     WARNING1 ("kpathsea: variable `%s' references itself (eventually)", var);
00126   } else {
00127     string vtry = concat3 (var, "_", kpse_program_name);
00128     /* Check for an environment variable.  */
00129     value = getenv (vtry);
00130     free (vtry);
00131     
00132     if (!value || !*value)
00133       value = getenv (var);
00134 
00135     /* If no envvar, check the config files.  */
00136     if (!value || !*value)
00137       value = kpse_cnf_get (var);
00138 
00139     if (value) {
00140       expanding (var, true);
00141       value = kpse_var_expand (value);
00142       expanding (var, false);
00143       fn_grow (expansion, value, strlen (value));
00144       free (value);
00145     }
00146 
00147     free (var);
00148   }
00149 }
00150 
00151 /* Can't think of when it would be useful to change these (and the
00152    diagnostic messages assume them), but ... */
00153 #ifndef IS_VAR_START /* starts all variable references */
00154 #define IS_VAR_START(c) ((c) == '$')
00155 #endif
00156 #ifndef IS_VAR_CHAR  /* variable name constituent */
00157 #define IS_VAR_CHAR(c) (ISALNUM (c) || (c) == '_')
00158 #endif
00159 #ifndef IS_VAR_BEGIN_DELIMITER /* start delimited variable name (after $) */
00160 #define IS_VAR_BEGIN_DELIMITER(c) ((c) == '{')
00161 #endif
00162 #ifndef IS_VAR_END_DELIMITER
00163 #define IS_VAR_END_DELIMITER(c) ((c) == '}')
00164 #endif
00165 
00166 
00167 /* Maybe we should support some or all of the various shell ${...}
00168    constructs, especially ${var-value}.  */
00169 
00170 string
00171 kpse_var_expand P1C(const_string, src)
00172 {
00173   const_string s;
00174   string ret;
00175   fn_type expansion;
00176   expansion = fn_init ();
00177   
00178   /* Copy everything but variable constructs.  */
00179   for (s = src; *s; s++) {
00180     if (IS_VAR_START (*s)) {
00181       s++;
00182 
00183       /* Three cases: `$VAR', `${VAR}', `$<anything-else>'.  */
00184       if (IS_VAR_CHAR (*s)) {
00185         /* $V: collect name constituents, then expand.  */
00186         const_string var_end = s;
00187 
00188         do {
00189           var_end++;
00190         } while (IS_VAR_CHAR (*var_end));
00191 
00192         var_end--; /* had to go one past */
00193         expand (&expansion, s, var_end);
00194         s = var_end;
00195 
00196       } else if (IS_VAR_BEGIN_DELIMITER (*s)) {
00197         /* ${: scan ahead for matching delimiter, then expand.  */
00198         const_string var_end = ++s;
00199 
00200         while (*var_end && !IS_VAR_END_DELIMITER (*var_end))
00201           var_end++;
00202 
00203         if (! *var_end) {
00204           WARNING1 ("%s: No matching } for ${", src);
00205           s = var_end - 1; /* will incr to null at top of loop */
00206         } else {
00207           expand (&expansion, s, var_end - 1);
00208           s = var_end; /* will incr past } at top of loop*/
00209         }
00210 
00211       } else {
00212         /* $<something-else>: error.  */
00213         WARNING2 ("%s: Unrecognized variable construct `$%c'", src, *s);
00214         /* Just ignore those chars and keep going.  */
00215       }
00216     } else
00217      fn_1grow (&expansion, *s);
00218   }
00219   fn_1grow (&expansion, 0);
00220           
00221   ret = FN_STRING (expansion);
00222   return ret;
00223 }
00224 
00225 #ifdef TEST
00226 
00227 static void
00228 test_var (string test, string right_answer)
00229 {
00230   string result = kpse_var_expand (test);
00231   
00232   printf ("expansion of `%s'\t=> %s", test, result);
00233   if (!STREQ (result, right_answer))
00234     printf (" [should be `%s']", right_answer);
00235   putchar ('\n');
00236 }
00237 
00238 
00239 int
00240 main ()
00241 {
00242   test_var ("a", "a");
00243   test_var ("$foo", "");
00244   test_var ("a$foo", "a");
00245   test_var ("$foo a", " a");
00246   test_var ("a$foo b", "a b");
00247 
00248   xputenv ("FOO", "foo value");
00249   test_var ("a$FOO", "afoo value");
00250 
00251   xputenv ("Dollar", "$");
00252   test_var ("$Dollar a", "$ a");
00253 
00254   test_var ("a${FOO}b", "afoo valueb");
00255   test_var ("a${}b", "ab");
00256 
00257   test_var ("$$", ""); /* and error */
00258   test_var ("a${oops", "a"); /* and error */
00259 
00260   return 0;
00261 }
00262 
00263 #endif /* TEST */
00264 
00265 
00266 /*
00267 Local variables:
00268 standalone-compile-command: "gcc -g -I. -I.. -DTEST variable.c kpathsea.a"
00269 End:
00270 */