Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_34_1/tools/jam/src/expand.c @ 29

Last change on this file since 29 was 29, checked in by landauf, 17 years ago

updated boost from 1_33_1 to 1_34_1

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