📄 expand.c
字号:
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 void
var_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 void
var_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 void
var_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 NDEBUG
void 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)";
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( "/cygdrive/c/foo/bar" ) ), 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, "/cygdrive/c/foo/bar" ) );
# 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 + -