Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_33_1/tools/build/jam_src/expand.c @ 12

Last change on this file since 12 was 12, checked in by landauf, 18 years ago

added boost

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