jam0 use passed cflags
[platform/upstream/boost-jam.git] / variable.c
1 /*
2  * Copyright 1993, 2000 Christopher Seiwald.
3  *
4  * This file is part of Jam - see jam.c for Copyright information.
5  */
6
7 /*  This file is ALSO:
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)
13  */
14
15 #include "jam.h"
16 #include "lists.h"
17 #include "parse.h"
18 #include "variable.h"
19 #include "expand.h"
20 #include "hash.h"
21 #include "filesys.h"
22 #include "newstr.h"
23 #include "strings.h"
24 #include "pathsys.h"
25 #include <stdlib.h>
26 #include <stdio.h>
27
28 /*
29  * variable.c - handle Jam multi-element variables.
30  *
31  * External routines:
32  *
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.
39  *
40  * Internal routines:
41  *
42  *  var_enter() - make new var symbol table entry, returning var ptr.
43  *  var_dump()  - dump a variable to stdout.
44  *
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
50  */
51
52 static struct hash *varhash = 0;
53
54 /*
55  * VARIABLE - a user defined multi-value variable
56  */
57
58 typedef struct _variable VARIABLE ;
59
60 struct _variable
61 {
62     char * symbol;
63     LIST * value;
64 };
65
66 static VARIABLE * var_enter( char * symbol );
67 static void var_dump( char * symbol, LIST * value, char * what );
68
69
70 /*
71  * var_hash_swap() - swap all variable settings with those passed
72  *
73  * Used to implement separate settings spaces for modules
74  */
75
76 void var_hash_swap( struct hash * * new_vars )
77 {
78     struct hash * old = varhash;
79     varhash = *new_vars;
80     *new_vars = old;
81 }
82
83
84 /*
85  * var_defines() - load a bunch of variable=value settings
86  *
87  * If preprocess is false, take the value verbatim.
88  *
89  * Otherwise, if the variable value is enclosed in quotes, strip the
90  * quotes.
91  *
92  * Otherwise, if variable name ends in PATH, split value at :'s.
93  *
94  * Otherwise, split the value at blanks.
95  */
96
97 void var_defines( char * const * e, int preprocess )
98 {
99     string buf[1];
100
101     string_new( buf );
102
103     for ( ; *e; ++e )
104     {
105         char * val;
106
107 # ifdef OS_MAC
108         /* On the mac (MPW), the var=val is actually var\0val */
109         /* Think different. */
110
111         if ( ( val = strchr( *e, '=' ) ) || ( val = *e + strlen( *e ) ) )
112 # else
113         if ( ( val = strchr( *e, '=' ) ) )
114 # endif
115         {
116             LIST * l = L0;
117             char * pp;
118             char * p;
119 # ifdef OPT_NO_EXTERNAL_VARIABLE_SPLIT
120             char split = '\0';
121 # else
122     # ifdef OS_MAC
123             char split = ',';
124     # else
125             char split = ' ';
126     # endif
127 # endif
128             size_t len = strlen( val + 1 );
129
130             int quoted = ( val[1] == '"' ) && ( val[len] == '"' ) &&
131                 ( len > 1 );
132
133             if ( quoted && preprocess )
134             {
135                 string_append_range( buf, val + 2, val + len );
136                 l = list_new( l, newstr( buf->value ) );
137                 string_truncate( buf, 0 );
138             }
139             else
140             {
141                 /* Split *PATH at :'s, not spaces. */
142                 if ( val - 4 >= *e )
143                 {
144                     if ( !strncmp( val - 4, "PATH", 4 ) ||
145                         !strncmp( val - 4, "Path", 4 ) ||
146                         !strncmp( val - 4, "path", 4 ) )
147                         split = SPLITPATH;
148                 }
149
150                 /* Do the split. */
151                 for
152                 (
153                     pp = val + 1;
154                     preprocess && ( ( p = strchr( pp, split ) ) != 0 );
155                     pp = p + 1
156                 )
157                 {
158                     string_append_range( buf, pp, p );
159                     l = list_new( l, newstr( buf->value ) );
160                     string_truncate( buf, 0 );
161                 }
162
163                 l = list_new( l, newstr( pp ) );
164             }
165
166             /* Get name. */
167             string_append_range( buf, *e, val );
168             var_set( buf->value, l, VAR_SET );
169             string_truncate( buf, 0 );
170         }
171     }
172     string_free( buf );
173 }
174
175
176 /*
177  * var_string() - expand a string with variables in it
178  *
179  * Copies in to out; doesn't modify targets & sources.
180  */
181
182 int var_string( char * in, char * out, int outsize, LOL * lol )
183 {
184     char * out0 = out;
185     char * oute = out + outsize - 1;
186
187     while ( *in )
188     {
189         char * lastword;
190         int    dollar = 0;
191
192         /* Copy white space. */
193         while ( isspace( *in ) )
194         {
195             if ( out >= oute )
196                 return -1;
197             *out++ = *in++;
198         }
199
200         lastword = out;
201
202         /* Copy non-white space, watching for variables. */
203         while ( *in && !isspace( *in ) )
204         {
205             if ( out >= oute )
206                 return -1;
207
208             if ( ( in[ 0 ] == '$' ) && ( in[ 1 ] == '(' ) )
209             {
210                 ++dollar;
211                 *out++ = *in++;
212             }
213             #ifdef OPT_AT_FILES
214             else if ( ( in[ 0 ] == '@' ) && ( in[ 1 ] == '(' ) )
215             {
216                 int depth = 1;
217                 char * ine = in + 2;
218                 char * split = 0;
219
220                 /* Scan the content of the response file @() section. */
221                 while ( *ine && ( depth > 0 ) )
222                 {
223                     switch ( *ine )
224                     {
225                     case '(': ++depth; break;
226                     case ')': --depth; break;
227                     case ':':
228                         if ( ( depth == 1 ) && ( ine[ 1 ] == 'E' ) && ( ine[ 2 ] == '=' ) )
229                             split = ine;
230                         break;
231                     }
232                     ++ine;
233                 }
234
235                 if ( !split )
236                 {
237                     /*  the @() reference doesn't match the @(foo:E=bar) format.
238                         hence we leave it alone by copying directly to output. */
239                     int l = 0;
240                     if ( out + 2 >= oute ) return -1;
241                     *( out++ ) = '@';
242                     *( out++ ) = '(';
243                     l = var_string( in + 2, out, oute - out, lol );
244                     if ( l < 0 ) return -1;
245                     out += l;
246                     if ( out + 1 >= oute ) return -1;
247                     *( out++ ) = ')';
248                 }
249                 else if ( depth == 0 )
250                 {
251                     string file_name_v;
252                     int file_name_l = 0;
253                     const char * file_name_s = 0;
254
255                     /* Expand the temporary file name var inline. */
256                     #if 0
257                     string_copy( &file_name_v, "$(" );
258                     string_append_range( &file_name_v, in + 2, split );
259                     string_push_back( &file_name_v, ')' );
260                     #else
261                     string_new( &file_name_v );
262                     string_append_range( &file_name_v, in + 2, split );
263                     #endif
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;
267                     file_name_s = out;
268
269                     /* For stdout/stderr we will create a temp file and generate
270                      * a command that outputs the content as needed.
271                      */
272                     if ( ( strcmp( "STDOUT", out ) == 0 ) ||
273                         ( strcmp( "STDERR", out ) == 0 ) )
274                     {
275                         int err_redir = strcmp( "STDERR", out ) == 0;
276                         out[ 0 ] = '\0';
277                         file_name_s = path_tmpfile();
278                         file_name_l = strlen(file_name_s);
279                         #ifdef OS_NT
280                         if ( ( out + 7 + file_name_l + ( err_redir ? 5 : 0 ) ) >= oute )
281                             return -1;
282                         sprintf( out,"type \"%s\"%s", file_name_s,
283                             err_redir ? " 1>&2" : "" );
284                         #else
285                         if ( ( out + 6 + file_name_l + ( err_redir ? 5 : 0 ) ) >= oute )
286                             return -1;
287                         sprintf( out,"cat \"%s\"%s", file_name_s,
288                             err_redir ? " 1>&2" : "" );
289                         #endif
290                         /* We also make sure that the temp files created by this
291                          * get nuked eventually.
292                          */
293                         file_remove_atexit( file_name_s );
294                     }
295
296                     /* Expand the file value into the file reference. */
297                     var_string_to_file( split + 3, ine - split - 4, file_name_s,
298                         lol );
299
300                     /* Continue on with the expansion. */
301                     out += strlen( out );
302                 }
303
304                 /* And continue with the parsing just past the @() reference. */
305                 in = ine;
306             }
307             #endif
308             else
309             {
310                 *out++ = *in++;
311             }
312         }
313
314         /* Add zero to 'out' so that 'lastword' is correctly zero-terminated. */
315         if ( out >= oute )
316             return -1;
317         /* Do not increment, intentionally. */
318         *out = '\0';
319
320         /* If a variable encountered, expand it and and embed the
321          * space-separated members of the list in the output.
322          */
323         if ( dollar )
324         {
325             LIST * l = var_expand( L0, lastword, out, lol, 0 );
326
327             out = lastword;
328
329             while ( l )
330             {
331                 int so = strlen( l->string );
332
333                 if ( out + so >= oute )
334                     return -1;
335
336                 strcpy( out, l->string );
337                 out += so;
338                 l = list_next( l );
339                 if ( l ) *out++ = ' ';
340             }
341
342             list_free( l );
343         }
344     }
345
346     if ( out >= oute )
347         return -1;
348
349     *out++ = '\0';
350
351     return out - out0;
352 }
353
354
355 void var_string_to_file( const char * in, int insize, const char * out, LOL * lol )
356 {
357     char const * ine = in + insize;
358     FILE * out_file = 0;
359     int out_debug = DEBUG_EXEC ? 1 : 0;
360     if ( globs.noexec )
361     {
362         /* out_debug = 1; */
363     }
364     else if ( strcmp( out, "STDOUT" ) == 0 )
365     {
366         out_file = stdout;
367     }
368     else if ( strcmp( out, "STDERR" ) == 0 )
369     {
370         out_file = stderr;
371     }
372     else
373     {
374         /* Handle "path to file" filenames. */
375         string out_name;
376         if ( ( out[ 0 ] == '"' ) && ( out[ strlen( out ) - 1 ] == '"' ) )
377         {
378             string_copy( &out_name, out + 1 );
379             string_truncate( &out_name, out_name.size - 1 );
380         }
381         else
382         {
383             string_copy( &out_name,out );
384         }
385         out_file = fopen( out_name.value, "w" );
386         if ( !out_file )
387         {
388             printf( "failed to write output file '%s'!\n", out_name.value );
389             exit( EXITBAD );
390         }
391         string_free( &out_name );
392     }
393
394     if ( out_debug ) printf( "\nfile %s\n", out );
395
396     while ( *in && ( in < ine ) )
397     {
398         int dollar = 0;
399         const char * output_0 = in;
400         const char * output_1 = in;
401
402         /* Copy white space. */
403         while ( ( output_1 < ine ) && isspace( *output_1 ) )
404             ++output_1;
405
406         if ( output_0 < output_1 )
407         {
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   );
410         }
411         output_0 = output_1;
412
413         /* Copy non-white space, watching for variables. */
414         while ( ( output_1 < ine ) && *output_1 && !isspace( *output_1 ) )
415         {
416             if ( ( output_1[ 0 ] == '$' ) && ( output_1[ 1 ] == '(' ) )
417                 ++dollar;
418             ++output_1;
419         }
420
421         /* If a variable encountered, expand it and embed the space-separated
422          * members of the list in the output.
423          */
424         if ( dollar )
425         {
426             LIST * l = var_expand( L0, (char *)output_0, (char *)output_1, lol, 0 );
427
428             while ( l )
429             {
430                 if ( out_file ) fputs( l->string, out_file );
431                 if ( out_debug ) puts( l->string );
432                 l = list_next( l );
433                 if ( l )
434                 {
435                     if ( out_file ) fputc( ' ', out_file );
436                     if ( out_debug ) fputc( ' ', stdout );
437                 }
438             }
439
440             list_free( l );
441         }
442         else if ( output_0 < output_1 )
443         {
444             if ( out_file )
445             {
446                 const char * output_n = output_0;
447                 while ( output_n < output_1 )
448                 {
449                     output_n += fwrite( output_n, 1, output_1-output_n, out_file );
450                 }
451             }
452             if ( out_debug )
453             {
454                 const char * output_n = output_0;
455                 while ( output_n < output_1 )
456                 {
457                     output_n += fwrite( output_n, 1, output_1-output_n, stdout );
458                 }
459             }
460         }
461
462         in = output_1;
463     }
464
465     if ( out_file && ( out_file != stdout ) && ( out_file != stderr ) )
466     {
467         fflush( out_file );
468         fclose( out_file );
469     }
470
471     if ( out_debug ) fputc( '\n', stdout );
472 }
473
474
475 /*
476  * var_get() - get value of a user defined symbol.
477  *
478  * Returns NULL if symbol unset.
479  */
480
481 LIST * var_get( char * symbol )
482 {
483     LIST * result = 0;
484 #ifdef OPT_AT_FILES
485     /* Some "fixed" variables... */
486     if ( strcmp( "TMPDIR", symbol ) == 0 )
487     {
488         result = list_new( L0, newstr( (char *)path_tmpdir() ) );
489     }
490     else if ( strcmp( "TMPNAME", symbol ) == 0 )
491     {
492         result = list_new( L0, newstr( (char *)path_tmpnam() ) );
493     }
494     else if ( strcmp( "TMPFILE", symbol ) == 0 )
495     {
496         result = list_new( L0, newstr( (char *)path_tmpfile() ) );
497     }
498     else if ( strcmp( "STDOUT", symbol ) == 0 )
499     {
500         result = list_new( L0, newstr( "STDOUT" ) );
501     }
502     else if ( strcmp( "STDERR", symbol ) == 0 )
503     {
504         result = list_new( L0, newstr( "STDERR" ) );
505     }
506     else
507 #endif
508     {
509         VARIABLE var;
510         VARIABLE * v = &var;
511
512         v->symbol = symbol;
513
514         if ( varhash && hashcheck( varhash, (HASHDATA * *)&v ) )
515         {
516             if ( DEBUG_VARGET )
517                 var_dump( v->symbol, v->value, "get" );
518             result = v->value;
519         }
520     }
521     return result;
522 }
523
524
525 /*
526  * var_set() - set a variable in Jam's user defined symbol table.
527  *
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.
531  *
532  * Copies symbol. Takes ownership of value.
533  */
534
535 void var_set( char * symbol, LIST * value, int flag )
536 {
537     VARIABLE * v = var_enter( symbol );
538
539     if ( DEBUG_VARSET )
540         var_dump( symbol, value, "set" );
541
542     switch ( flag )
543     {
544     case VAR_SET:
545         /* Replace value */
546         list_free( v->value );
547         v->value = value;
548         break;
549
550     case VAR_APPEND:
551         /* Append value */
552         v->value = list_append( v->value, value );
553         break;
554
555     case VAR_DEFAULT:
556         /* Set only if unset */
557         if ( !v->value )
558             v->value = value;
559         else
560             list_free( value );
561         break;
562     }
563 }
564
565
566 /*
567  * var_swap() - swap a variable's value with the given one.
568  */
569
570 LIST * var_swap( char * symbol, LIST * value )
571 {
572     VARIABLE * v = var_enter( symbol );
573     LIST     * oldvalue = v->value;
574     if ( DEBUG_VARSET )
575         var_dump( symbol, value, "set" );
576     v->value = value;
577     return oldvalue;
578 }
579
580
581 /*
582  * var_enter() - make new var symbol table entry, returning var ptr.
583  */
584
585 static VARIABLE * var_enter( char * symbol )
586 {
587     VARIABLE var;
588     VARIABLE * v = &var;
589
590     if ( !varhash )
591         varhash = hashinit( sizeof( VARIABLE ), "variables" );
592
593     v->symbol = symbol;
594     v->value = 0;
595
596     if ( hashenter( varhash, (HASHDATA * *)&v ) )
597         v->symbol = newstr( symbol );  /* never freed */
598
599     return v;
600 }
601
602
603 /*
604  * var_dump() - dump a variable to stdout.
605  */
606
607 static void var_dump( char * symbol, LIST * value, char * what )
608 {
609     printf( "%s %s = ", what, symbol );
610     list_print( value );
611     printf( "\n" );
612 }
613
614
615 /*
616  * var_done() - free variable tables.
617  */
618
619 static void delete_var_( void * xvar, void * data )
620 {
621     VARIABLE * v = (VARIABLE *)xvar;
622     freestr( v->symbol );
623     list_free( v-> value );
624 }
625
626
627 void var_done()
628 {
629     hashenumerate( varhash, delete_var_, (void *)0 );
630     hashdone( varhash );
631 }