The Machine Perception Toolbox

[Introduction]- [News]- [Download]- [Screenshots]- [Manual (pdf)]- [Forums]- [API Reference]- [Repository ]

 

Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

expand.c File Reference

#include "jam.h"
#include "lists.h"
#include "variable.h"
#include "expand.h"
#include "pathsys.h"
#include "newstr.h"
#include <assert.h>

Include dependency graph for expand.c:

Include dependency graph

Go to the source code of this file.

Classes

struct  VAR_EDITS

Defines

#define MAGIC_COLON   '\001'
#define MAGIC_LEFT   '\002'
#define MAGIC_RIGHT   '\003'

Functions

void var_edit_file (char *in, string *out, VAR_EDITS *edits)
void var_edit_parse (char *mods, VAR_EDITS *edits)
void var_edit_shift (string *out, VAR_EDITS *edits)
LISTvar_expand (LIST *l, char *in, char *end, LOL *lol, int cancopyin)
void var_expand_unit_test ()


Define Documentation

#define MAGIC_COLON   '\001'
 

Definition at line 48 of file expand.c.

Referenced by var_edit_parse(), and var_expand().

#define MAGIC_LEFT   '\002'
 

Definition at line 49 of file expand.c.

Referenced by var_expand().

#define MAGIC_RIGHT   '\003'
 

Definition at line 50 of file expand.c.


Function Documentation

void var_edit_file char *  in,
string out,
VAR_EDITS edits
[static]
 

Definition at line 586 of file expand.c.

References VAR_EDITS::f, VAR_EDITS::parent, path_build(), path_parent(), path_parse(), and PATHNAME.

Referenced by var_expand().

00590 {
00591         PATHNAME pathname;
00592 
00593         /* Parse apart original filename, putting parts into "pathname" */
00594 
00595         path_parse( in, &pathname );
00596 
00597         /* Replace any pathname with edits->f */
00598 
00599         if( edits->f.f_grist.ptr )
00600             pathname.f_grist = edits->f.f_grist;
00601 
00602         if( edits->f.f_root.ptr )
00603             pathname.f_root = edits->f.f_root;
00604 
00605         if( edits->f.f_dir.ptr )
00606             pathname.f_dir = edits->f.f_dir;
00607 
00608         if( edits->f.f_base.ptr )
00609             pathname.f_base = edits->f.f_base;
00610 
00611         if( edits->f.f_suffix.ptr )
00612             pathname.f_suffix = edits->f.f_suffix;
00613 
00614         if( edits->f.f_member.ptr )
00615             pathname.f_member = edits->f.f_member;
00616 
00617         /* If requested, modify pathname to point to parent */
00618 
00619         if( edits->parent )
00620             path_parent( &pathname );
00621 
00622         /* Put filename back together */
00623 
00624     path_build( &pathname, out, 0 );
00625 }

Here is the call graph for this function:

void var_edit_parse char *  mods,
VAR_EDITS edits
[static]
 

Definition at line 501 of file expand.c.

References i, _pathpart::len, MAGIC_COLON, p, PATHPART, and _pathpart::ptr.

Referenced by var_expand().

00504 {
00505         int havezeroed = 0;
00506         memset( (char *)edits, 0, sizeof( *edits ) );
00507 
00508         while( *mods )
00509         {
00510             char *p;
00511             PATHPART *fp;
00512 
00513             switch( *mods++ )
00514             {
00515             case 'L': edits->downshift = 1; continue;
00516             case 'U': edits->upshift = 1; continue;
00517             case 'P': edits->parent = edits->filemods = 1; continue;
00518             case 'E': fp = &edits->empty; goto strval;
00519             case 'J': fp = &edits->join; goto strval;
00520             case 'G': fp = &edits->f.f_grist; goto fileval;
00521             case 'R': fp = &edits->f.f_root; goto fileval;
00522             case 'D': fp = &edits->f.f_dir; goto fileval;
00523             case 'B': fp = &edits->f.f_base; goto fileval;
00524             case 'S': fp = &edits->f.f_suffix; goto fileval;
00525             case 'M': fp = &edits->f.f_member; goto fileval;
00526             case 'T': edits->to_slashes = 1; continue;
00527             case 'W': edits->to_windows = 1; continue;
00528 
00529             default: return; /* should complain, but so what... */
00530             }
00531 
00532         fileval:
00533 
00534             /* Handle :CHARS, where each char (without a following =) */
00535             /* selects a particular file path element.  On the first such */
00536             /* char, we deselect all others (by setting ptr = "", len = 0) */
00537             /* and for each char we select that element (by setting ptr = 0) */
00538 
00539             edits->filemods = 1;
00540 
00541             if( *mods != '=' )
00542             {
00543                 int i;
00544 
00545                 if( !havezeroed++ )
00546                     for( i = 0; i < 6; i++ )
00547                 {
00548                     edits->f.part[ i ].len = 0;
00549                     edits->f.part[ i ].ptr = "";
00550                 }
00551 
00552                 fp->ptr = 0;
00553                 continue;
00554             }
00555 
00556         strval:
00557 
00558             /* Handle :X=value, or :X */
00559 
00560             if( *mods != '=' )
00561             {
00562                 fp->ptr = "";
00563                 fp->len = 0;
00564             }
00565             else if( p = strchr( mods, MAGIC_COLON ) )
00566             {
00567                 *p = 0;
00568                 fp->ptr = ++mods;
00569                 fp->len = p - mods;
00570                 mods = p + 1;
00571             }
00572             else
00573             {
00574                 fp->ptr = ++mods;
00575                 fp->len = strlen( mods );
00576                 mods += fp->len;
00577             }
00578         }
00579 }

