📄 expand.c
字号:
/*
* Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
# include "jam.h"
# include "lists.h"
# include "variable.h"
# include "expand.h"
# include "pathsys.h"
# include "newstr.h"
# include <assert.h>
# ifdef OS_CYGWIN
# include <sys/cygwin.h>
# include <windows.h>
# endif
/*
* expand.c - expand a buffer, given variable values
*
* External routines:
*
* var_expand() - variable-expand input string into list of strings
*
* Internal routines:
*
* var_edit_parse() - parse : modifiers into PATHNAME structure
* var_edit_file() - copy input target name to output, modifying filename
* var_edit_shift() - do upshift/downshift mods
*
* 01/25/94 (seiwald) - $(X)$(UNDEF) was expanding like plain $(X)
* 04/13/94 (seiwald) - added shorthand L0 for null list pointer
* 01/11/01 (seiwald) - added support for :E=emptyvalue, :J=joinval
*/
typedef struct {
PATHNAME f; /* :GDBSMR -- pieces */
char parent; /* :P -- go to parent directory */
char filemods; /* one of the above applied */
char downshift; /* :L -- downshift result */
char upshift; /* :U -- upshift result */
char to_slashes; /* :T -- convert "\" to "/" */
char to_windows; /* :W -- convert cygwin to native paths */
PATHPART empty; /* :E -- default for empties */
PATHPART join; /* :J -- join list with char */
} VAR_EDITS ;
static void var_edit_parse( char *mods, VAR_EDITS *edits );
static void var_edit_file( char *in, string *out, VAR_EDITS *edits );
static void var_edit_shift( string *out, VAR_EDITS *edits );
# define MAGIC_COLON '\001'
# define MAGIC_LEFT '\002'
# define MAGIC_RIGHT '\003'
/*
* var_expand() - variable-expand input string into list of strings
*
* Would just copy input to output, performing variable expansion,
* except that since variables can contain multiple values the result
* of variable expansion may contain multiple values (a list). Properly
* performs "product" operations that occur in "$(var1)xxx$(var2)" or
* even "$($(var2))".
*
* Returns a newly created list.
*/
LIST *
var_expand(
LIST *l,
char *in,
char *end,
LOL *lol,
int cancopyin )
{
char out_buf[ MAXSYM ];
string buf[1];
string out1[1]; /* Temporary buffer */
size_t prefix_length;
char *out;
char *inp = in;
char *ov; /* for temp copy of variable in outbuf */
int depth;
if( DEBUG_VAREXP )
printf( "expand '%.*s'\n", end - in, in );
/* This gets alot of cases: $(<) and $(>) */
if( in[0] == '$' && in[1] == '(' && in[3] == ')' && !in[4] )
{
switch( in[2] )
{
case '1':
case '<':
return list_copy( l, lol_get( lol, 0 ) );
case '2':
case '>':
return list_copy( l, lol_get( lol, 1 ) );
}
}
/* Just try simple copy of in to out. */
while( in < end )
if( *in++ == '$' && *in == '(' )
goto expand;
/* No variables expanded - just add copy of input string to list. */
/* Cancopyin is an optimization: if the input was already a list */
/* item, we can use the copystr() to put it on the new list. */
/* Otherwise, we use the slower newstr(). */
if( cancopyin )
{
return list_new( l, copystr( inp ) );
}
else
{
LIST* r;
string_new( buf );
string_append_range( buf, inp, end );
r = list_new( l, newstr( buf->value) );
string_free( buf );
return r;
}
expand:
string_new( buf );
string_append_range( buf, inp, in - 1); /* copy the part before '$'. */
/*
* Input so far (ignore blanks):
*
* stuff-in-outbuf $(variable) remainder
* ^ ^
* in end
* Output so far:
*
* stuff-in-outbuf $
* ^ ^
* out_buf out
*
*
* We just copied the $ of $(...), so back up one on the output.
* We now find the matching close paren, copying the variable and
* modifiers between the $( and ) temporarily into out_buf, so that
* we can replace :'s with MAGIC_COLON. This is necessary to avoid
* being confused by modifier values that are variables containing
* :'s. Ugly.
*/
depth = 1;
inp = ++in; /* skip over the '(' */
while( in < end && depth )
{
switch( *in++ )
{
case '(': depth++; break;
case ')': depth--; break;
}
}
/*
* Input so far (ignore blanks):
*
* stuff-in-outbuf $(variable) remainder
* ^ ^ ^
* inp in end
*/
prefix_length = buf->size;
string_append_range( buf, inp, in - 1 );
out = buf->value + prefix_length;
for ( ov = out; ov < buf->value + buf->size; ++ov )
{
switch( *ov )
{
case ':': *ov = MAGIC_COLON; break;
case '[': *ov = MAGIC_LEFT; break;
case ']': *ov = MAGIC_RIGHT; break;
}
}
/*
* Input so far (ignore blanks):
*
* stuff-in-outbuf $(variable) remainder
* ^ ^
* in end
* Output so far:
*
* stuff-in-outbuf variable
* ^ ^ ^
* out_buf out ov
*
* Later we will overwrite 'variable' in out_buf, but we'll be
* done with it by then. 'variable' may be a multi-element list,
* so may each value for '$(variable element)', and so may 'remainder'.
* Thus we produce a product of three lists.
*/
{
LIST *variables = 0;
LIST *remainder = 0;
LIST *vars;
/* Recursively expand variable name & rest of input */
if( out < ov )
variables = var_expand( L0, out, ov, lol, 0 );
if( in < end )
remainder = var_expand( L0, in, end, lol, 0 );
/* Now produce the result chain */
/* For each variable name */
for( vars = variables; vars; vars = list_next( vars ) )
{
LIST *value, *evalue = 0;
char *colon;
char *bracket;
string variable[1];
char *varname;
int sub1 = 0, sub2 = -1;
VAR_EDITS edits;
/* Look for a : modifier in the variable name */
/* Must copy into varname so we can modify it */
string_copy( variable, vars->string );
varname = variable->value;
if( colon = strchr( varname, MAGIC_COLON ) )
{
string_truncate( variable, colon - varname );
var_edit_parse( colon + 1, &edits );
}
/* Look for [x-y] subscripting */
/* sub1 and sub2 are x and y. */
if ( bracket = strchr( varname, MAGIC_LEFT ) )
{
/*
** Make all syntax errors in [] subscripting
** result in the same behavior: silenty return an empty
** expansion (by setting sub2 = 0). Brute force parsing;
** May get moved into yacc someday.
*/
char *s = bracket + 1;
string_truncate( variable, bracket - varname );
do /* so we can use "break" */
{
/* Allow negative indexes. */
if (! isdigit( *s ) && ! ( *s == '-') )
{
sub2 = 0;
break;
}
sub1 = atoi(s);
/* Skip over the first symbol, which is either a digit or dash. */
s++;
while ( isdigit( *s ) ) s++;
if ( *s == MAGIC_RIGHT )
{
sub2 = sub1;
break;
}
if ( *s != '-')
{
sub2 = 0;
break;
}
s++;
if ( *s == MAGIC_RIGHT )
{
sub2 = -1;
break;
}
if (! isdigit( *s ) && ! ( *s == '-') )
{
sub2 = 0;
break;
}
/* First, compute the index of the last element. */
sub2 = atoi(s);
s++;
while ( isdigit( *s ) ) s++;
if ( *s != MAGIC_RIGHT)
sub2 = 0;
} while (0);
/*
** Anything but the end of the string, or the colon
** introducing a modifier is a syntax error.
*/
s++;
if (*s && *s != MAGIC_COLON)
sub2 = 0;
*bracket = '\0';
}
/* Get variable value, specially handling $(<), $(>), $(n) */
if( varname[0] == '<' && !varname[1] )
value = lol_get( lol, 0 );
else if( varname[0] == '>' && !varname[1] )
value = lol_get( lol, 1 );
else if( varname[0] >= '1' && varname[0] <= '9' && !varname[1] )
value = lol_get( lol, varname[0] - '1' );
else
value = var_get( varname );
/* Handle negitive indexes: part two. */
{
int length = list_length( value );
if (sub1 < 0)
sub1 = length + sub1;
else
sub1 -= 1;
if (sub2 < 0)
sub2 = length + 1 + sub2 - sub1;
else
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 )
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -