📄 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># include <stdlib.h># include <limits.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] == '\0' ) { switch( in[2] ) { case '<': return list_copy( l, lol_get( lol, 0 ) ); case '>': return list_copy( l, lol_get( lol, 1 ) ); case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return list_copy( l, lol_get( lol, in[2]-'1' ) ); } } /* Expand @() files, to single item plus accompanying file. */ if ( in[0] == '@' && in[1] == '(' && *(end-1) == ')' ) { /* We try the expansion until it fits within the propspective output buffer. */ char * at_buf = 0; int at_size = MAXJPATH; int at_len = 0; do { BJAM_FREE(at_buf); at_buf = (char*)BJAM_MALLOC_ATOMIC(at_size + 1); at_len = var_string( in, at_buf, at_size, lol ); at_size *= 2; } while ( at_len < 0 && at_size < INT_MAX / 2 ); /* Return the result as a single item list. */ if ( at_len > 0 ) { LIST * r; string_copy( buf, at_buf ); r = list_new( l, newstr( buf->value) ); string_free( buf ); BJAM_FREE(at_buf); return r; } BJAM_FREE(at_buf); } /* 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -