jam0 use passed cflags
[platform/upstream/boost-jam.git] / expand.c
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 # include "jam.h"
8 # include "lists.h"
9 # include "variable.h"
10 # include "expand.h"
11 # include "pathsys.h"
12 # include "newstr.h"
13 # include <assert.h>
14 # include <stdlib.h>
15 # include <limits.h>
16
17 # ifdef OS_CYGWIN
18 #  include <sys/cygwin.h>
19 #  include <windows.h>
20 # endif
21
22 /*
23  * expand.c - expand a buffer, given variable values
24  *
25  * External routines:
26  *
27  *  var_expand() - variable-expand input string into list of strings
28  *
29  * Internal routines:
30  *
31  *  var_edit_parse() - parse : modifiers into PATHNAME structure.
32  *  var_edit_file() - copy input target name to output, modifying filename.
33  *  var_edit_shift() - do upshift/downshift mods.
34  *
35  * 01/25/94 (seiwald) - $(X)$(UNDEF) was expanding like plain $(X)
36  * 04/13/94 (seiwald) - added shorthand L0 for null list pointer
37  * 01/11/01 (seiwald) - added support for :E=emptyvalue, :J=joinval
38  */
39
40 typedef struct
41 {
42     PATHNAME f;           /* :GDBSMR -- pieces */
43     char     parent;      /* :P -- go to parent directory */
44     char     filemods;    /* one of the above applied */
45     char     downshift;   /* :L -- downshift result */
46     char     upshift;     /* :U -- upshift result */
47     char     to_slashes;  /* :T -- convert "\" to "/" */
48     char     to_windows;  /* :W -- convert cygwin to native paths */
49     PATHPART empty;       /* :E -- default for empties */
50     PATHPART join;        /* :J -- join list with char */
51 } VAR_EDITS ;
52
53 static void var_edit_parse( char * mods, VAR_EDITS * edits );
54 static void var_edit_file ( char * in, string * out, VAR_EDITS * edits );
55 static void var_edit_shift( string * out, VAR_EDITS * edits );
56
57 #define MAGIC_COLON '\001'
58 #define MAGIC_LEFT  '\002'
59 #define MAGIC_RIGHT '\003'
60
61
62 /*
63  * var_expand() - variable-expand input string into list of strings.
64  *
65  * Would just copy input to output, performing variable expansion, except that
66  * since variables can contain multiple values the result of variable expansion
67  * may contain multiple values (a list). Properly performs "product" operations
68  * that occur in "$(var1)xxx$(var2)" or even "$($(var2))".
69  *
70  * Returns a newly created list.
71  */
72
73 LIST * var_expand( LIST * l, char * in, char * end, LOL * lol, int cancopyin )
74 {
75     char out_buf[ MAXSYM ];
76     string buf[ 1 ];
77     string out1[ 1 ];  /* temporary buffer */
78     size_t prefix_length;
79     char * out;
80     char * inp = in;
81     char * ov;  /* for temp copy of variable in outbuf */
82     int depth;
83
84     if ( DEBUG_VAREXP )
85         printf( "expand '%.*s'\n", (int)(end - in), in );
86
87     /* This gets a lot of cases: $(<) and $(>). */
88     if
89     (
90         ( in[ 0 ] == '$'  ) &&
91         ( in[ 1 ] == '('  ) &&
92         ( in[ 3 ] == ')'  ) &&
93         ( in[ 4 ] == '\0' )
94     )
95     {
96         switch ( in[ 2 ] )
97         {
98             case '<': return list_copy( l, lol_get( lol, 0 ) );
99             case '>': return list_copy( l, lol_get( lol, 1 ) );
100
101             case '1':
102             case '2':
103             case '3':
104             case '4':
105             case '5':
106             case '6':
107             case '7':
108             case '8':
109             case '9':
110                 return list_copy( l, lol_get( lol, in[ 2 ] - '1' ) );
111         }
112     }
113     else if ( in[0] == '$' && in[1] == '(' && in[2] == '1' && in[4] == ')' &&
114               in[5] == '\0') {
115
116         switch( in[3] )
117         {
118         case '0':
119         case '1':
120         case '2':
121         case '3':
122         case '4':
123         case '5':
124         case '6':
125         case '7':
126         case '8':
127         case '9':
128             return list_copy( l, lol_get( lol, in[3]-'0'+10-1 ) );
129         }
130     }
131
132     /* Expand @() files, to single item plus accompanying file. */
133     if ( ( in[ 0 ] == '@' ) && ( in[ 1 ] == '(' ) && ( *( end - 1 ) == ')' ) )
134     {
135         /* We try the expansion until it fits within the propective output
136          * buffer.
137          */
138         char * at_buf = 0;
139         int at_size = MAXJPATH;
140         int at_len = 0;
141         do
142         {
143             BJAM_FREE( at_buf );
144             at_buf = (char *)BJAM_MALLOC_ATOMIC( at_size + 1 );
145             at_len = var_string( in, at_buf, at_size, lol );
146             at_size *= 2;
147         }
148         while ( ( at_len < 0 ) && ( at_size < INT_MAX / 2 ) );
149         /* Return the result as a single item list. */
150         if ( at_len > 0 )
151         {
152             LIST * r;
153             string_copy( buf, at_buf );
154             r = list_new( l, newstr( buf->value ) );
155             string_free( buf );
156             BJAM_FREE( at_buf );
157             return r;
158         }
159         BJAM_FREE( at_buf );
160     }
161
162     /* Just try simple copy of in to out. */
163     while ( in < end )
164         if ( ( *in++ == '$' ) && ( *in == '(' ) )
165             goto expand;
166
167     /* No variables expanded - just add copy of input string to list. */
168
169     /* 'cancopyin' is an optimization: if the input was already a list item, we
170      * can use copystr() to put it on the new list. Otherwise, we use the slower
171      * newstr().
172      */
173     if ( cancopyin )
174         return list_new( l, copystr( inp ) );
175
176     {
177         LIST * r;
178         string_new( buf );
179         string_append_range( buf, inp, end );
180         r = list_new( l, newstr( buf->value ) );
181         string_free( buf );
182         return r;
183     }
184
185 expand:
186     string_new( buf );
187     string_append_range( buf, inp, in - 1 );  /* Copy the part before '$'. */
188     /*
189      * Input so far (ignore blanks):
190      *
191      *  stuff-in-outbuf $(variable) remainder
192      *                   ^                   ^
193      *                   in                  end
194      * Output so far:
195      *
196      *  stuff-in-outbuf $
197      *  ^                ^
198      *  out_buf          out
199      *
200      *
201      * We just copied the $ of $(...), so back up one on the output. We now find
202      * the matching close paren, copying the variable and modifiers between the
203      * $( and ) temporarily into out_buf, so that we can replace :'s with
204      * MAGIC_COLON. This is necessary to avoid being confused by modifier values
205      * that are variables containing :'s. Ugly.
206      */
207
208     depth = 1;
209     inp = ++in;  /* Skip over the '('. */
210
211     while ( ( in < end ) && depth )
212     {
213         switch ( *in++ )
214         {
215             case '(': ++depth; break;
216             case ')': --depth; break;
217         }
218     }
219
220     /*
221      * Input so far (ignore blanks):
222      *
223      *  stuff-in-outbuf $(variable) remainder
224      *                    ^        ^         ^
225      *                    inp      in        end
226      */
227     prefix_length = buf->size;
228     string_append_range( buf, inp, in - 1 );
229
230     out = buf->value + prefix_length;
231     for ( ov = out; ov < buf->value + buf->size; ++ov )
232     {
233         switch ( *ov )
234         {
235             case ':': *ov = MAGIC_COLON; break;
236             case '[': *ov = MAGIC_LEFT ; break;
237             case ']': *ov = MAGIC_RIGHT; break;
238         }
239     }
240
241     /*
242      * Input so far (ignore blanks):
243      *
244      *  stuff-in-outbuf $(variable) remainder
245      *                              ^        ^
246      *                              in       end
247      * Output so far:
248      *
249      *  stuff-in-outbuf variable
250      *  ^               ^       ^
251      *  out_buf         out     ov
252      *
253      * Later we will overwrite 'variable' in out_buf, but we will be done with
254      * it by then. 'variable' may be a multi-element list, so may each value for
255      * '$(variable element)', and so may 'remainder'. Thus we produce a product
256      * of three lists.
257      */
258     {
259         LIST * variables = 0;
260         LIST * remainder = 0;
261         LIST * vars;
262
263         /* Recursively expand variable name & rest of input. */
264         if ( out < ov ) variables = var_expand( L0, out, ov, lol, 0 );
265         if ( in < end ) remainder = var_expand( L0, in, end, lol, 0 );
266
267         /* Now produce the result chain. */
268
269         /* For each variable name. */
270         for ( vars = variables; vars; vars = list_next( vars ) )
271         {
272             LIST * value = 0;
273             LIST * evalue = 0;
274             char * colon;
275             char * bracket;
276             string variable[1];
277             char * varname;
278             int sub1 = 0;
279             int sub2 = -1;
280             VAR_EDITS edits;
281
282             /* Look for a : modifier in the variable name. Must copy into
283              * varname so we can modify it.
284              */
285             string_copy( variable, vars->string );
286             varname = variable->value;
287
288             if ( ( colon = strchr( varname, MAGIC_COLON ) ) )
289             {
290                 string_truncate( variable, colon - varname );
291                 var_edit_parse( colon + 1, &edits );
292             }
293
294             /* Look for [x-y] subscripting. sub1 and sub2 are x and y. */
295             if ( ( bracket = strchr( varname, MAGIC_LEFT ) ) )
296             {
297                 /* Make all syntax errors in [] subscripting result in the same
298                  * behavior: silenty return an empty expansion (by setting sub2
299                  * = 0). Brute force parsing; May get moved into yacc someday.
300                  */
301
302                 char * s = bracket + 1;
303
304                 string_truncate( variable, bracket - varname );
305
306                 do  /* so we can use "break" */
307                 {
308                     /* Allow negative indexes. */
309                     if ( !isdigit( *s ) && ( *s != '-' ) )
310                     {
311                         sub2 = 0;
312                         break;
313                     }
314                     sub1 = atoi( s );
315
316                     /* Skip over the first symbol, which is either a digit or dash. */
317                     ++s;
318                     while ( isdigit( *s ) ) ++s;
319
320                     if ( *s == MAGIC_RIGHT )
321                     {
322                         sub2 = sub1;
323                         break;
324                     }
325
326                     if ( *s != '-' )
327                     {
328                         sub2 = 0;
329                         break;
330                     }
331
332                     ++s;
333
334                     if ( *s == MAGIC_RIGHT )
335                     {
336                         sub2 = -1;
337                         break;
338                     }
339
340                     if ( !isdigit( *s ) && ( *s != '-' ) )
341                     {
342                         sub2 = 0;
343                         break;
344                     }
345
346                     /* First, compute the index of the last element. */
347                     sub2 = atoi( s );
348                     while ( isdigit( *++s ) );
349
350                     if ( *s != MAGIC_RIGHT )
351                         sub2 = 0;
352
353                 } while ( 0 );
354
355                 /* Anything but the end of the string, or the colon introducing
356                  * a modifier is a syntax error.
357                  */
358                 ++s;
359                 if ( *s && ( *s != MAGIC_COLON ) )
360                     sub2 = 0;
361
362                 *bracket = '\0';
363             }
364
365             /* Get variable value, with special handling for $(<), $(>), $(n).
366              */
367             if ( !varname[1] )
368             {
369                 if ( varname[0] == '<' )
370                     value = lol_get( lol, 0 );
371                 else if ( varname[0] == '>' )
372                     value = lol_get( lol, 1 );
373                 else if ( ( varname[0] >= '1' ) && ( varname[0] <= '9' ) )
374                     value = lol_get( lol, varname[0] - '1' );
375                 else if( varname[0] == '1' && varname[1] >= '0' &&
376                      varname[1] <= '9' && !varname[2] )
377                 value = lol_get( lol, varname[1] - '0' + 10 - 1 );
378             }
379
380             if ( !value )
381                 value = var_get( varname );
382
383             /* Handle negitive indexes: part two. */
384             {
385                 int length = list_length( value );
386
387                 if ( sub1 < 0 )
388                     sub1 = length + sub1;
389                 else
390                     sub1 -= 1;
391
392                 if ( sub2 < 0 )
393                     sub2 = length + 1 + sub2 - sub1;
394                 else
395                     sub2 -= sub1;
396                 /* The "sub2 < 0" test handles the semantic error of sub2 <
397                  * sub1.
398                  */
399                 if ( sub2 < 0 )
400                     sub2 = 0;
401             }
402
403             /* The fast path: $(x) - just copy the variable value. This is only
404              * an optimization.
405              */
406             if ( ( out == out_buf ) && !bracket && !colon && ( in == end ) )
407             {
408                 string_free( variable );
409                 l = list_copy( l, value );
410                 continue;
411             }
412
413             /* Handle start subscript. */
414             while ( ( sub1 > 0 ) && value )
415                 --sub1, value = list_next( value );
416
417             /* Empty w/ :E=default?. */
418             if ( !value && colon && edits.empty.ptr )
419                 evalue = value = list_new( L0, newstr( edits.empty.ptr ) );
420
421             /* For each variable value. */
422             string_new( out1 );
423             for ( ; value; value = list_next( value ) )
424             {
425                 LIST * rem;
426                 size_t postfix_start;
427
428                 /* Handle end subscript (length actually). */
429
430                 if ( sub2 >= 0 && --sub2 < 0 )
431                     break;
432
433                 string_truncate( buf, prefix_length );
434
435                 /* Apply : mods, if present */
436
437                 if ( colon && edits.filemods )
438                     var_edit_file( value->string, out1, &edits );
439                 else
440                     string_append( out1, value->string );
441
442                 if ( colon && ( edits.upshift || edits.downshift || edits.to_slashes || edits.to_windows ) )
443                     var_edit_shift( out1, &edits );
444
445                 /* Handle :J=joinval */
446                 /* If we have more values for this var, just keep appending them
447                  * (using the join value) rather than creating separate LIST
448                  * elements.
449                  */
450                 if ( colon && edits.join.ptr &&
451                     ( list_next( value ) || list_next( vars ) ) )
452                 {
453                     string_append( out1, edits.join.ptr );
454                     continue;
455                 }
456
457                 string_append( buf, out1->value );
458                 string_free( out1 );
459                 string_new( out1 );
460
461                 /* If no remainder, append result to output chain. */
462                 if ( in == end )
463                 {
464                     l = list_new( l, newstr( buf->value ) );
465                     continue;
466                 }
467
468                 /* For each remainder, append the complete string to the output
469                  * chain. Remember the end of the variable expansion so we can
470                  * just tack on each instance of 'remainder'.
471                  */
472                 postfix_start = buf->size;
473                 for ( rem = remainder; rem; rem = list_next( rem ) )
474                 {
475                     string_truncate( buf, postfix_start );
476                     string_append( buf, rem->string );
477                     l = list_new( l, newstr( buf->value ) );
478                 }
479             }
480             string_free( out1 );
481
482             /* Toss used empty. */
483             if ( evalue )
484                 list_free( evalue );
485
486             string_free( variable );
487         }
488
489         /* variables & remainder were gifts from var_expand and must be freed. */
490         if ( variables ) list_free( variables );
491         if ( remainder ) list_free( remainder );
492
493         if ( DEBUG_VAREXP )
494         {
495             printf( "expanded to " );
496             list_print( l );
497             printf( "\n" );
498         }
499
500         string_free( buf );
501         return l;
502     }
503 }
504
505
506 /*
507  * var_edit_parse() - parse : modifiers into PATHNAME structure
508  *
509  * The : modifiers in a $(varname:modifier) currently support replacing or
510  * omitting elements of a filename, and so they are parsed into a PATHNAME
511  * structure (which contains pointers into the original string).
512  *
513  * Modifiers of the form "X=value" replace the component X with the given value.
514  * Modifiers without the "=value" cause everything but the component X to be
515  * omitted. X is one of:
516  *
517  *  G <grist>
518  *  D directory name
519  *  B base name
520  *  S .suffix
521  *  M (member)
522  *  R root directory - prepended to whole path
523  *
524  * This routine sets:
525  *
526  *  f->f_xxx.ptr = 0
527  *  f->f_xxx.len = 0
528  *      -> leave the original component xxx
529  *
530  *  f->f_xxx.ptr = string
531  *  f->f_xxx.len = strlen( string )
532  *      -> replace component xxx with string
533  *
534  *  f->f_xxx.ptr = ""
535  *  f->f_xxx.len = 0
536  *      -> omit component xxx
537  *
538  * var_edit_file() below and path_build() obligingly follow this convention.
539  */
540
541 static void var_edit_parse( char * mods, VAR_EDITS * edits )
542 {
543     int havezeroed = 0;
544     memset( (char *)edits, 0, sizeof( *edits ) );
545
546     while ( *mods )
547     {
548         char * p;
549         PATHPART * fp;
550
551         switch ( *mods++ )
552         {
553             case 'L': edits->downshift = 1; continue;
554             case 'U': edits->upshift = 1; continue;
555             case 'P': edits->parent = edits->filemods = 1; continue;
556             case 'E': fp = &edits->empty; goto strval;
557             case 'J': fp = &edits->join; goto strval;
558             case 'G': fp = &edits->f.f_grist; goto fileval;
559             case 'R': fp = &edits->f.f_root; goto fileval;
560             case 'D': fp = &edits->f.f_dir; goto fileval;
561             case 'B': fp = &edits->f.f_base; goto fileval;
562             case 'S': fp = &edits->f.f_suffix; goto fileval;
563             case 'M': fp = &edits->f.f_member; goto fileval;
564             case 'T': edits->to_slashes = 1; continue;
565             case 'W': edits->to_windows = 1; continue;
566             default:
567                 return;  /* Should complain, but so what... */
568         }
569
570     fileval:
571         /* Handle :CHARS, where each char (without a following =) selects a
572          * particular file path element. On the first such char, we deselect all
573          * others (by setting ptr = "", len = 0) and for each char we select
574          * that element (by setting ptr = 0).
575          */
576         edits->filemods = 1;
577
578         if ( *mods != '=' )
579         {
580             if ( !havezeroed++ )
581             {
582                 int i;
583                 for ( i = 0; i < 6; ++i )
584                 {
585                     edits->f.part[ i ].len = 0;
586                     edits->f.part[ i ].ptr = "";
587                 }
588             }
589
590             fp->ptr = 0;
591             continue;
592         }
593
594     strval:
595         /* Handle :X=value, or :X */
596         if ( *mods != '=' )
597         {
598             fp->ptr = "";
599             fp->len = 0;
600         }
601         else if ( ( p = strchr( mods, MAGIC_COLON ) ) )
602         {
603             *p = 0;
604             fp->ptr = ++mods;
605             fp->len = p - mods;
606             mods = p + 1;
607         }
608         else
609         {
610             fp->ptr = ++mods;
611             fp->len = strlen( mods );
612             mods += fp->len;
613         }
614     }
615 }
616
617
618 /*
619  * var_edit_file() - copy input target name to output, modifying filename.
620  */
621
622 static void var_edit_file( char * in, string * out, VAR_EDITS * edits )
623 {
624     PATHNAME pathname;
625
626     /* Parse apart original filename, putting parts into "pathname". */
627     path_parse( in, &pathname );
628
629     /* Replace any pathname with edits->f */
630     if ( edits->f.f_grist .ptr ) pathname.f_grist  = edits->f.f_grist;
631     if ( edits->f.f_root  .ptr ) pathname.f_root   = edits->f.f_root;
632     if ( edits->f.f_dir   .ptr ) pathname.f_dir    = edits->f.f_dir;
633     if ( edits->f.f_base  .ptr ) pathname.f_base   = edits->f.f_base;
634     if ( edits->f.f_suffix.ptr ) pathname.f_suffix = edits->f.f_suffix;
635     if ( edits->f.f_member.ptr ) pathname.f_member = edits->f.f_member;
636
637     /* If requested, modify pathname to point to parent. */
638     if ( edits->parent )
639         path_parent( &pathname );
640
641     /* Put filename back together. */
642     path_build( &pathname, out, 0 );
643 }
644
645
646 /*
647  * var_edit_shift() - do upshift/downshift mods.
648  */
649
650 static void var_edit_shift( string * out, VAR_EDITS * edits )
651 {
652     /* Handle upshifting, downshifting and slash translation now. */
653     char * p;
654     for ( p = out->value; *p; ++p)
655     {
656         if ( edits->upshift )
657             *p = toupper( *p );
658         else if ( edits->downshift )
659             *p = tolower( *p );
660         if ( edits->to_slashes && ( *p == '\\' ) )
661             *p = '/';
662 # ifdef OS_CYGWIN
663         if ( edits->to_windows )
664         {
665             char result[ MAX_PATH + 1 ];
666             cygwin_conv_to_win32_path( out->value, result );
667             assert( strlen( result ) <= MAX_PATH );
668             string_free( out );
669             string_copy( out, result );
670         }
671 # endif
672     }
673     out->size = p - out->value;
674 }
675
676
677 #ifndef NDEBUG
678 void var_expand_unit_test()
679 {
680     LOL lol[ 1 ];
681     LIST * l;
682     LIST * l2;
683     LIST * expected = list_new( list_new( L0, newstr( "axb" ) ), newstr( "ayb" ) );
684     LIST * e2;
685     char axyb[] = "a$(xy)b";
686     char azb[] = "a$($(z))b";
687     char path[] = "$(p:W)";
688
689 # ifdef OS_CYGWIN
690     char cygpath[ 256 ];
691     cygwin_conv_to_posix_path( "c:\\foo\\bar", cygpath );
692 # else
693     char cygpath[] = "/cygdrive/c/foo/bar";
694 # endif
695
696     lol_init(lol);
697     var_set( "xy", list_new( list_new( L0, newstr( "x" ) ), newstr( "y" ) ), VAR_SET );
698     var_set( "z", list_new( L0, newstr( "xy" ) ), VAR_SET );
699     var_set( "p", list_new( L0, newstr( cygpath ) ), VAR_SET );
700
701     l = var_expand( 0, axyb, axyb + sizeof( axyb ) - 1, lol, 0 );
702     for ( l2 = l, e2 = expected; l2 && e2; l2 = list_next( l2 ), e2 = list_next( e2 ) )
703         assert( !strcmp( e2->string, l2->string ) );
704     assert( l2 == 0 );
705     assert( e2 == 0 );
706     list_free( l );
707
708     l = var_expand( 0, azb, azb + sizeof( azb ) - 1, lol, 0 );
709     for ( l2 = l, e2 = expected; l2 && e2; l2 = list_next( l2 ), e2 = list_next( e2 ) )
710         assert( !strcmp( e2->string, l2->string ) );
711     assert( l2 == 0 );
712     assert( e2 == 0 );
713     list_free( l );
714
715     l = var_expand( 0, path, path + sizeof( path ) - 1, lol, 0 );
716     assert( l != 0 );
717     assert( list_next( l ) == 0 );
718 # ifdef OS_CYGWIN
719     /* On some installations of cygwin the drive letter is expanded to other
720      * case. This has been reported to be the case if cygwin has been installed
721      * to C:\ as opposed to C:\cygwin. Since case of the drive letter will not
722      * matter, we allow for both.
723      */
724     assert( !strcmp( l->string, "c:\\foo\\bar" ) ||
725             !strcmp( l->string, "C:\\foo\\bar" ) );
726 # else
727     assert( !strcmp( l->string, cygpath ) );
728 # endif
729     list_free( l );
730     list_free( expected );
731     lol_free( lol );
732 }
733 #endif