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