summaryrefslogtreecommitdiff
path: root/jam-files/engine/variable.c
diff options
context:
space:
mode:
Diffstat (limited to 'jam-files/engine/variable.c')
-rw-r--r--jam-files/engine/variable.c631
1 files changed, 631 insertions, 0 deletions
diff --git a/jam-files/engine/variable.c b/jam-files/engine/variable.c
new file mode 100644
index 00000000..795f3458
--- /dev/null
+++ b/jam-files/engine/variable.c
@@ -0,0 +1,631 @@
+/*
+ * Copyright 1993, 2000 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2001-2004 David Abrahams.
+ * Copyright 2005 Reece H. Dunn.
+ * Copyright 2005 Rene Rivera.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+#include "jam.h"
+#include "lists.h"
+#include "parse.h"
+#include "variable.h"
+#include "expand.h"
+#include "hash.h"
+#include "filesys.h"
+#include "newstr.h"
+#include "strings.h"
+#include "pathsys.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * variable.c - handle Jam multi-element variables.
+ *
+ * External routines:
+ *
+ * var_defines() - load a bunch of variable=value settings.
+ * var_string() - expand a string with variables in it.
+ * var_get() - get value of a user defined symbol.
+ * var_set() - set a variable in jam's user defined symbol table.
+ * var_swap() - swap a variable's value with the given one.
+ * var_done() - free variable tables.
+ *
+ * Internal routines:
+ *
+ * var_enter() - make new var symbol table entry, returning var ptr.
+ * var_dump() - dump a variable to stdout.
+ *
+ * 04/13/94 (seiwald) - added shorthand L0 for null list pointer
+ * 08/23/94 (seiwald) - Support for '+=' (append to variable)
+ * 01/22/95 (seiwald) - split environment variables at blanks or :'s
+ * 05/10/95 (seiwald) - split path variables at SPLITPATH (not :)
+ * 09/11/00 (seiwald) - defunct var_list() removed
+ */
+
+static struct hash *varhash = 0;
+
+/*
+ * VARIABLE - a user defined multi-value variable
+ */
+
+typedef struct _variable VARIABLE ;
+
+struct _variable
+{
+ char * symbol;
+ LIST * value;
+};
+
+static VARIABLE * var_enter( char * symbol );
+static void var_dump( char * symbol, LIST * value, char * what );
+
+
+/*
+ * var_hash_swap() - swap all variable settings with those passed
+ *
+ * Used to implement separate settings spaces for modules
+ */
+
+void var_hash_swap( struct hash * * new_vars )
+{
+ struct hash * old = varhash;
+ varhash = *new_vars;
+ *new_vars = old;
+}
+
+
+/*
+ * var_defines() - load a bunch of variable=value settings
+ *
+ * If preprocess is false, take the value verbatim.
+ *
+ * Otherwise, if the variable value is enclosed in quotes, strip the
+ * quotes.
+ *
+ * Otherwise, if variable name ends in PATH, split value at :'s.
+ *
+ * Otherwise, split the value at blanks.
+ */
+
+void var_defines( char * const * e, int preprocess )
+{
+ string buf[1];
+
+ string_new( buf );
+
+ for ( ; *e; ++e )
+ {
+ char * val;
+
+# ifdef OS_MAC
+ /* On the mac (MPW), the var=val is actually var\0val */
+ /* Think different. */
+
+ if ( ( val = strchr( *e, '=' ) ) || ( val = *e + strlen( *e ) ) )
+# else
+ if ( ( val = strchr( *e, '=' ) ) )
+# endif
+ {
+ LIST * l = L0;
+ char * pp;
+ char * p;
+# ifdef OPT_NO_EXTERNAL_VARIABLE_SPLIT
+ char split = '\0';
+# else
+ # ifdef OS_MAC
+ char split = ',';
+ # else
+ char split = ' ';
+ # endif
+# endif
+ size_t len = strlen( val + 1 );
+
+ int quoted = ( val[1] == '"' ) && ( val[len] == '"' ) &&
+ ( len > 1 );
+
+ if ( quoted && preprocess )
+ {
+ string_append_range( buf, val + 2, val + len );
+ l = list_new( l, newstr( buf->value ) );
+ string_truncate( buf, 0 );
+ }
+ else
+ {
+ /* Split *PATH at :'s, not spaces. */
+ if ( val - 4 >= *e )
+ {
+ if ( !strncmp( val - 4, "PATH", 4 ) ||
+ !strncmp( val - 4, "Path", 4 ) ||
+ !strncmp( val - 4, "path", 4 ) )
+ split = SPLITPATH;
+ }
+
+ /* Do the split. */
+ for
+ (
+ pp = val + 1;
+ preprocess && ( ( p = strchr( pp, split ) ) != 0 );
+ pp = p + 1
+ )
+ {
+ string_append_range( buf, pp, p );
+ l = list_new( l, newstr( buf->value ) );
+ string_truncate( buf, 0 );
+ }
+
+ l = list_new( l, newstr( pp ) );
+ }
+
+ /* Get name. */
+ string_append_range( buf, *e, val );
+ var_set( buf->value, l, VAR_SET );
+ string_truncate( buf, 0 );
+ }
+ }
+ string_free( buf );
+}
+
+
+/*
+ * var_string() - expand a string with variables in it
+ *
+ * Copies in to out; doesn't modify targets & sources.
+ */
+
+int var_string( char * in, char * out, int outsize, LOL * lol )
+{
+ char * out0 = out;
+ char * oute = out + outsize - 1;
+
+ while ( *in )
+ {
+ char * lastword;
+ int dollar = 0;
+
+ /* Copy white space. */
+ while ( isspace( *in ) )
+ {
+ if ( out >= oute )
+ return -1;
+ *out++ = *in++;
+ }
+
+ lastword = out;
+
+ /* Copy non-white space, watching for variables. */
+ while ( *in && !isspace( *in ) )
+ {
+ if ( out >= oute )
+ return -1;
+
+ if ( ( in[ 0 ] == '$' ) && ( in[ 1 ] == '(' ) )
+ {
+ ++dollar;
+ *out++ = *in++;
+ }
+ #ifdef OPT_AT_FILES
+ else if ( ( in[ 0 ] == '@' ) && ( in[ 1 ] == '(' ) )
+ {
+ int depth = 1;
+ char * ine = in + 2;
+ char * split = 0;
+
+ /* Scan the content of the response file @() section. */
+ while ( *ine && ( depth > 0 ) )
+ {
+ switch ( *ine )
+ {
+ case '(': ++depth; break;
+ case ')': --depth; break;
+ case ':':
+ if ( ( depth == 1 ) && ( ine[ 1 ] == 'E' ) && ( ine[ 2 ] == '=' ) )
+ split = ine;
+ break;
+ }
+ ++ine;
+ }
+
+ if ( !split )
+ {
+ /* the @() reference doesn't match the @(foo:E=bar) format.
+ hence we leave it alone by copying directly to output. */
+ int l = 0;
+ if ( out + 2 >= oute ) return -1;
+ *( out++ ) = '@';
+ *( out++ ) = '(';
+ l = var_string( in + 2, out, oute - out, lol );
+ if ( l < 0 ) return -1;
+ out += l;
+ if ( out + 1 >= oute ) return -1;
+ *( out++ ) = ')';
+ }
+ else if ( depth == 0 )
+ {
+ string file_name_v;
+ int file_name_l = 0;
+ const char * file_name_s = 0;
+
+ /* Expand the temporary file name var inline. */
+ #if 0
+ string_copy( &file_name_v, "$(" );
+ string_append_range( &file_name_v, in + 2, split );
+ string_push_back( &file_name_v, ')' );
+ #else
+ string_new( &file_name_v );
+ string_append_range( &file_name_v, in + 2, split );
+ #endif
+ file_name_l = var_string( file_name_v.value, out, oute - out + 1, lol );
+ string_free( &file_name_v );
+ if ( file_name_l < 0 ) return -1;
+ file_name_s = out;
+
+ /* For stdout/stderr we will create a temp file and generate
+ * a command that outputs the content as needed.
+ */
+ if ( ( strcmp( "STDOUT", out ) == 0 ) ||
+ ( strcmp( "STDERR", out ) == 0 ) )
+ {
+ int err_redir = strcmp( "STDERR", out ) == 0;
+ out[ 0 ] = '\0';
+ file_name_s = path_tmpfile();
+ file_name_l = strlen(file_name_s);
+ #ifdef OS_NT
+ if ( ( out + 7 + file_name_l + ( err_redir ? 5 : 0 ) ) >= oute )
+ return -1;
+ sprintf( out,"type \"%s\"%s", file_name_s,
+ err_redir ? " 1>&2" : "" );
+ #else
+ if ( ( out + 6 + file_name_l + ( err_redir ? 5 : 0 ) ) >= oute )
+ return -1;
+ sprintf( out,"cat \"%s\"%s", file_name_s,
+ err_redir ? " 1>&2" : "" );
+ #endif
+ /* We also make sure that the temp files created by this
+ * get nuked eventually.
+ */
+ file_remove_atexit( file_name_s );
+ }
+
+ /* Expand the file value into the file reference. */
+ var_string_to_file( split + 3, ine - split - 4, file_name_s,
+ lol );
+
+ /* Continue on with the expansion. */
+ out += strlen( out );
+ }
+
+ /* And continue with the parsing just past the @() reference. */
+ in = ine;
+ }
+ #endif
+ else
+ {
+ *out++ = *in++;
+ }
+ }
+
+ /* Add zero to 'out' so that 'lastword' is correctly zero-terminated. */
+ if ( out >= oute )
+ return -1;
+ /* Do not increment, intentionally. */
+ *out = '\0';
+
+ /* If a variable encountered, expand it and and embed the
+ * space-separated members of the list in the output.
+ */
+ if ( dollar )
+ {
+ LIST * l = var_expand( L0, lastword, out, lol, 0 );
+
+ out = lastword;
+
+ while ( l )
+ {
+ int so = strlen( l->string );
+
+ if ( out + so >= oute )
+ return -1;
+
+ strcpy( out, l->string );
+ out += so;
+ l = list_next( l );
+ if ( l ) *out++ = ' ';
+ }
+
+ list_free( l );
+ }
+ }
+
+ if ( out >= oute )
+ return -1;
+
+ *out++ = '\0';
+
+ return out - out0;
+}
+
+
+void var_string_to_file( const char * in, int insize, const char * out, LOL * lol )
+{
+ char const * ine = in + insize;
+ FILE * out_file = 0;
+ int out_debug = DEBUG_EXEC ? 1 : 0;
+ if ( globs.noexec )
+ {
+ /* out_debug = 1; */
+ }
+ else if ( strcmp( out, "STDOUT" ) == 0 )
+ {
+ out_file = stdout;
+ }
+ else if ( strcmp( out, "STDERR" ) == 0 )
+ {
+ out_file = stderr;
+ }
+ else
+ {
+ /* Handle "path to file" filenames. */
+ string out_name;
+ if ( ( out[ 0 ] == '"' ) && ( out[ strlen( out ) - 1 ] == '"' ) )
+ {
+ string_copy( &out_name, out + 1 );
+ string_truncate( &out_name, out_name.size - 1 );
+ }
+ else
+ {
+ string_copy( &out_name,out );
+ }
+ out_file = fopen( out_name.value, "w" );
+ if ( !out_file )
+ {
+ printf( "failed to write output file '%s'!\n", out_name.value );
+ exit( EXITBAD );
+ }
+ string_free( &out_name );
+ }
+
+ if ( out_debug ) printf( "\nfile %s\n", out );
+
+ while ( *in && ( in < ine ) )
+ {
+ int dollar = 0;
+ const char * output_0 = in;
+ const char * output_1 = in;
+
+ /* Copy white space. */
+ while ( ( output_1 < ine ) && isspace( *output_1 ) )
+ ++output_1;
+
+ if ( output_0 < output_1 )
+ {
+ if ( out_file ) fwrite( output_0, output_1 - output_0, 1, out_file );
+ if ( out_debug ) fwrite( output_0, output_1 - output_0, 1, stdout );
+ }
+ output_0 = output_1;
+
+ /* Copy non-white space, watching for variables. */
+ while ( ( output_1 < ine ) && *output_1 && !isspace( *output_1 ) )
+ {
+ if ( ( output_1[ 0 ] == '$' ) && ( output_1[ 1 ] == '(' ) )
+ ++dollar;
+ ++output_1;
+ }
+
+ /* If a variable encountered, expand it and embed the space-separated
+ * members of the list in the output.
+ */
+ if ( dollar )
+ {
+ LIST * l = var_expand( L0, (char *)output_0, (char *)output_1, lol, 0 );
+
+ while ( l )
+ {
+ if ( out_file ) fputs( l->string, out_file );
+ if ( out_debug ) puts( l->string );
+ l = list_next( l );
+ if ( l )
+ {
+ if ( out_file ) fputc( ' ', out_file );
+ if ( out_debug ) fputc( ' ', stdout );
+ }
+ }
+
+ list_free( l );
+ }
+ else if ( output_0 < output_1 )
+ {
+ if ( out_file )
+ {
+ const char * output_n = output_0;
+ while ( output_n < output_1 )
+ {
+ output_n += fwrite( output_n, 1, output_1-output_n, out_file );
+ }
+ }
+ if ( out_debug )
+ {
+ const char * output_n = output_0;
+ while ( output_n < output_1 )
+ {
+ output_n += fwrite( output_n, 1, output_1-output_n, stdout );
+ }
+ }
+ }
+
+ in = output_1;
+ }
+
+ if ( out_file && ( out_file != stdout ) && ( out_file != stderr ) )
+ {
+ fflush( out_file );
+ fclose( out_file );
+ }
+
+ if ( out_debug ) fputc( '\n', stdout );
+}
+
+
+/*
+ * var_get() - get value of a user defined symbol.
+ *
+ * Returns NULL if symbol unset.
+ */
+
+LIST * var_get( char * symbol )
+{
+ LIST * result = 0;
+#ifdef OPT_AT_FILES
+ /* Some "fixed" variables... */
+ if ( strcmp( "TMPDIR", symbol ) == 0 )
+ {
+ result = list_new( L0, newstr( (char *)path_tmpdir() ) );
+ }
+ else if ( strcmp( "TMPNAME", symbol ) == 0 )
+ {
+ result = list_new( L0, newstr( (char *)path_tmpnam() ) );
+ }
+ else if ( strcmp( "TMPFILE", symbol ) == 0 )
+ {
+ result = list_new( L0, newstr( (char *)path_tmpfile() ) );
+ }
+ else if ( strcmp( "STDOUT", symbol ) == 0 )
+ {
+ result = list_new( L0, newstr( "STDOUT" ) );
+ }
+ else if ( strcmp( "STDERR", symbol ) == 0 )
+ {
+ result = list_new( L0, newstr( "STDERR" ) );
+ }
+ else
+#endif
+ {
+ VARIABLE var;
+ VARIABLE * v = &var;
+
+ v->symbol = symbol;
+
+ if ( varhash && hashcheck( varhash, (HASHDATA * *)&v ) )
+ {
+ if ( DEBUG_VARGET )
+ var_dump( v->symbol, v->value, "get" );
+ result = v->value;
+ }
+ }
+ return result;
+}
+
+
+/*
+ * var_set() - set a variable in Jam's user defined symbol table.
+ *
+ * 'flag' controls the relationship between new and old values of the variable:
+ * SET replaces the old with the new; APPEND appends the new to the old; DEFAULT
+ * only uses the new if the variable was previously unset.
+ *
+ * Copies symbol. Takes ownership of value.
+ */
+
+void var_set( char * symbol, LIST * value, int flag )
+{
+ VARIABLE * v = var_enter( symbol );
+
+ if ( DEBUG_VARSET )
+ var_dump( symbol, value, "set" );
+
+ switch ( flag )
+ {
+ case VAR_SET:
+ /* Replace value */
+ list_free( v->value );
+ v->value = value;
+ break;
+
+ case VAR_APPEND:
+ /* Append value */
+ v->value = list_append( v->value, value );
+ break;
+
+ case VAR_DEFAULT:
+ /* Set only if unset */
+ if ( !v->value )
+ v->value = value;
+ else
+ list_free( value );
+ break;
+ }
+}
+
+
+/*
+ * var_swap() - swap a variable's value with the given one.
+ */
+
+LIST * var_swap( char * symbol, LIST * value )
+{
+ VARIABLE * v = var_enter( symbol );
+ LIST * oldvalue = v->value;
+ if ( DEBUG_VARSET )
+ var_dump( symbol, value, "set" );
+ v->value = value;
+ return oldvalue;
+}
+
+
+/*
+ * var_enter() - make new var symbol table entry, returning var ptr.
+ */
+
+static VARIABLE * var_enter( char * symbol )
+{
+ VARIABLE var;
+ VARIABLE * v = &var;
+
+ if ( !varhash )
+ varhash = hashinit( sizeof( VARIABLE ), "variables" );
+
+ v->symbol = symbol;
+ v->value = 0;
+
+ if ( hashenter( varhash, (HASHDATA * *)&v ) )
+ v->symbol = newstr( symbol ); /* never freed */
+
+ return v;
+}
+
+
+/*
+ * var_dump() - dump a variable to stdout.
+ */
+
+static void var_dump( char * symbol, LIST * value, char * what )
+{
+ printf( "%s %s = ", what, symbol );
+ list_print( value );
+ printf( "\n" );
+}
+
+
+/*
+ * var_done() - free variable tables.
+ */
+
+static void delete_var_( void * xvar, void * data )
+{
+ VARIABLE * v = (VARIABLE *)xvar;
+ freestr( v->symbol );
+ list_free( v-> value );
+}
+
+
+void var_done()
+{
+ hashenumerate( varhash, delete_var_, (void *)0 );
+ hashdone( varhash );
+}