string_free( &variable );
var_expand( 	LIST	*l,	char	*in,	char	*end,	LOL	*lol,	int	cancopyin ){	char out_buf[ MAXSYM ];    string buf[1];    string out1; /* 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;        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 ) )			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( &variable );		}        string_new( &out1 );		/* Toss used empty */		if( evalue )		    list_free( evalue );	    }	    /* 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;	}}