1 /*
2  * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
3  *
4  * This file is part of Jam - see jam.c for Copyright information.
5  */
6 
7 /*
8  * variable.c - handle jam multi-element variables
9  *
10  * External routines:
11  *
12  *	var_defines() - load a bunch of variable=value settings
13  *	var_string() - expand a string with variables in it
14  *	var_get() - get value of a user defined symbol
15  *	var_set() - set a variable in jam's user defined symbol table
16  *	var_swap() - swap a variable's value with the given one
17  *	var_done() - free variable tables
18  *
19  * Internal routines:
20  *
21  *	var_enter() - make new var symbol table entry, returning var ptr
22  *	var_dump() - dump a variable to stdout
23  *
24  * 04/13/94 (seiwald) - added shorthand L0 for null list pointer
25  * 08/23/94 (seiwald) - Support for '+=' (append to variable)
26  * 01/22/95 (seiwald) - split environment variables at blanks or :'s
27  * 05/10/95 (seiwald) - split path variables at SPLITPATH (not :)
28  * 09/11/00 (seiwald) - defunct var_list() removed
29  * 10/22/02 (seiwald) - list_new() now does its own newstr()/copystr()
30  * 11/04/02 (seiwald) - const-ing for string literals
31  * 06/11/03 (seiwald) - fix var_string mem leak found by Matt Armstrong
32  */
33 
34 # include "jam.h"
35 # include "lists.h"
36 # include "parse.h"
37 # include "variable.h"
38 # include "expand.h"
39 # include "hash.h"
40 # include "newstr.h"
41 
42 static struct hash *varhash = 0;
43 
44 /*
45  * VARIABLE - a user defined multi-value variable
46  */
47 
48 typedef struct _variable VARIABLE ;
49 
50 struct _variable {
51 	const char	*symbol;
52 	LIST		*value;
53 } ;
54 
55 static VARIABLE *var_enter( const char *symbol );
56 static void var_dump( const char *symbol, LIST *value, const char *what );
57 
58 
59 
60 /*
61  * var_defines() - load a bunch of variable=value settings
62  *
63  * If variable name ends in PATH, split value at :'s.
64  * Otherwise, split at blanks.
65  */
66 
67 void
var_defines(const char ** e)68 var_defines( const char **e )
69 {
70 	for( ; *e; e++ )
71 	{
72 	    const char *val;
73 
74 	    /* Just say "no": windows defines this in the env, */
75 	    /* but we don't want it to override our notion of OS. */
76 
77 	    if( !strcmp( *e, "OS=Windows_NT" ) )
78 		continue;
79 
80 # ifdef OS_MAC
81 	    /* On the mac (MPW), the var=val is actually var\0val */
82 	    /* Think different. */
83 
84 	    if( ( val = strchr( *e, '=' ) ) || ( val = *e + strlen( *e ) ) )
85 # else
86 	    if( val = strchr( *e, '=' ) )
87 # endif
88 	    {
89 		LIST *l = L0;
90 		const char *pp, *p;
91 # ifdef OS_MAC
92 		char split = ',';
93 # else
94 		char split = ' ';
95 # endif
96 		char buf[ MAXSYM ];
97 
98 		/* Split *PATH at :'s, not spaces */
99 
100 		if( val - 4 >= *e )
101 		{
102 		    if( !strncmp( val - 4, "PATH", 4 ) ||
103 		        !strncmp( val - 4, "Path", 4 ) ||
104 		        !strncmp( val - 4, "path", 4 ) )
105 			    split = SPLITPATH;
106 		}
107 
108 		/* Do the split */
109 
110 		for( pp = val + 1; p = strchr( pp, split ); pp = p + 1 )
111 		{
112 		    strncpy( buf, pp, p - pp );
113 		    buf[ p - pp ] = '\0';
114 		    l = list_new( l, buf, 0 );
115 		}
116 
117 		l = list_new( l, pp, 0 );
118 
119 		/* Get name */
120 
121 		strncpy( buf, *e, val - *e );
122 		buf[ val - *e ] = '\0';
123 
124 		var_set( buf, l, VAR_SET );
125 	    }
126 	}
127 }
128 
129 /*
130  * var_string() - expand a string with variables in it
131  *
132  * Copies in to out; doesn't modify targets & sources.
133  */
134 
135 int
var_string(const char * in,char * out,int outsize,LOL * lol)136 var_string(
137 	const char *in,
138 	char	*out,
139 	int	outsize,
140 	LOL	*lol )
141 {
142 	char 	*out0 = out;
143 	char	*oute = out + outsize - 1;
144 
145 	while( *in )
146 	{
147 	    char	*lastword;
148 	    int		dollar = 0;
149 
150 	    /* Copy white space */
151 
152 	    while( isspace( *in ) )
153 	    {
154 		if( out >= oute )
155 		    return -1;
156 
157 		*out++ = *in++;
158 	    }
159 
160 	    lastword = out;
161 
162 	    /* Copy non-white space, watching for variables */
163 
164 	    while( *in && !isspace( *in ) )
165 	    {
166 	        if( out >= oute )
167 		    return -1;
168 
169 		if( in[0] == '$' && in[1] == '(' )
170 		    dollar++;
171 
172 		*out++ = *in++;
173 	    }
174 
175 	    /* If a variable encountered, expand it and and embed the */
176 	    /* space-separated members of the list in the output. */
177 
178 	    if( dollar )
179 	    {
180 		LIST *l = var_expand( L0, lastword, out, lol, 0 );
181 		LIST *h = l;
182 
183 		out = lastword;
184 
185 		while( l )
186 		{
187 		    int so = strlen( l->string );
188 
189 		    if( out + so >= oute )
190 			return -1;
191 
192 		    strcpy( out, l->string );
193 		    out += so;
194 
195 		    /* Separate with space */
196 
197 		    if( l = list_next( l ) )
198 			*out++ = ' ';
199 		}
200 
201 		list_free( h );
202 	    }
203 	}
204 
205 	if( out >= oute )
206 	    return -1;
207 
208 	*out++ = '\0';
209 
210 	return out - out0;
211 }
212 
213 /*
214  * var_get() - get value of a user defined symbol
215  *
216  * Returns NULL if symbol unset.
217  */
218 
219 LIST *
var_get(const char * symbol)220 var_get( const char *symbol )
221 {
222 	VARIABLE var, *v = &var;
223 
224 	v->symbol = symbol;
225 
226 	if( varhash && hashcheck( varhash, (HASHDATA **)&v ) )
227 	{
228 	    if( DEBUG_VARGET )
229 		var_dump( v->symbol, v->value, "get" );
230 	    return v->value;
231 	}
232 
233 	return 0;
234 }
235 
236 /*
237  * var_set() - set a variable in jam's user defined symbol table
238  *
239  * 'flag' controls the relationship between new and old values of
240  * the variable: SET replaces the old with the new; APPEND appends
241  * the new to the old; DEFAULT only uses the new if the variable
242  * was previously unset.
243  *
244  * Copies symbol.  Takes ownership of value.
245  */
246 
247 void
var_set(const char * symbol,LIST * value,int flag)248 var_set(
249 	const char *symbol,
250 	LIST	*value,
251 	int	flag )
252 {
253 	VARIABLE *v = var_enter( symbol );
254 
255 	if( DEBUG_VARSET )
256 	    var_dump( symbol, value, "set" );
257 
258 	switch( flag )
259 	{
260 	case VAR_SET:
261 	    /* Replace value */
262 	    list_free( v->value );
263 	    v->value = value;
264 	    break;
265 
266 	case VAR_APPEND:
267 	    /* Append value */
268 	    v->value = list_append( v->value, value );
269 	    break;
270 
271 	case VAR_DEFAULT:
272 	    /* Set only if unset */
273 	    if( !v->value )
274 		v->value = value;
275 	    else
276 		list_free( value );
277 	    break;
278 	}
279 }
280 
281 /*
282  * var_swap() - swap a variable's value with the given one
283  */
284 
285 LIST *
var_swap(const char * symbol,LIST * value)286 var_swap(
287 	const char *symbol,
288 	LIST	*value )
289 {
290 	VARIABLE *v = var_enter( symbol );
291 	LIST 	 *oldvalue = v->value;
292 
293 	if( DEBUG_VARSET )
294 	    var_dump( symbol, value, "set" );
295 
296 	v->value = value;
297 
298 	return oldvalue;
299 }
300 
301 
302 
303 /*
304  * var_enter() - make new var symbol table entry, returning var ptr
305  */
306 
307 static VARIABLE *
var_enter(const char * symbol)308 var_enter( const char *symbol )
309 {
310 	VARIABLE var, *v = &var;
311 
312 	if( !varhash )
313 	    varhash = hashinit( sizeof( VARIABLE ), "variables" );
314 
315 	v->symbol = symbol;
316 	v->value = 0;
317 
318 	if( hashenter( varhash, (HASHDATA **)&v ) )
319 	    v->symbol = newstr( symbol );	/* never freed */
320 
321 	return v;
322 }
323 
324 /*
325  * var_dump() - dump a variable to stdout
326  */
327 
328 static void
var_dump(const char * symbol,LIST * value,const char * what)329 var_dump(
330 	const char	*symbol,
331 	LIST		*value,
332 	const char	*what )
333 {
334 	printf( "%s %s = ", what, symbol );
335 	list_print( value );
336 	printf( "\n" );
337 }
338 
339 /*
340  * var_done() - free variable tables
341  */
342 
343 void
var_done()344 var_done()
345 {
346 	hashdone( varhash );
347 }
348