void var_edit_shift string out,
VAR_EDITS edits
[static]
 

Definition at line 632 of file expand.c.

References VAR_EDITS::downshift, p, string::size, string_copy(), string_free(), VAR_EDITS::to_slashes, VAR_EDITS::to_windows, VAR_EDITS::upshift, and string::value.

Referenced by var_expand().

00635 {
00636         /* Handle upshifting, downshifting and slash translation now */
00637 
00638     char *p;
00639     for ( p = out->value; *p; ++p)
00640     {
00641         if (edits->upshift)
00642         {
00643             *p = toupper( *p );
00644         }
00645         else if ( edits->downshift )
00646         {
00647             *p = tolower( *p );
00648         } 
00649         if ( edits->to_slashes )
00650         {
00651             if ( *p == '\\')
00652                 *p = '/';
00653         }
00654 # ifdef OS_CYGWIN
00655         if ( edits->to_windows )
00656         {
00657             char result[MAX_PATH + 1];
00658             cygwin_conv_to_win32_path(out->value, result);
00659             assert(strlen(result) <= MAX_PATH);
00660             string_free( out );
00661             string_copy( out, result );
00662         }
00663 # endif
00664     }
00665     out->size = p - out->value;
00666 }

Here is the call graph for this function:

LIST* var_expand LIST l,
char *  in,
char *  end,
LOL lol,
int  cancopyin
 

Definition at line 65 of file expand.c.

References copystr(), VAR_EDITS::downshift, VAR_EDITS::empty, end, VAR_EDITS::filemods, VAR_EDITS::join, L0, LIST, list_copy(), list_free(), list_length(), list_new(), list_next, list_print(), LOL, lol_get(), MAGIC_COLON, MAGIC_LEFT, newstr(), _pathpart::ptr, r, s, string::size, _list::string, string_append(), string_append_range(), string_copy(), string_free(), string_new(), string_truncate(), VAR_EDITS::to_slashes, VAR_EDITS::to_windows, VAR_EDITS::upshift, string::value, var_edit_file(), var_edit_parse(), var_edit_shift(), and var_get().

Referenced by compile_list(), evaluate_rule(), var_expand_unit_test(), and var_string().

00071 {
00072     char out_buf[ MAXSYM ];
00073     string buf[1];
00074     string out1[1]; /* Temporary buffer */
00075     size_t prefix_length;
00076     char *out;
00077     char *inp = in;
00078     char *ov;           /* for temp copy of variable in outbuf */
00079     int depth;
00080 
00081     if( DEBUG_VAREXP )
00082         printf( "expand '%.*s'\n", end - in, in );
00083 
00084     /* This gets alot of cases: $(<) and $(>) */
00085 
00086     if( in[0] == '$' && in[1] == '(' && in[3] == ')' && !in[4] )
00087     {
00088         switch( in[2] )
00089         {
00090         case '1':
00091         case '<':
00092             return list_copy( l, lol_get( lol, 0 ) );
00093 
00094         case '2':
00095         case '>':
00096             return list_copy( l, lol_get( lol, 1 ) );
00097         }
00098     }
00099 
00100     /* Just try simple copy of in to out. */
00101 
00102     while( in < end )
00103         if( *in++ == '$' && *in == '(' ) 
00104             goto expand;
00105 
00106     /* No variables expanded - just add copy of input string to list. */
00107 
00108     /* Cancopyin is an optimization: if the input was already a list */
00109     /* item, we can use the copystr() to put it on the new list. */
00110     /* Otherwise, we use the slower newstr(). */
00111 
00112     if( cancopyin ) 
00113     {
00114         return list_new( l, copystr( inp ) );
00115     }
00116     else
00117     {
00118         LIST* r;
00119         string_new( buf );
00120         string_append_range( buf, inp, end );
00121 
00122         r = list_new( l, newstr( buf->value) );
00123         string_free( buf );
00124         return r;
00125     }
00126 
00127 expand:
00128     string_new( buf );
00129     string_append_range( buf, inp, in - 1); /* copy the part before '$'. */
00130     /*
00131      * Input so far (ignore blanks):
00132      *
00133      *  stuff-in-outbuf $(variable) remainder
00134      *                   ^                   ^
00135      *                   in                  end
00136      * Output so far:
00137      *
00138      *  stuff-in-outbuf $
00139      *  ^                ^
00140      *  out_buf          out
00141      *
00142      *
00143      * We just copied the $ of $(...), so back up one on the output.
00144      * We now find the matching close paren, copying the variable and
00145      * modifiers between the $( and ) temporarily into out_buf, so that
00146      * we can replace :'s with MAGIC_COLON.  This is necessary to avoid
00147      * being confused by modifier values that are variables containing
00148      * :'s.  Ugly.
00149      */
00150 
00151     depth = 1;
00152     inp = ++in; /* skip over the '(' */
00153 
00154     while( in < end && depth )
00155     {
00156         switch( *in++ )
00157         {
00158         case '(': depth++; break;
00159         case ')': depth--; break;
00160         }
00161     }
00162 
00163     /*
00164      * Input so far (ignore blanks):
00165      *
00166      *  stuff-in-outbuf $(variable) remainder
00167      *                    ^        ^         ^
00168      *                    inp      in        end
00169      */
00170     prefix_length = buf->size;
00171     string_append_range( buf, inp, in - 1 );
00172 
00173     out = buf->value + prefix_length;
00174     for ( ov = out; ov < buf->value + buf->size; ++ov )
00175     {
00176         switch( *ov )
00177         {
00178         case ':': *ov = MAGIC_COLON; break;
00179         case '[': *ov = MAGIC_LEFT; break;
00180         case ']': *ov = MAGIC_RIGHT; break;
00181         }
00182     }
00183 
00184     /*
00185      * Input so far (ignore blanks):
00186      *
00187      *  stuff-in-outbuf $(variable) remainder
00188      *                              ^        ^
00189      *                              in       end
00190      * Output so far:
00191      *
00192      *  stuff-in-outbuf variable
00193      *  ^               ^       ^
00194      *  out_buf         out     ov
00195      *
00196      * Later we will overwrite 'variable' in out_buf, but we'll be
00197      * done with it by then.  'variable' may be a multi-element list, 
00198      * so may each value for '$(variable element)', and so may 'remainder'.
00199      * Thus we produce a product of three lists.
00200      */
00201 
00202     {
00203         LIST *variables = 0;
00204         LIST *remainder = 0;
00205         LIST *vars;
00206 
00207         /* Recursively expand variable name & rest of input */
00208 
00209         if( out < ov )
00210             variables = var_expand( L0, out, ov, lol, 0 );
00211         if( in < end )
00212             remainder = var_expand( L0, in, end, lol, 0 );
00213 
00214         /* Now produce the result chain */
00215 
00216         /* For each variable name */
00217 
00218         for( vars = variables; vars; vars = list_next( vars ) )
00219         {
00220             LIST *value, *evalue = 0;
00221             char *colon;
00222             char *bracket;
00223             string variable[1];
00224             char *varname;
00225             int sub1 = 0, sub2 = -1;
00226             VAR_EDITS edits;
00227 
00228             /* Look for a : modifier in the variable name */
00229             /* Must copy into varname so we can modify it */
00230 
00231             string_copy( variable, vars->string );
00232             varname = variable->value;
00233 
00234             if( colon = strchr( varname, MAGIC_COLON ) )
00235             {
00236                 string_truncate( variable, colon - varname );
00237                 var_edit_parse( colon + 1, &edits );
00238             }
00239 
00240             /* Look for [x-y] subscripting */
00241             /* sub1 and sub2 are x and y. */
00242 
00243             if ( bracket = strchr( varname, MAGIC_LEFT ) )
00244             {
00245                 /*
00246                 ** Make all syntax errors in [] subscripting
00247                 ** result in the same behavior: silenty return an empty
00248                 ** expansion (by setting sub2 = 0). Brute force parsing;
00249                 ** May get moved into yacc someday.
00250                 */
00251 
00252                 char *s = bracket + 1;
00253 
00254                 string_truncate( variable, bracket - varname );
00255 
00256                 do  /* so we can use "break" */
00257                 {
00258                     /* Allow negative indexes. */
00259                     if (! isdigit( *s ) && ! ( *s == '-') )
00260                     {
00261                         sub2 = 0;
00262                         break;
00263                     }
00264                     sub1 = atoi(s);
00265 
00266                     /* Skip over the first symbol, which is either a digit or dash. */
00267                     s++;
00268                     while ( isdigit( *s ) ) s++;
00269 
00270                     if ( *s == MAGIC_RIGHT )
00271                     {
00272                         sub2 = sub1;
00273                         break;
00274                     }
00275 
00276                     if ( *s != '-')
00277                     {
00278                         sub2 = 0;
00279                         break;
00280                     }
00281 
00282                     s++;
00283 
00284                     if ( *s == MAGIC_RIGHT )
00285                     {
00286                         sub2 = -1;
00287                         break;
00288                     }
00289 
00290                     if (! isdigit( *s ) && ! ( *s == '-') )
00291                     {
00292                         sub2 = 0;
00293                         break;
00294                     }
00295 
00296                     /* First, compute the index of the last element. */
00297                     sub2 = atoi(s);               
00298                     s++;
00299                     while ( isdigit( *s ) ) s++;
00300 
00301                     if ( *s != MAGIC_RIGHT)
00302                         sub2 = 0;
00303 
00304                 } while (0);
00305 
00306                 /*
00307                 ** Anything but the end of the string, or the colon
00308                 ** introducing a modifier is a syntax error.
00309                 */
00310 
00311                 s++;                
00312                 if (*s && *s != MAGIC_COLON)
00313                     sub2 = 0;
00314 
00315                 *bracket = '\0';
00316             }
00317 
00318             /* Get variable value, specially handling $(<), $(>), $(n) */
00319                 
00320             if( varname[0] == '<' && !varname[1] )
00321                 value = lol_get( lol, 0 );
00322             else if( varname[0] == '>' && !varname[1] )
00323                 value = lol_get( lol, 1 );
00324             else if( varname[0] >= '1' && varname[0] <= '9' && !varname[1] )
00325                 value = lol_get( lol, varname[0] - '1' );
00326             else 
00327                 value = var_get( varname );
00328 
00329             /* Handle negitive indexes: part two. */
00330             {
00331                 int length = list_length( value );
00332 
00333                 if (sub1 < 0)
00334                     sub1 = length + sub1;
00335                 else
00336                     sub1 -= 1;
00337 
00338                 if (sub2 < 0)
00339                     sub2 = length + 1 + sub2 - sub1;
00340                 else
00341                     sub2 -= sub1;
00342                 /*
00343                 ** The "sub2 < 0" test handles the semantic error
00344                 ** of sub2 < sub1.
00345                 */
00346                 if ( sub2 < 0 )
00347                     sub2 = 0;
00348             }
00349 
00350 
00351 
00352             /* The fast path: $(x) - just copy the variable value. */
00353             /* This is only an optimization */
00354 
00355             if( out == out_buf && !bracket && !colon && in == end )
00356             {
00357                 string_free( variable );
00358                 l = list_copy( l, value );
00359                 continue;
00360             }
00361 
00362             /* Handle start subscript */
00363 
00364             while( sub1 > 0 && value )
00365                 --sub1, value = list_next( value );
00366 
00367             /* Empty w/ :E=default? */
00368 
00369             if( !value && colon && edits.empty.ptr )
00370                 evalue = value = list_new( L0, newstr( edits.empty.ptr ) );
00371 
00372             /* For each variable value */
00373 
00374             string_new( out1 );
00375             for( ; value; value = list_next( value ) )
00376             {
00377                 LIST *rem;
00378                 size_t postfix_start;
00379 
00380                 /* Handle end subscript (length actually) */
00381 
00382                 if( sub2 >= 0 && --sub2 < 0 )
00383                     break;
00384 
00385                 string_truncate( buf, prefix_length );
00386 
00387                 /* Apply : mods, if present */
00388 
00389                 if( colon && edits.filemods )
00390                     var_edit_file( value->string, out1, &edits );
00391                 else
00392                     string_append( out1, value->string );
00393 
00394                 if( colon && ( edits.upshift || edits.downshift || edits.to_slashes || edits.to_windows ) )
00395                     var_edit_shift( out1, &edits );
00396 
00397                 /* Handle :J=joinval */
00398                 /* If we have more values for this var, just */
00399                 /* keep appending them (with the join value) */
00400                 /* rather than creating separate LIST elements. */
00401 
00402                 if( colon && edits.join.ptr && 
00403                     ( list_next( value ) || list_next( vars ) ) )
00404                 {
00405                     string_append( out1, edits.join.ptr );
00406                     continue;
00407                 }
00408 
00409                 string_append( buf, out1->value );
00410                 string_free( out1 );
00411                 string_new( out1 );
00412 
00413                 /* If no remainder, append result to output chain. */
00414 
00415                 if( in == end )
00416                 {
00417                     l = list_new( l, newstr( buf->value ) );
00418                     continue;
00419                 }
00420 
00421                 /* For each remainder, append the complete string */
00422                 /* to the output chain. */
00423                 /* Remember the end of the variable expansion so */
00424                 /* we can just tack on each instance of 'remainder' */
00425 
00426                 postfix_start = buf->size;
00427 
00428                 for( rem = remainder; rem; rem = list_next( rem ) )
00429                 {
00430                     string_truncate( buf, postfix_start );
00431                     string_append( buf, rem->string );
00432                     l = list_new( l, newstr( buf->value ) );
00433                 }
00434             }
00435             string_free( out1 );
00436 
00437             /* Toss used empty */
00438 
00439             if( evalue )
00440                 list_free( evalue );
00441 
00442             string_free( variable );
00443         }
00444 
00445         /* variables & remainder were gifts from var_expand */
00446         /* and must be freed */
00447 
00448         if( variables )
00449             list_free( variables );
00450         if( remainder)
00451             list_free( remainder );
00452 
00453         if( DEBUG_VAREXP )
00454         {
00455             printf( "expanded to " );
00456             list_print( l );
00457             printf( "\n" );
00458         }
00459 
00460         string_free( buf );
00461         return l;
00462     }
00463 }

