LIST * headers1( const char *file, LIST *hdrscan ) { FILE *f; LIST *result = 0; LIST *hdrpipe; LIST *hdrpipefile; if ( list_first(hdrpipe = var_get( "HDRPIPE" )) ) { LOL args; BUFFER buff; lol_init( &args ); lol_add( &args, list_append( L0, file, 0 ) ); buffer_init( &buff ); if ( var_string( list_value(list_first(hdrpipe)), &buff, 0, &args, ' ') < 0 ) { printf( "Cannot expand HDRPIPE '%s' !\n", list_value(list_first(hdrpipe)) ); exit( EXITBAD ); } buffer_addchar( &buff, 0 ); if ( !( f = file_popen( (const char*)buffer_ptr( &buff ), "r" ) ) ) { buffer_free( &buff ); return result; } buffer_free( &buff ); lol_free( &args ); } else { if( !( f = fopen( file, "r" ) ) ) return result; } result = headers1helper( f, hdrscan ); if ( list_first(hdrpipe) ) file_pclose( f ); else fclose( f ); if ( list_first(hdrpipefile = var_get( "HDRPIPEFILE" )) ) { if( !( f = fopen( list_value(list_first(hdrpipefile)), "r" ) ) ) return result; result = headers1helper( f, hdrscan ); fclose( f ); } return result; }
LIST * evaluate_rule( const char *rulename, LOL *args, LIST *result ) { #ifdef OPT_EXPAND_RULE_NAMES_EXT RULE *rule; char *expanded; char *c; int i; BUFFER buff; buffer_init( &buff ); if( (i = var_string( rulename, &buff, 0, args, ' ' )) < 0 ) { printf( "Failed to expand rule %s -- expansion too long\n", rulename ); exit( EXITBAD ); } expanded = buffer_ptr( &buff ); while ( expanded[0] == ' ' ) expanded++; while ( (c = strrchr(expanded, ' ')) ) *c = '\0'; if( DEBUG_COMPILE ) { debug_compile( 1, rulename ); if ( strcmp(rulename, expanded) ) printf( "-> %s ", expanded ); lol_print( args ); printf( "\n" ); } rule = bindrule( expanded ); #else RULE *rule = bindrule( rulename ); if( DEBUG_COMPILE ) { debug_compile( 1, rulename ); lol_print( args ); printf( "\n" ); } #endif #ifdef OPT_LOAD_MISSING_RULE_EXT if( !rule->actions && !rule->procedure ) { if( ruleexists( "FindMissingRule" ) ) { LOL lol; LIST *args = list_append( L0, expanded, 0 ); LIST *result; lol_init( &lol ); lol_add( &lol, args ); result = evaluate_rule( "FindMissingRule", &lol, L0 ); lol_free( &lol ); if( list_first( result ) ) { rule = bindrule( list_value( list_first( result ) ) ); } list_free( result ); } } #endif /* OPT_LOAD_MISSING_RULE_EXT */ /* Check traditional targets $(<) and sources $(>) */ #ifdef OPT_IMPROVED_WARNINGS_EXT if( !rule->actions && !rule->procedure && !globs.silence ) printf( "warning: unknown rule %s %s\n", rule->name, file_and_line()); #else if( !rule->actions && !rule->procedure ) printf( "warning: unknown rule %s\n", rule->name ); #endif /* If this rule will be executed for updating the targets */ /* then construct the action for make(). */ if( rule->actions ) { TARGETS *t; ACTION *action; /* The action is associated with this instance of this rule */ action = (ACTION *)malloc( sizeof( ACTION ) ); memset( (char *)action, '\0', sizeof( *action ) ); action->rule = rule; #ifdef OPT_BUILTIN_NEEDS_EXT action->targets = targetlist( (TARGETS *)0, lol_get( args, 0 ), 0 ); action->sources = targetlist( (TARGETS *)0, lol_get( args, 1 ), 0 ); action->autosettings = targetlist( (TARGETS *)0, lol_get( args, 2 ), 0 ); #else action->targets = targetlist( (TARGETS *)0, lol_get( args, 0 ) ); action->sources = targetlist( (TARGETS *)0, lol_get( args, 1 ) ); #endif #ifdef OPT_USE_CHECKSUMS_EXT action->extratargets = targetlist( (TARGETS *)0, lol_get( args, 3 ), 0 ); #endif /* OPT_USE_CHECKSUMS_EXT */ #ifdef OPT_CLEAN_GLOBS_EXT { TARGETS* targets; for ( targets = action->targets; targets; targets = targets->next ) { if ( !( targets->target->flags & T_FLAG_NOTFILE ) ) add_used_target_to_hash( targets->target ); } } #endif /* OPT_CLEAN_GLOBS_EXT */ /* Append this action to the actions of each target */ #ifdef OPT_MULTIPASS_EXT action->pass = actionpass; for( t = action->targets; t; t = t->next ) { t->target->progress = T_MAKE_INIT; t->target->actions = actionlist( t->target->actions, action ); } #else for( t = action->targets; t; t = t->next ) t->target->actions = actionlist( t->target->actions, action ); #endif } /* Now recursively compile any parse tree associated with this rule */ if( rule->procedure ) { PARSE *parse = rule->procedure; SETTINGS *s = 0; int jmp = JMP_NONE; LISTITEM *l; int i; /* build parameters as local vars */ for( l = list_first(rule->params), i = 0; l; l = list_next(l), i++ ) s = addsettings( s, 0, list_value(l), list_copy( L0, lol_get( args, i ) ) ); /* Run rule. */ /* Bring in local params. */ /* refer/free to ensure rule not freed during use. */ parse_refer( parse ); pushsettings( s ); result = list_appendList( result, (*parse->func)( parse, args, &jmp ) ); popsettings( s ); freesettings( s ); parse_free( parse ); } if( DEBUG_COMPILE ) debug_compile( -1, 0 ); #ifdef OPT_EXPAND_RULE_NAMES_EXT buffer_free( &buff ); #endif return result; }
int var_string( char *in, char *out, int outsize, LOL *lol ) { char *out0 = out; char *oute = out + outsize - 1; while( *in ) { char *lastword; int dollar = 0; /* Copy white space */ while( isspace( *in ) ) { if( out >= oute ) return -1; *out++ = *in++; } lastword = out; /* Copy non-white space, watching for variables */ while( *in && !isspace( *in ) ) { if( out >= oute ) return -1; if( in[0] == '$' && in[1] == '(' ) dollar++; #ifdef OPT_AT_FILES else if ( in[0] == '@' && in[1] == '(' ) { int depth = 1; char *ine = in + 2; char *split = 0; /* Scan the content of the response file @() section. */ while( *ine && depth > 0 ) { switch( *ine ) { case '(': ++depth; break; case ')': --depth; break; case ':': if( depth == 1 && ine[1] == 'E' && ine[2] == '=' ) { split = ine; } break; } ++ine; } if (!split) { printf( "no file specified!\n" ); exit( EXITBAD ); } if ( depth == 0 ) { string file_name_v; int file_name_l = 0; const char * file_name_s = 0; /* expand the temporary file name var inline */ #if 0 string_copy(&file_name_v,"$("); string_append_range(&file_name_v,in+2,split); string_push_back(&file_name_v,')'); #else string_new(&file_name_v); string_append_range(&file_name_v,in+2,split); #endif file_name_l = var_string(file_name_v.value,out,oute-out+1,lol); string_free(&file_name_v); if ( file_name_l < 0 ) return -1; file_name_s = out; /* for stdout/stderr we will create a temp file and generate a command that outputs the content as needed. */ if ( strcmp( "STDOUT", out ) == 0 || strcmp( "STDERR", out ) == 0 ) { int err_redir = strcmp( "STDERR", out ) == 0; out[0] = '\0'; file_name_s = path_tmpfile(); file_name_l = strlen(file_name_s); #ifdef OS_NT if ( (out+7+file_name_l+(err_redir?5:0)) >= oute ) return -1; sprintf( out,"type \"%s\"%s", file_name_s, err_redir ? " 1>&2" : "" ); #else if ( (out+6+file_name_l+(err_redir?5:0)) >= oute ) return -1; sprintf( out,"cat \"%s\"%s", file_name_s, err_redir ? " 1>&2" : "" ); #endif /* we also make sure that the temp files created by this get nuked eventually. */ file_remove_atexit( file_name_s ); } /* expand the file value into the file reference */ if ( !globs.noexec ) var_string_to_file( split+3, ine-split-4, file_name_s, lol ); /* continue on with the expansion */ out += strlen(out); } /* and continue with the parsing just past the @() reference */ in = ine; } #endif *out++ = *in++; } /* Add zero to 'out' so that 'lastword' is correctly zero-terminated. */ if (out >= oute) return -1; /* Don't increment, intentionally. */ *out= '\0'; /* If a variable encountered, expand it and and embed the */ /* space-separated members of the list in the output. */ if( dollar ) { LIST *l; l = var_expand( L0, lastword, out, lol, 0 ); out = lastword; while ( l ) { int so = strlen( l->string ); if( out + so >= oute ) return -1; strcpy( out, l->string ); out += so; l = list_next( l ); if ( l ) *out++ = ' '; } list_free( l ); } } if( out >= oute ) return -1; *out++ = '\0'; return out - out0; }
CMD * cmd_new( RULE *rule, LIST *targets, LIST *sources, LIST *shell ) { CMD *cmd = (CMD *)malloc( sizeof( CMD ) ); /* lift line-length limitation entirely when JAMSHELL is just "%" */ int no_limit = ( shell && !strcmp(shell->string,"%") && !list_next(shell) ); int max_line = MAXLINE; int allocated = -1; if ( DEBUG_PROFILE ) profile_memory( sizeof( CMD ) ); cmd->rule = rule; cmd->shell = shell; cmd->next = 0; lol_init( &cmd->args ); lol_add( &cmd->args, targets ); lol_add( &cmd->args, sources ); cmd->buf = 0; do { free(cmd->buf); /* free any buffer from previous iteration */ cmd->buf = (char*)malloc(max_line + 1); if ( DEBUG_PROFILE ) profile_memory( max_line + 1 ); if (cmd->buf == 0) break; allocated = var_string( rule->actions->command, cmd->buf, max_line, &cmd->args ); max_line = max_line * 2; } while( allocated < 0 && max_line < INT_MAX / 2 ); if ( !no_limit ) { /* Bail if the result won't fit in MAXLINE */ char *s = cmd->buf; while ( *s ) { size_t l = strcspn( s, "\n" ); if ( l > MAXLINE ) { /* We don't free targets/sources/shell if bailing. */ cmd_free( cmd ); return 0; } s += l; if ( *s ) ++s; } } return cmd; }
//--------------------------------------------------------------------------- void Var::fromString( const string &value ) { if ( type == Var_Float ) var_float() = ofToFloat( value ); if ( type == Var_Int ) var_int() = ofToInt( value ); if ( type == Var_String ) var_string() = value; }
//--------------------------------------------------------------------------- string Var::toString() { if ( type == Var_Float ) return ofToString(var_float()); if ( type == Var_Int ) return ofToString(var_float()); if ( type == Var_String ) return var_string(); return ""; }
//--------------------------------------------------------------------------- void Var::loadIni( ParamMap &ini ) { if ( type == Var_Float ) { var_float() = ini.getFloat(name, var_float() ); } if ( type == Var_Int ) { var_int() = ini.getInt(name, var_int() ); } if ( type == Var_String ) { var_string() = ini.getString(name, var_string() ); } }
//--------------------------------------------------------------------------- //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void Var::saveIni( ParamMap &ini ) { if ( type == Var_Float ) { ini.setFloat(name, var_float() ); } if ( type == Var_Int ) { ini.setInt(name, var_int() ); } if ( type == Var_String ) { ini.setString(name, var_string() ); } }
/* * Try to set the value of an atom. * * If the atom has a non-fixed value and is set to a fixed value, run * unit_propagation...; * If the atom has a non-fixed value and is set to the opposite non-fixed value, * just change the value (and change the state of the relavent clauses?) */ static int32_t update_atom_tval(int32_t var, samp_truth_value_t tval, samp_table_t *table) { pred_table_t *pred_table = &table->pred_table; atom_table_t *atom_table = &table->atom_table; samp_atom_t *atom = atom_table->atom[var]; pred_entry_t *pred_entry = get_pred_entry(pred_table, atom->pred); /* * Case 0: If the atom has been fixed, check if consistent: if it is * assigned to the opposite value, return inconsistency; otherwise do * nothing; */ samp_truth_value_t old_tval = atom_table->assignment[var]; if (!unfixed_tval(old_tval)) { if ((assigned_true(old_tval) && assigned_false(tval)) || (assigned_false(old_tval) && assigned_true(tval))) { mcsat_err("[update_atom_tval] Assigning a conflict truth value. No model exists.\n"); return -1; } else { return 0; } } #if USE_PTHREADS /* Internally, this is now theoretically protected with a mutex, but do we really want a multithreaded mcmc to be waiting for mutexes? */ #else /* Something's still bad here. For now, we ignore this code block if we are compiled for pthreads */ char *var_str = var_string(var, table); cprintf(3, "[update_atom_tval] Setting the value of %s to %s\n", var_str, samp_truth_value_string(tval)); free(var_str); #endif /* Not case 0: update the value */ atom_table->assignment[var] = tval; if (fixed_tval(tval)) { /* If fixed, this can break a later assertion that there are no fixed vars. Why? */ atom_table->num_unfixed_vars--; } /* Case 1: If the value just gets fixed but not changed, we are done. */ if (old_tval == unfix_tval(tval)) { return 0; } /* Case 2: the value has changed */ if (assigned_true(tval)) { link_propagate(table, neg_lit(var)); } else { link_propagate(table, pos_lit(var)); } //assert(valid_table(table)); /* If the atom is inactive AND the value is non-default, activate the atom. */ if (lazy_mcsat() && !atom_table->active[var] && assigned_false(tval) == pred_default_value(pred_entry)) { activate_atom(table, var); } //assert(valid_table(table)); samp_truth_value_t new_tval = atom_table->assignment[var]; assert(new_tval == tval || (fixed_tval(new_tval) && tval == unfix_tval(negate_tval(new_tval)))); /* * WARNING: in lazy mcsat, when we activate a new clause, it may force the * value of the atom being activated to be the nagation of the value we * intend to assign. E.g., when we want to set p(A) to v_true, and * activated (and kept alive of) the clause ~p(A) or q(B), where q(B) is * fixed to false (either by database or unit propagation), then p(A) has * to change back to v_fixed_false. */ if (new_tval != tval) return -1; return 0; }
int var_string( const char * in, char * out, int outsize, LOL * lol ) { char * out0 = out; char * oute = out + outsize - 1; while ( *in ) { char * lastword; int dollar = 0; /* Copy white space. */ while ( isspace( *in ) ) { if ( out >= oute ) return -1; *out++ = *in++; } lastword = out; /* Copy non-white space, watching for variables. */ while ( *in && !isspace( *in ) ) { if ( out >= oute ) return -1; if ( ( in[ 0 ] == '$' ) && ( in[ 1 ] == '(' ) ) { ++dollar; *out++ = *in++; } #ifdef OPT_AT_FILES else if ( ( in[ 0 ] == '@' ) && ( in[ 1 ] == '(' ) ) { int depth = 1; const char * ine = in + 2; const char * split = 0; /* Scan the content of the response file @() section. */ while ( *ine && ( depth > 0 ) ) { switch ( *ine ) { case '(': ++depth; break; case ')': --depth; break; case ':': if ( ( depth == 1 ) && ( ine[ 1 ] == 'E' ) && ( ine[ 2 ] == '=' ) ) split = ine; break; } ++ine; } if ( !split ) { /* the @() reference doesn't match the @(foo:E=bar) format. hence we leave it alone by copying directly to output. */ int l = 0; if ( out + 2 >= oute ) return -1; *( out++ ) = '@'; *( out++ ) = '('; l = var_string( in + 2, out, oute - out, lol ); if ( l < 0 ) return -1; out += l; if ( out + 1 >= oute ) return -1; *( out++ ) = ')'; } else if ( depth == 0 ) { string file_name_v; OBJECT * file_name = 0; int file_name_l = 0; const char * file_name_s = 0; /* Expand the temporary file name var inline. */ #if 0 string_copy( &file_name_v, "$(" ); string_append_range( &file_name_v, in + 2, split ); string_push_back( &file_name_v, ')' ); #else string_new( &file_name_v ); string_append_range( &file_name_v, in + 2, split ); #endif file_name_l = var_string( file_name_v.value, out, oute - out + 1, lol ); string_free( &file_name_v ); if ( file_name_l < 0 ) return -1; file_name_s = out; /* For stdout/stderr we will create a temp file and generate * a command that outputs the content as needed. */ if ( ( strcmp( "STDOUT", out ) == 0 ) || ( strcmp( "STDERR", out ) == 0 ) ) { int err_redir = strcmp( "STDERR", out ) == 0; out[ 0 ] = '\0'; file_name = path_tmpfile(); file_name_s = object_str(file_name); file_name_l = strlen(file_name_s); #ifdef OS_NT if ( ( out + 7 + file_name_l + ( err_redir ? 5 : 0 ) ) >= oute ) return -1; sprintf( out,"type \"%s\"%s", file_name_s, err_redir ? " 1>&2" : "" ); #else if ( ( out + 6 + file_name_l + ( err_redir ? 5 : 0 ) ) >= oute ) return -1; sprintf( out,"cat \"%s\"%s", file_name_s, err_redir ? " 1>&2" : "" ); #endif /* We also make sure that the temp files created by this * get nuked eventually. */ file_remove_atexit( file_name ); } /* Expand the file value into the file reference. */ var_string_to_file( split + 3, ine - split - 4, file_name_s, lol ); if ( file_name ) { object_free( file_name ); } /* Continue on with the expansion. */ out += strlen( out ); } /* And continue with the parsing just past the @() reference. */ in = ine; } #endif else { *out++ = *in++; } } /* Add zero to 'out' so that 'lastword' is correctly zero-terminated. */ if ( out >= oute ) return -1; /* Do not increment, intentionally. */ *out = '\0'; /* If a variable encountered, expand it and and embed the * space-separated members of the list in the output. */ if ( dollar ) { LIST * l = var_expand( L0, lastword, out, lol, 0 ); LIST * saved = l; out = lastword; while ( l ) { int so = strlen( object_str( l->value ) ); if ( out + so >= oute ) return -1; strcpy( out, object_str( l->value ) ); out += so; l = list_next( l ); if ( l ) *out++ = ' '; } list_free( saved ); } } if ( out >= oute ) return -1; *out++ = '\0'; return out - out0; }
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 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; } }