dvitomp fix from Akira
[mplib] / src / texk / kpathsea / variable.c
1 /* variable.c: variable expansion.
2
3     Copyright 1993, 1994, 1995, 1996, 2008 Karl Berry.
4     Copyright 1997, 1999, 2001, 2002, 2005 Olaf Weber.
5
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.
10
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.
15  
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/>.  */
18
19 #include <kpathsea/config.h>
20
21 #include <kpathsea/c-ctype.h>
22 #include <kpathsea/cnf.h>
23 #include <kpathsea/fn.h>
24 #include <kpathsea/tilde.h>
25 #include <kpathsea/variable.h>
26
27
28 /* Here's the simple one, when a program just wants a value.  */
29
30 string
31 kpse_var_value P1C(const_string, var)
32 {
33   string vtry, ret;
34
35   assert (kpse_program_name);
36
37   /* First look for VAR.progname. */
38   vtry = concat3 (var, ".", kpse_program_name);
39   ret = getenv (vtry);
40   free (vtry);
41
42   if (!ret || !*ret) {
43     /* Now look for VAR_progname. */
44     vtry = concat3 (var, "_", kpse_program_name);
45     ret = getenv (vtry);
46     free (vtry);
47   }
48
49   /* Just plain VAR.  */
50   if (!ret || !*ret)
51     ret = getenv (var);
52
53   /* Not in the environment; check a config file.  */
54   if (!ret || !*ret)
55     ret = kpse_cnf_get (var);
56
57   /* We have a value; do variable and tilde expansion.  We want to use ~
58      in the cnf files, to adapt nicely to Windows and to avoid extra /'s
59      (see tilde.c), but we also want kpsewhich -var-value=foo to not
60      have any literal ~ characters, so our shell scripts don't have to
61      worry about doing the ~ expansion.  */
62   if (ret) {
63     string tmp = kpse_var_expand (ret);
64     /* We don't want to free the previous value of ret here; apparently
65        it's used later, somewhere, somehow.  (The end result was a crash
66        when making tex.fmt.)  Sigh.  */
67     ret = kpse_tilde_expand (tmp);
68     if (ret != tmp) {
69       free (tmp);
70     }
71   }
72
73 #ifdef KPSE_DEBUG
74   if (KPSE_DEBUG_P (KPSE_DEBUG_VARS))
75     DEBUGF2("variable: %s = %s\n", var, ret ? ret : "(nil)");
76 #endif
77
78   return ret;
79 }
80 \f
81 /* We have to keep track of variables being expanded, otherwise
82    constructs like TEXINPUTS = $TEXINPUTS result in an infinite loop.
83    (Or indirectly recursive variables, etc.)  Our simple solution is to
84    add to a list each time an expansion is started, and check the list
85    before expanding.  */
86
87 typedef struct {
88   const_string var;
89   boolean expanding;
90 } expansion_type;
91 static expansion_type *expansions; /* The sole variable of this type.  */
92 static unsigned expansion_len = 0;
93
94 static void
95 expanding P2C(const_string, var,  boolean, xp)
96 {
97   unsigned e;
98   for (e = 0; e < expansion_len; e++) {
99     if (STREQ (expansions[e].var, var)) {
100       expansions[e].expanding = xp;
101       return;
102     }
103   }
104
105   /* New variable, add it to the list.  */
106   expansion_len++;
107   XRETALLOC (expansions, expansion_len, expansion_type);
108   expansions[expansion_len - 1].var = xstrdup (var);
109   expansions[expansion_len - 1].expanding = xp;
110 }
111
112
113 /* Return whether VAR is currently being expanding.  */
114
115 static boolean
116 expanding_p P1C(const_string, var)
117 {
118   unsigned e;
119   for (e = 0; e < expansion_len; e++) {
120     if (STREQ (expansions[e].var, var))
121       return expansions[e].expanding;
122   }
123   
124   return false;
125 }
126 \f
127 /* Append the result of value of `var' to EXPANSION, where `var' begins
128    at START and ends at END.  If `var' is not set, do not complain.
129    This is a subroutine for the more complicated expansion function.  */
130
131 static void
132 expand P3C(fn_type *, expansion,  const_string, start,  const_string, end)
133 {
134   string value;
135   unsigned len = end - start + 1;
136   string var = (string)xmalloc (len + 1);
137   strncpy (var, start, len);
138   var[len] = 0;
139   
140   if (expanding_p (var)) {
141     WARNING1 ("kpathsea: variable `%s' references itself (eventually)", var);
142   } else {
143     string vtry = concat3 (var, "_", kpse_program_name);
144     /* Check for an environment variable.  */
145     value = getenv (vtry);
146     free (vtry);
147     
148     if (!value || !*value)
149       value = getenv (var);
150
151     /* If no envvar, check the config files.  */
152     if (!value || !*value)
153       value = kpse_cnf_get (var);
154
155     if (value) {
156       expanding (var, true);
157       value = kpse_var_expand (value);
158       expanding (var, false);
159       
160       { /* Do tilde expansion; see explanation above in kpse_var_value.  */
161         string tmp = kpse_tilde_expand (value);
162         if (value != tmp) {
163           free (value);
164           value = tmp;
165         }
166       }
167       
168       fn_grow (expansion, value, strlen (value));
169       free (value);
170     }
171
172     free (var);
173   }
174 }
175 \f
176 /* Can't think of when it would be useful to change these (and the
177    diagnostic messages assume them), but ... */
178 #ifndef IS_VAR_START /* starts all variable references */
179 #define IS_VAR_START(c) ((c) == '$')
180 #endif
181 #ifndef IS_VAR_CHAR  /* variable name constituent */
182 #define IS_VAR_CHAR(c) (ISALNUM (c) || (c) == '_')
183 #endif
184 #ifndef IS_VAR_BEGIN_DELIMITER /* start delimited variable name (after $) */
185 #define IS_VAR_BEGIN_DELIMITER(c) ((c) == '{')
186 #endif
187 #ifndef IS_VAR_END_DELIMITER
188 #define IS_VAR_END_DELIMITER(c) ((c) == '}')
189 #endif
190
191
192 /* Maybe we should support some or all of the various shell ${...}
193    constructs, especially ${var-value}.  We do do ~ expansion.  */
194
195 string
196 kpse_var_expand P1C(const_string, src)
197 {
198   const_string s;
199   string ret;
200   fn_type expansion;
201   expansion = fn_init ();
202   
203   /* Copy everything but variable constructs.  */
204   for (s = src; *s; s++) {
205     if (IS_VAR_START (*s)) {
206       s++;
207
208       /* Three cases: `$VAR', `${VAR}', `$<anything-else>'.  */
209       if (IS_VAR_CHAR (*s)) {
210         /* $V: collect name constituents, then expand.  */
211         const_string var_end = s;
212
213         do {
214           var_end++;
215         } while (IS_VAR_CHAR (*var_end));
216
217         var_end--; /* had to go one past */
218         expand (&expansion, s, var_end);
219         s = var_end;
220
221       } else if (IS_VAR_BEGIN_DELIMITER (*s)) {
222         /* ${: scan ahead for matching delimiter, then expand.  */
223         const_string var_end = ++s;
224
225         while (*var_end && !IS_VAR_END_DELIMITER (*var_end))
226           var_end++;
227
228         if (! *var_end) {
229           WARNING1 ("%s: No matching } for ${", src);
230           s = var_end - 1; /* will incr to null at top of loop */
231         } else {
232           expand (&expansion, s, var_end - 1);
233           s = var_end; /* will incr past } at top of loop*/
234         }
235
236       } else {
237         /* $<something-else>: error.  */
238         WARNING2 ("%s: Unrecognized variable construct `$%c'", src, *s);
239         /* Just ignore those chars and keep going.  */
240       }
241     } else
242      fn_1grow (&expansion, *s);
243   }
244   fn_1grow (&expansion, 0);
245           
246   ret = FN_STRING (expansion);
247   return ret;
248 }
249 \f
250 #ifdef TEST
251
252 static void
253 test_var (string test, string right_answer)
254 {
255   string result = kpse_var_expand (test);
256   
257   printf ("expansion of `%s'\t=> %s", test, result);
258   if (!STREQ (result, right_answer))
259     printf (" [should be `%s']", right_answer);
260   putchar ('\n');
261 }
262
263
264 int
265 main ()
266 {
267   test_var ("a", "a");
268   test_var ("$foo", "");
269   test_var ("a$foo", "a");
270   test_var ("$foo a", " a");
271   test_var ("a$foo b", "a b");
272
273   xputenv ("FOO", "foo value");
274   test_var ("a$FOO", "afoo value");
275
276   xputenv ("Dollar", "$");
277   test_var ("$Dollar a", "$ a");
278
279   test_var ("a${FOO}b", "afoo valueb");
280   test_var ("a${}b", "ab");
281
282   test_var ("$$", ""); /* and error */
283   test_var ("a${oops", "a"); /* and error */
284
285   return 0;
286 }
287
288 #endif /* TEST */
289
290
291 /*
292 Local variables:
293 standalone-compile-command: "gcc -g -I. -I.. -DTEST variable.c kpathsea.a"
294 End:
295 */