📄 expand.c
字号:
sub2 -= sub1; /* ** The "sub2 < 0" test handles the semantic error ** of sub2 < sub1. */ if ( sub2 < 0 ) sub2 = 0; } /* The fast path: $(x) - just copy the variable value. */ /* This is only an optimization */ if( out == out_buf && !bracket && !colon && in == end ) { string_free( variable ); l = list_copy( l, value ); continue; } /* Handle start subscript */ while( sub1 > 0 && value ) --sub1, value = list_next( value ); /* Empty w/ :E=default? */ if( !value && colon && edits.empty.ptr ) evalue = value = list_new( L0, newstr( edits.empty.ptr ) ); /* For each variable value */ string_new( out1 ); for( ; value; value = list_next( value ) ) { LIST *rem; size_t postfix_start; /* Handle end subscript (length actually) */ if( sub2 >= 0 && --sub2 < 0 ) break; string_truncate( buf, prefix_length ); /* Apply : mods, if present */ if( colon && edits.filemods ) var_edit_file( value->string, out1, &edits ); else string_append( out1, value->string ); if( colon && ( edits.upshift || edits.downshift || edits.to_slashes || edits.to_windows ) ) var_edit_shift( out1, &edits ); /* Handle :J=joinval */ /* If we have more values for this var, just */ /* keep appending them (with the join value) */ /* rather than creating separate LIST elements. */ if( colon && edits.join.ptr && ( list_next( value ) || list_next( vars ) ) ) { string_append( out1, edits.join.ptr ); continue; } string_append( buf, out1->value ); string_free( out1 ); string_new( out1 ); /* If no remainder, append result to output chain. */ if( in == end ) { l = list_new( l, newstr( buf->value ) ); continue; } /* For each remainder, append the complete string */ /* to the output chain. */ /* Remember the end of the variable expansion so */ /* we can just tack on each instance of 'remainder' */ postfix_start = buf->size; for( rem = remainder; rem; rem = list_next( rem ) ) { string_truncate( buf, postfix_start ); string_append( buf, rem->string ); l = list_new( l, newstr( buf->value ) ); } } string_free( out1 ); /* Toss used empty */ if( evalue ) list_free( evalue ); string_free( variable ); } /* variables & remainder were gifts from var_expand */ /* and must be freed */ if( variables ) list_free( variables ); if( remainder) list_free( remainder ); if( DEBUG_VAREXP ) { printf( "expanded to " ); list_print( l ); printf( "\n" ); } string_free( buf ); return l; }}/* * var_edit_parse() - parse : modifiers into PATHNAME structure * * The : modifiers in a $(varname:modifier) currently support replacing * or omitting elements of a filename, and so they are parsed into a * PATHNAME structure (which contains pointers into the original string). * * Modifiers of the form "X=value" replace the component X with * the given value. Modifiers without the "=value" cause everything * but the component X to be omitted. X is one of: * * G <grist> * D directory name * B base name * S .suffix * M (member) * R root directory - prepended to whole path * * This routine sets: * * f->f_xxx.ptr = 0 * f->f_xxx.len = 0 * -> leave the original component xxx * * f->f_xxx.ptr = string * f->f_xxx.len = strlen( string ) * -> replace component xxx with string * * f->f_xxx.ptr = "" * f->f_xxx.len = 0 * -> omit component xxx * * var_edit_file() below and path_build() obligingly follow this convention. */static voidvar_edit_parse( char *mods, VAR_EDITS *edits ){ int havezeroed = 0; memset( (char *)edits, 0, sizeof( *edits ) ); while( *mods ) { char *p; PATHPART *fp; switch( *mods++ ) { case 'L': edits->downshift = 1; continue; case 'U': edits->upshift = 1; continue; case 'P': edits->parent = edits->filemods = 1; continue; case 'E': fp = &edits->empty; goto strval; case 'J': fp = &edits->join; goto strval; case 'G': fp = &edits->f.f_grist; goto fileval; case 'R': fp = &edits->f.f_root; goto fileval; case 'D': fp = &edits->f.f_dir; goto fileval; case 'B': fp = &edits->f.f_base; goto fileval; case 'S': fp = &edits->f.f_suffix; goto fileval; case 'M': fp = &edits->f.f_member; goto fileval; case 'T': edits->to_slashes = 1; continue; case 'W': edits->to_windows = 1; continue; default: return; /* should complain, but so what... */ } fileval: /* Handle :CHARS, where each char (without a following =) */ /* selects a particular file path element. On the first such */ /* char, we deselect all others (by setting ptr = "", len = 0) */ /* and for each char we select that element (by setting ptr = 0) */ edits->filemods = 1; if( *mods != '=' ) { int i; if( !havezeroed++ ) for( i = 0; i < 6; i++ ) { edits->f.part[ i ].len = 0; edits->f.part[ i ].ptr = ""; } fp->ptr = 0; continue; } strval: /* Handle :X=value, or :X */ if( *mods != '=' ) { fp->ptr = ""; fp->len = 0; } else if( p = strchr( mods, MAGIC_COLON ) ) { *p = 0; fp->ptr = ++mods; fp->len = p - mods; mods = p + 1; } else { fp->ptr = ++mods; fp->len = strlen( mods ); mods += fp->len; } }}/* * var_edit_file() - copy input target name to output, modifying filename */ static voidvar_edit_file( char *in, string *out, VAR_EDITS *edits ){ PATHNAME pathname; /* Parse apart original filename, putting parts into "pathname" */ path_parse( in, &pathname ); /* Replace any pathname with edits->f */ if( edits->f.f_grist.ptr ) pathname.f_grist = edits->f.f_grist; if( edits->f.f_root.ptr ) pathname.f_root = edits->f.f_root; if( edits->f.f_dir.ptr ) pathname.f_dir = edits->f.f_dir; if( edits->f.f_base.ptr ) pathname.f_base = edits->f.f_base; if( edits->f.f_suffix.ptr ) pathname.f_suffix = edits->f.f_suffix; if( edits->f.f_member.ptr ) pathname.f_member = edits->f.f_member; /* If requested, modify pathname to point to parent */ if( edits->parent ) path_parent( &pathname ); /* Put filename back together */ path_build( &pathname, out, 0 );}/* * var_edit_shift() - do upshift/downshift mods */static voidvar_edit_shift( string *out, VAR_EDITS *edits ){ /* Handle upshifting, downshifting and slash translation now */ char *p; for ( p = out->value; *p; ++p) { if (edits->upshift) { *p = toupper( *p ); } else if ( edits->downshift ) { *p = tolower( *p ); } if ( edits->to_slashes ) { if ( *p == '\\') *p = '/'; }# ifdef OS_CYGWIN if ( edits->to_windows ) { char result[MAX_PATH + 1]; cygwin_conv_to_win32_path(out->value, result); assert(strlen(result) <= MAX_PATH); string_free( out ); string_copy( out, result ); }# endif } out->size = p - out->value;}#ifndef NDEBUGvoid var_expand_unit_test(){ LOL lol[1]; LIST* l, *l2; LIST *expected = list_new( list_new( L0, newstr( "axb" ) ), newstr( "ayb" ) ); LIST *e2; char axyb[] = "a$(xy)b"; char azb[] = "a$($(z))b"; char path[] = "$(p:W)"; # ifdef OS_CYGWIN char cygpath[256]; cygwin_conv_to_posix_path("c:\\foo\\bar", cygpath); # else char cygpath[] = "/cygdrive/c/foo/bar"; # endif lol_init(lol); var_set("xy", list_new( list_new( L0, newstr( "x" ) ), newstr( "y" ) ), VAR_SET ); var_set("z", list_new( L0, newstr( "xy" ) ), VAR_SET ); var_set("p", list_new( L0, newstr( cygpath ) ), VAR_SET ); l = var_expand( 0, axyb, axyb + sizeof(axyb) - 1, lol, 0 ); for ( l2 = l, e2 = expected; l2 && e2; l2 = list_next(l2), e2 = list_next(e2) ) assert( !strcmp( e2->string, l2->string ) ); assert(l2 == 0 && e2 == 0); list_free(l); l = var_expand( 0, azb, azb + sizeof(azb) - 1, lol, 0 ); for ( l2 = l, e2 = expected; l2 && e2; l2 = list_next(l2), e2 = list_next(e2) ) assert( !strcmp( e2->string, l2->string ) ); assert(l2 == 0 && e2 == 0); list_free(l); l = var_expand( 0, path, path + sizeof(path) - 1, lol, 0 ); assert(l != 0); assert(list_next(l) == 0); # ifdef OS_CYGWIN assert( !strcmp( l->string, "c:\\foo\\bar" ) ); # else assert( !strcmp( l->string, cygpath ) ); # endif list_free(l); list_free(expected); lol_free(lol);}#endif/* Local Variables: tab-width: 8 End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -