1 /* variable.c: variable expansion.
3 Copyright 1993, 1994, 1995, 1996, 2008, 2009, 2011, 2012 Karl Berry.
4 Copyright 1997, 1999, 2001, 2002, 2005 Olaf Weber.
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.
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.
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/>. */
19 #include <kpathsea/config.h>
21 #include <kpathsea/c-ctype.h>
22 #include <kpathsea/cnf.h>
23 #include <kpathsea/fn.h>
24 #include <kpathsea/expand.h>
25 #include <kpathsea/variable.h>
28 /* Here's the simple one, when a program just wants a value. */
31 kpathsea_var_value (kpathsea kpse
, const_string var
)
36 assert (kpse
->program_name
);
38 /* First look for VAR.progname. */
39 vtry
= concat3 (var
, ".", kpse
->program_name
);
40 value
= getenv (vtry
);
43 if (!value
|| !*value
) {
44 /* Now look for VAR_progname. */
45 vtry
= concat3 (var
, "_", kpse
->program_name
);
46 value
= getenv (vtry
);
51 if (!value
|| !*value
)
54 /* Not in the environment; check a config file. */
55 if (!value
|| !*value
)
56 value
= kpathsea_cnf_get (kpse
, var
);
58 /* We have a value; do variable and tilde expansion. We want to use ~
59 in the cnf files, to adapt nicely to Windows and to avoid extra /'s
60 (see tilde.c), but we also want kpsewhich -var-value=foo to not
61 have any literal ~ characters, so our shell scripts don't have to
62 worry about doing the ~ expansion. */
63 ret
= value
? kpathsea_expand (kpse
, value
) : NULL
;
66 if (KPATHSEA_DEBUG_P (KPSE_DEBUG_VARS
))
67 DEBUGF2("variable: %s = %s\n", var
, ret
? ret
: "(nil)");
73 #if defined (KPSE_COMPAT_API)
75 kpse_var_value (const_string var
)
77 return kpathsea_var_value (kpse_def
,var
);
82 /* We have to keep track of variables being expanded, otherwise
83 constructs like TEXINPUTS = $TEXINPUTS result in an infinite loop.
84 (Or indirectly recursive variables, etc.) Our simple solution is to
85 add to a list each time an expansion is started, and check the list
89 expanding (kpathsea kpse
, const_string var
, boolean xp
)
92 for (e
= 0; e
< kpse
->expansion_len
; e
++) {
93 if (STREQ (kpse
->expansions
[e
].var
, var
)) {
94 kpse
->expansions
[e
].expanding
= xp
;
99 /* New variable, add it to the list. */
100 kpse
->expansion_len
++;
101 XRETALLOC (kpse
->expansions
, kpse
->expansion_len
, expansion_type
);
102 kpse
->expansions
[kpse
->expansion_len
- 1].var
= xstrdup (var
);
103 kpse
->expansions
[kpse
->expansion_len
- 1].expanding
= xp
;
107 /* Return whether VAR is currently being expanding. */
110 expanding_p (kpathsea kpse
, const_string var
)
113 for (e
= 0; e
< kpse
->expansion_len
; e
++) {
114 if (STREQ (kpse
->expansions
[e
].var
, var
))
115 return kpse
->expansions
[e
].expanding
;
121 /* Append the result of value of `var' to EXPANSION, where `var' begins
122 at START and ends at END. If `var' is not set, do not complain.
123 Return 1 if `var' was defined, 0 if not. This is a subroutine for
124 the `kpathsea_var_expand' function. */
127 expand (kpathsea kpse
, fn_type
*expansion
,
128 const_string start
, const_string end
)
132 unsigned len
= end
- start
+ 1;
133 string var
= (string
)xmalloc (len
+ 1);
134 strncpy (var
, start
, len
);
137 if (expanding_p (kpse
, var
)) {
138 WARNING1 ("kpathsea: variable `%s' references itself (eventually)", var
);
140 string vtry
= concat3 (var
, "_", kpse
->program_name
);
141 /* Check for an environment variable. */
142 value
= getenv (vtry
);
145 if (!value
|| !*value
)
146 value
= getenv (var
);
148 /* If no envvar, check the config files. */
149 if (!value
|| !*value
)
150 value
= kpathsea_cnf_get (kpse
, var
);
155 expanding (kpse
, var
, true);
156 tmp
= kpathsea_expand (kpse
, value
);
157 expanding (kpse
, var
, false);
159 fn_grow (expansion
, tmp
, strlen (tmp
));
168 /* Can't think of when it would be useful to change these (and the
169 diagnostic messages assume them), but ... */
170 #ifndef IS_VAR_START /* starts all variable references */
171 #define IS_VAR_START(c) ((c) == '$')
173 #ifndef IS_VAR_CHAR /* variable name constituent */
174 #define IS_VAR_CHAR(c) (ISALNUM (c) || (c) == '_')
176 #ifndef IS_VAR_BEGIN_DELIMITER /* start delimited variable name (after $) */
177 #define IS_VAR_BEGIN_DELIMITER(c) ((c) == '{')
179 #ifndef IS_VAR_END_DELIMITER
180 #define IS_VAR_END_DELIMITER(c) ((c) == '}')
184 /* Maybe we should support some or all of the various shell ${...}
185 constructs, especially ${var-value}. We do do ~ expansion. */
188 kpathsea_var_expand (kpathsea kpse
, const_string src
)
193 expansion
= fn_init ();
195 /* Copy everything but variable constructs. */
196 for (s
= src
; *s
; s
++) {
197 if (IS_VAR_START (*s
)) {
200 /* Three cases: `$VAR', `${VAR}', `$<anything-else>'. */
201 if (IS_VAR_CHAR (*s
)) {
202 /* $V: collect name constituents, then expand. */
203 const_string var_end
= s
;
207 } while (IS_VAR_CHAR (*var_end
));
209 var_end
--; /* had to go one past */
210 if (!expand (kpse
, &expansion
, s
, var_end
)) {
211 /* If no expansion, include the literal $x construct,
212 so filenames containing dollar signs can be read.
213 The first +1 is to get the full variable name,
214 the other +1 is to get the dollar sign; we've moved past it. */
215 fn_grow (&expansion
, s
- 1, var_end
- s
+ 1 + 1);
219 } else if (IS_VAR_BEGIN_DELIMITER (*s
)) {
220 /* ${: scan ahead for matching delimiter, then expand. */
221 const_string var_end
= ++s
;
223 while (*var_end
&& !IS_VAR_END_DELIMITER (*var_end
)) {
225 if (IS_KANJI(var_end
))
232 WARNING1 ("kpathsea: %s: No matching } for ${", src
);
233 s
= var_end
- 1; /* will incr to null at top of loop */
235 expand (kpse
, &expansion
, s
, var_end
- 1);
236 s
= var_end
; /* will incr past } at top of loop*/
240 /* $<something-else>: warn, but preserve characters; again, so
241 filenames containing dollar signs can be read. */
242 WARNING2 ("kpathsea: %s: Unrecognized variable construct `$%c'",
244 fn_grow (&expansion
, s
- 1, 2); /* moved past the $ */
247 fn_1grow (&expansion
, *s
);
249 fn_1grow (&expansion
, 0);
251 ret
= FN_STRING (expansion
);
255 #if defined (KPSE_COMPAT_API)
257 kpse_var_expand (const_string src
)
259 return kpathsea_var_expand (kpse_def
,src
);
267 test_var (string test
, string right_answer
)
269 string result
= kpse_var_expand (test
);
271 printf ("expansion of `%s'\t=> %s", test
, result
);
272 if (!STREQ (result
, right_answer
))
273 printf (" [should be `%s']", right_answer
);
279 main (int argc
, char **argv
)
281 kpse_set_program_name(argv
[0], NULL
);
283 test_var ("$foo", "");
284 test_var ("a$foo", "a");
285 test_var ("$foo a", " a");
286 test_var ("a$foo b", "a b");
288 xputenv ("FOO", "foo value");
289 test_var ("a$FOO", "afoo value");
291 xputenv ("Dollar", "$");
292 test_var ("$Dollar a", "$ a");
294 test_var ("a${FOO}b", "afoo valueb");
295 test_var ("a${}b", "ab");
297 test_var ("$$", ""); /* and error */
298 test_var ("a${oops", "a"); /* and error */
308 standalone-compile-command: "gcc -g -I. -I.. -DTEST variable.c kpathsea.a"