Here is the call graph for this function:

void var_expand_unit_test  ) 
 

Definition at line 669 of file expand.c.

References L0, LIST, list_free(), list_new(), list_next, LOL, lol_free(), lol_init(), newstr(), _list::string, var_expand(), var_set(), and VAR_SET.

Referenced by run_unit_tests().

00670 {
00671     LOL lol[1];
00672     LIST* l, *l2;
00673     LIST *expected = list_new( list_new( L0, newstr( "axb" ) ), newstr( "ayb" ) );
00674     LIST *e2;
00675     char axyb[] = "a$(xy)b";
00676     char azb[] = "a$($(z))b";
00677     char path[] = "$(p:W)";
00678         
00679     lol_init(lol);
00680     var_set("xy", list_new( list_new( L0, newstr( "x" ) ), newstr( "y" ) ), VAR_SET );
00681     var_set("z", list_new( L0, newstr( "xy" ) ), VAR_SET );
00682     var_set("p", list_new( L0, newstr( "/cygdrive/c/foo/bar" ) ), VAR_SET );
00683 
00684     l = var_expand( 0, axyb, axyb + sizeof(axyb) - 1, lol, 0 );
00685     for ( l2 = l, e2 = expected; l2 && e2; l2 = list_next(l2), e2 = list_next(e2) )
00686         assert( !strcmp( e2->string, l2->string ) );
00687     assert(l2 == 0 && e2 == 0);
00688     list_free(l);
00689     
00690     l = var_expand( 0, azb, azb + sizeof(azb) - 1, lol, 0 );
00691     for ( l2 = l, e2 = expected; l2 && e2; l2 = list_next(l2), e2 = list_next(e2) )
00692         assert( !strcmp( e2->string, l2->string ) );
00693     assert(l2 == 0 && e2 == 0);
00694     list_free(l);
00695 
00696     l = var_expand( 0, path, path + sizeof(path) - 1, lol, 0 );
00697     assert(l != 0);
00698     assert(list_next(l) == 0);
00699 # ifdef OS_CYGWIN
00700     assert( !strcmp( l->string, "c:\\foo\\bar" ) );
00701 # else 
00702     assert( !strcmp( l->string, "/cygdrive/c/foo/bar" ) );
00703 # endif   
00704     list_free(l);
00705 
00706     list_free(expected);
00707     
00708     lol_free(lol);
00709 }

Here is the call graph for this function:


Generated on Mon Nov 8 17:07:58 2004 for MPT by  doxygen 1.3.9.1