2 * Copyright 1993, 2000 Christopher Seiwald.
4 * This file is part of Jam - see jam.c for Copyright information.
8 * Copyright 2001-2004 David Abrahams.
9 * Copyright 2005 Reece H. Dunn.
10 * Copyright 2005 Rene Rivera.
11 * Distributed under the Boost Software License, Version 1.0.
12 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
29 * variable.c - handle Jam multi-element variables.
33 * var_defines() - load a bunch of variable=value settings.
34 * var_string() - expand a string with variables in it.
35 * var_get() - get value of a user defined symbol.
36 * var_set() - set a variable in jam's user defined symbol table.
37 * var_swap() - swap a variable's value with the given one.
38 * var_done() - free variable tables.
42 * var_enter() - make new var symbol table entry, returning var ptr.
43 * var_dump() - dump a variable to stdout.
45 * 04/13/94 (seiwald) - added shorthand L0 for null list pointer
46 * 08/23/94 (seiwald) - Support for '+=' (append to variable)
47 * 01/22/95 (seiwald) - split environment variables at blanks or :'s
48 * 05/10/95 (seiwald) - split path variables at SPLITPATH (not :)
49 * 09/11/00 (seiwald) - defunct var_list() removed
52 static struct hash *varhash = 0;
55 * VARIABLE - a user defined multi-value variable
58 typedef struct _variable VARIABLE ;
66 static VARIABLE * var_enter( char * symbol );
67 static void var_dump( char * symbol, LIST * value, char * what );
71 * var_hash_swap() - swap all variable settings with those passed
73 * Used to implement separate settings spaces for modules
76 void var_hash_swap( struct hash * * new_vars )
78 struct hash * old = varhash;
85 * var_defines() - load a bunch of variable=value settings
87 * If preprocess is false, take the value verbatim.
89 * Otherwise, if the variable value is enclosed in quotes, strip the
92 * Otherwise, if variable name ends in PATH, split value at :'s.
94 * Otherwise, split the value at blanks.
97 void var_defines( char * const * e, int preprocess )
108 /* On the mac (MPW), the var=val is actually var\0val */
109 /* Think different. */
111 if ( ( val = strchr( *e, '=' ) ) || ( val = *e + strlen( *e ) ) )
113 if ( ( val = strchr( *e, '=' ) ) )
119 # ifdef OPT_NO_EXTERNAL_VARIABLE_SPLIT
128 size_t len = strlen( val + 1 );
130 int quoted = ( val[1] == '"' ) && ( val[len] == '"' ) &&
133 if ( quoted && preprocess )
135 string_append_range( buf, val + 2, val + len );
136 l = list_new( l, newstr( buf->value ) );
137 string_truncate( buf, 0 );
141 /* Split *PATH at :'s, not spaces. */
144 if ( !strncmp( val - 4, "PATH", 4 ) ||
145 !strncmp( val - 4, "Path", 4 ) ||
146 !strncmp( val - 4, "path", 4 ) )
154 preprocess && ( ( p = strchr( pp, split ) ) != 0 );
158 string_append_range( buf, pp, p );
159 l = list_new( l, newstr( buf->value ) );
160 string_truncate( buf, 0 );
163 l = list_new( l, newstr( pp ) );
167 string_append_range( buf, *e, val );
168 var_set( buf->value, l, VAR_SET );
169 string_truncate( buf, 0 );
177 * var_string() - expand a string with variables in it
179 * Copies in to out; doesn't modify targets & sources.
182 int var_string( char * in, char * out, int outsize, LOL * lol )
185 char * oute = out + outsize - 1;
192 /* Copy white space. */
193 while ( isspace( *in ) )
202 /* Copy non-white space, watching for variables. */
203 while ( *in && !isspace( *in ) )
208 if ( ( in[ 0 ] == '$' ) && ( in[ 1 ] == '(' ) )
214 else if ( ( in[ 0 ] == '@' ) && ( in[ 1 ] == '(' ) )
220 /* Scan the content of the response file @() section. */
221 while ( *ine && ( depth > 0 ) )
225 case '(': ++depth; break;
226 case ')': --depth; break;
228 if ( ( depth == 1 ) && ( ine[ 1 ] == 'E' ) && ( ine[ 2 ] == '=' ) )
237 /* the @() reference doesn't match the @(foo:E=bar) format.
238 hence we leave it alone by copying directly to output. */
240 if ( out + 2 >= oute ) return -1;
243 l = var_string( in + 2, out, oute - out, lol );
244 if ( l < 0 ) return -1;
246 if ( out + 1 >= oute ) return -1;
249 else if ( depth == 0 )
253 const char * file_name_s = 0;
255 /* Expand the temporary file name var inline. */
257 string_copy( &file_name_v, "$(" );
258 string_append_range( &file_name_v, in + 2, split );
259 string_push_back( &file_name_v, ')' );
261 string_new( &file_name_v );
262 string_append_range( &file_name_v, in + 2, split );
264 file_name_l = var_string( file_name_v.value, out, oute - out + 1, lol );
265 string_free( &file_name_v );
266 if ( file_name_l < 0 ) return -1;
269 /* For stdout/stderr we will create a temp file and generate
270 * a command that outputs the content as needed.
272 if ( ( strcmp( "STDOUT", out ) == 0 ) ||
273 ( strcmp( "STDERR", out ) == 0 ) )
275 int err_redir = strcmp( "STDERR", out ) == 0;
277 file_name_s = path_tmpfile();
278 file_name_l = strlen(file_name_s);
280 if ( ( out + 7 + file_name_l + ( err_redir ? 5 : 0 ) ) >= oute )
282 sprintf( out,"type \"%s\"%s", file_name_s,
283 err_redir ? " 1>&2" : "" );
285 if ( ( out + 6 + file_name_l + ( err_redir ? 5 : 0 ) ) >= oute )
287 sprintf( out,"cat \"%s\"%s", file_name_s,
288 err_redir ? " 1>&2" : "" );
290 /* We also make sure that the temp files created by this
291 * get nuked eventually.
293 file_remove_atexit( file_name_s );
296 /* Expand the file value into the file reference. */
297 var_string_to_file( split + 3, ine - split - 4, file_name_s,
300 /* Continue on with the expansion. */
301 out += strlen( out );
304 /* And continue with the parsing just past the @() reference. */
314 /* Add zero to 'out' so that 'lastword' is correctly zero-terminated. */
317 /* Do not increment, intentionally. */
320 /* If a variable encountered, expand it and and embed the
321 * space-separated members of the list in the output.
325 LIST * l = var_expand( L0, lastword, out, lol, 0 );
331 int so = strlen( l->string );
333 if ( out + so >= oute )
336 strcpy( out, l->string );
339 if ( l ) *out++ = ' ';
355 void var_string_to_file( const char * in, int insize, const char * out, LOL * lol )
357 char const * ine = in + insize;
359 int out_debug = DEBUG_EXEC ? 1 : 0;
364 else if ( strcmp( out, "STDOUT" ) == 0 )
368 else if ( strcmp( out, "STDERR" ) == 0 )
374 /* Handle "path to file" filenames. */
376 if ( ( out[ 0 ] == '"' ) && ( out[ strlen( out ) - 1 ] == '"' ) )
378 string_copy( &out_name, out + 1 );
379 string_truncate( &out_name, out_name.size - 1 );
383 string_copy( &out_name,out );
385 out_file = fopen( out_name.value, "w" );
388 printf( "failed to write output file '%s'!\n", out_name.value );
391 string_free( &out_name );
394 if ( out_debug ) printf( "\nfile %s\n", out );
396 while ( *in && ( in < ine ) )
399 const char * output_0 = in;
400 const char * output_1 = in;
402 /* Copy white space. */
403 while ( ( output_1 < ine ) && isspace( *output_1 ) )
406 if ( output_0 < output_1 )
408 if ( out_file ) fwrite( output_0, output_1 - output_0, 1, out_file );
409 if ( out_debug ) fwrite( output_0, output_1 - output_0, 1, stdout );
413 /* Copy non-white space, watching for variables. */
414 while ( ( output_1 < ine ) && *output_1 && !isspace( *output_1 ) )
416 if ( ( output_1[ 0 ] == '$' ) && ( output_1[ 1 ] == '(' ) )
421 /* If a variable encountered, expand it and embed the space-separated
422 * members of the list in the output.
426 LIST * l = var_expand( L0, (char *)output_0, (char *)output_1, lol, 0 );
430 if ( out_file ) fputs( l->string, out_file );
431 if ( out_debug ) puts( l->string );
435 if ( out_file ) fputc( ' ', out_file );
436 if ( out_debug ) fputc( ' ', stdout );
442 else if ( output_0 < output_1 )
446 const char * output_n = output_0;
447 while ( output_n < output_1 )
449 output_n += fwrite( output_n, 1, output_1-output_n, out_file );
454 const char * output_n = output_0;
455 while ( output_n < output_1 )
457 output_n += fwrite( output_n, 1, output_1-output_n, stdout );
465 if ( out_file && ( out_file != stdout ) && ( out_file != stderr ) )
471 if ( out_debug ) fputc( '\n', stdout );
476 * var_get() - get value of a user defined symbol.
478 * Returns NULL if symbol unset.
481 LIST * var_get( char * symbol )
485 /* Some "fixed" variables... */
486 if ( strcmp( "TMPDIR", symbol ) == 0 )
488 result = list_new( L0, newstr( (char *)path_tmpdir() ) );
490 else if ( strcmp( "TMPNAME", symbol ) == 0 )
492 result = list_new( L0, newstr( (char *)path_tmpnam() ) );
494 else if ( strcmp( "TMPFILE", symbol ) == 0 )
496 result = list_new( L0, newstr( (char *)path_tmpfile() ) );
498 else if ( strcmp( "STDOUT", symbol ) == 0 )
500 result = list_new( L0, newstr( "STDOUT" ) );
502 else if ( strcmp( "STDERR", symbol ) == 0 )
504 result = list_new( L0, newstr( "STDERR" ) );
514 if ( varhash && hashcheck( varhash, (HASHDATA * *)&v ) )
517 var_dump( v->symbol, v->value, "get" );
526 * var_set() - set a variable in Jam's user defined symbol table.
528 * 'flag' controls the relationship between new and old values of the variable:
529 * SET replaces the old with the new; APPEND appends the new to the old; DEFAULT
530 * only uses the new if the variable was previously unset.
532 * Copies symbol. Takes ownership of value.
535 void var_set( char * symbol, LIST * value, int flag )
537 VARIABLE * v = var_enter( symbol );
540 var_dump( symbol, value, "set" );
546 list_free( v->value );
552 v->value = list_append( v->value, value );
556 /* Set only if unset */
567 * var_swap() - swap a variable's value with the given one.
570 LIST * var_swap( char * symbol, LIST * value )
572 VARIABLE * v = var_enter( symbol );
573 LIST * oldvalue = v->value;
575 var_dump( symbol, value, "set" );
582 * var_enter() - make new var symbol table entry, returning var ptr.
585 static VARIABLE * var_enter( char * symbol )
591 varhash = hashinit( sizeof( VARIABLE ), "variables" );
596 if ( hashenter( varhash, (HASHDATA * *)&v ) )
597 v->symbol = newstr( symbol ); /* never freed */
604 * var_dump() - dump a variable to stdout.
607 static void var_dump( char * symbol, LIST * value, char * what )
609 printf( "%s %s = ", what, symbol );
616 * var_done() - free variable tables.
619 static void delete_var_( void * xvar, void * data )
621 VARIABLE * v = (VARIABLE *)xvar;
622 freestr( v->symbol );
623 list_free( v-> value );
629 hashenumerate( varhash, delete_var_, (void *)0 );