/*    walk.c
 *
 *    Copyright (C) 1991, 1992, 1993, 1994, 1995, 1997, 1998, 1999,
 *    2000, 2001, 2002, 2005 by Larry Wall and others
 *
 *    You may distribute under the terms of either the GNU General Public
 *    License or the Artistic License, as specified in the README file.
 */

#include "EXTERN.h"
#include "a2p.h"
#include "util.h"

bool exitval = FALSE;
bool realexit = FALSE;
bool saw_getline = FALSE;
bool subretnum = FALSE;
bool saw_FNR = FALSE;
bool saw_argv0 = FALSE;
bool saw_fh = FALSE;
int maxtmp = 0;
const char *lparen;
const char *rparen;
const char *limit;
STR *subs;
STR *curargs = NULL;

static void addsemi ( STR *str );
static void emit_split ( STR *str, int level );
static void fixtab ( STR *str, int lvl );
static void numericize ( int node );
static void tab ( STR *str, int lvl );

int prewalk ( int numit, int level, int node, int *numericptr );
STR * walk ( int useval, int level, int node, int *numericptr, int minprec );
#ifdef NETWARE
char *savestr(char *str);
char *cpytill(register char *to, register char *from, register int delim);
char *instr(char *big, const char *little);
#endif

STR *
walk(int useval, int level, register int node, int *numericptr, int minprec)
{
    register int len;
    register STR *str;
    register int type;
    register int i;
    register STR *tmpstr;
    STR *tmp2str;
    STR *tmp3str;
    char *t;
    char *d, *s;
    int numarg;
    int numeric = FALSE;
    STR *fstr;
    int prec = P_MAX;		/* assume no parens needed */

    if (!node) {
	*numericptr = 0;
	return str_make("");
    }
    type = ops[node].ival;
    len = type >> 8;
    type &= 255;
    switch (type) {
    case OPROG:
	arymax = 0;
	if (namelist) {
	    while (isALPHA(*namelist)) {
		for (d = tokenbuf,s=namelist;
		  isALPHA(*s) || isDIGIT(*s) || *s == '_';
		  *d++ = *s++) ;
		*d = '\0';
		while (*s && !isALPHA(*s)) s++;
		namelist = s;
		nameary[++arymax] = savestr(tokenbuf);
	    }
	}
	if (maxfld < arymax)
	    maxfld = arymax;
	opens = str_new(0);
	subs = str_new(0);
	str = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
	if (do_split && need_entire && !absmaxfld)
	    split_to_array = TRUE;
	if (do_split && split_to_array)
	    set_array_base = TRUE;
	if (set_array_base) {
	    str_cat(str,"$[ = 1;\t\t\t# set array base to 1\n");
	}
	if (fswitch && !const_FS)
	    const_FS = fswitch;
	if (saw_FS > 1 || saw_RS)
	    const_FS = 0;
	if (saw_ORS && need_entire)
	    do_chop = TRUE;
	if (fswitch) {
	    str_cat(str,"$FS = '");
	    if (strchr("*+?.[]()|^$\\",fswitch))
		str_cat(str,"\\");
	    sprintf(tokenbuf,"%c",fswitch);
	    str_cat(str,tokenbuf);
	    str_cat(str,"';\t\t# field separator from -F switch\n");
	}
	else if (saw_FS && !const_FS) {
	    str_cat(str,"$FS = ' ';\t\t# set field separator\n");
	}
	if (saw_OFS) {
	    str_cat(str,"$, = ' ';\t\t# set output field separator\n");
	}
	if (saw_ORS) {
	    str_cat(str,"$\\ = \"\\n\";\t\t# set output record separator\n");
	}
	if (saw_argv0) {
	    str_cat(str,"$ARGV0 = $0;\t\t# remember what we ran as\n");
	}
	if (str->str_cur > 20)
	    str_cat(str,"\n");
	if (ops[node+2].ival) {
	    str_scat(str,fstr=walk(0,level,ops[node+2].ival,&numarg,P_MIN));
	    str_free(fstr);
	    str_cat(str,"\n\n");
	}
	fstr = walk(0,level+1,ops[node+3].ival,&numarg,P_MIN);
	if (*fstr->str_ptr) {
	    if (saw_line_op)
		str_cat(str,"line: ");
	    str_cat(str,"while (<>) {\n");
	    tab(str,++level);
	    if (saw_FS && !const_FS)
		do_chop = TRUE;
	    if (do_chop) {
		str_cat(str,"chomp;\t# strip record separator\n");
		tab(str,level);
	    }
	    if (do_split)
		emit_split(str,level);
	    str_scat(str,fstr);
	    str_free(fstr);
	    fixtab(str,--level);
	    str_cat(str,"}\n");
	    if (saw_FNR)
		str_cat(str,"continue {\n    $FNRbase = $. if eof;\n}\n");
	}
	else if (old_awk)
	    str_cat(str,"while (<>) { }		# (no line actions)\n");
	if (ops[node+4].ival) {
	    realexit = TRUE;
	    str_cat(str,"\n");
	    tab(str,level);
	    str_scat(str,fstr=walk(0,level,ops[node+4].ival,&numarg,P_MIN));
	    str_free(fstr);
	    str_cat(str,"\n");
	}
	if (exitval)
	    str_cat(str,"exit $ExitValue;\n");
	if (subs->str_ptr) {
	    str_cat(str,"\n");
	    str_scat(str,subs);
	}
	if (saw_getline) {
	    for (len = 0; len < 4; len++) {
		if (saw_getline & (1 << len)) {
		    sprintf(tokenbuf,"\nsub Getline%d {\n",len);
		    str_cat(str, tokenbuf);
		    if (len & 2) {
			if (do_fancy_opens)
			    str_cat(str,"    &Pick('',@_);\n");
			else
			    str_cat(str,"    ($fh) = @_;\n");
		    }
		    else {
			if (saw_FNR)
			    str_cat(str,"    $FNRbase = $. if eof;\n");
		    }
		    if (len & 1)
			str_cat(str,"    local($_);\n");
		    if (len & 2)
			str_cat(str,
			  "    if ($getline_ok = (($_ = <$fh>) ne ''))");
		    else
			str_cat(str,
			  "    if ($getline_ok = (($_ = <>) ne ''))");
		    str_cat(str, " {\n");
		    level += 2;
		    tab(str,level);
		    i = 0;
		    if (do_chop) {
			i++;
			str_cat(str,"chomp;\t# strip record separator\n");
			tab(str,level);
		    }
		    if (do_split && !(len & 1)) {
			i++;
			emit_split(str,level);
		    }
		    if (!i)
			str_cat(str,";\n");
		    fixtab(str,--level);
		    str_cat(str,"}\n    $_;\n}\n");
		    --level;
		}
	    }
	}
	if (do_fancy_opens) {
	    str_cat(str,"\n\
sub Pick {\n\
    local($mode,$name,$pipe) = @_;\n\
    $fh = $name;\n\
    open($name,$mode.$name.$pipe) unless $opened{$name}++;\n\
}\n\
");
	}
	break;
    case OHUNKS:
	str = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
	str_scat(str,fstr=walk(0,level,ops[node+2].ival,&numarg,P_MIN));
	str_free(fstr);
	if (len == 3) {
	    str_scat(str,fstr=walk(0,level,ops[node+3].ival,&numarg,P_MIN));
	    str_free(fstr);
	}
	else {
	}
	break;
    case ORANGE:
	prec = P_DOTDOT;
	str = walk(1,level,ops[node+1].ival,&numarg,prec+1);
	str_cat(str," .. ");
	str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec+1));
	str_free(fstr);
	break;
    case OPAT:
	goto def;
    case OREGEX:
	str = str_new(0);
	str_set(str,"/");
	tmpstr=walk(0,level,ops[node+1].ival,&numarg,P_MIN);
	/* translate \nnn to [\nnn] */
	for (s = tmpstr->str_ptr, d = tokenbuf; *s; s++, d++) {
	    if (*s == '\\' && isDIGIT(s[1]) && isDIGIT(s[2]) && isDIGIT(s[3])){
		*d++ = '[';
		*d++ = *s++;
		*d++ = *s++;
		*d++ = *s++;
		*d++ = *s;
		*d = ']';
	    }
	    else
		*d = *s;
	}
	*d = '\0';
	for (d=tokenbuf; *d; d++)
           *d += (char)128;
	str_cat(str,tokenbuf);
	str_free(tmpstr);
	str_cat(str,"/");
	break;
    case OHUNK:
	if (len == 1) {
	    str = str_new(0);
	    str = walk(0,level,oper1(OPRINT,0),&numarg,P_MIN);
	    str_cat(str," if ");
	    str_scat(str,fstr=walk(0,level,ops[node+1].ival,&numarg,P_MIN));
	    str_free(fstr);
	    str_cat(str,";");
	}
	else {
	    tmpstr = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
	    if (*tmpstr->str_ptr) {
		str = str_new(0);
		str_set(str,"if (");
		str_scat(str,tmpstr);
		str_cat(str,") {\n");
		tab(str,++level);
		str_scat(str,fstr=walk(0,level,ops[node+2].ival,&numarg,P_MIN));
		str_free(fstr);
		fixtab(str,--level);
		str_cat(str,"}\n");
		tab(str,level);
	    }
	    else {
		str = walk(0,level,ops[node+2].ival,&numarg,P_MIN);
	    }
	}
	break;
    case OPPAREN:
	str = str_new(0);
	str_set(str,"(");
	str_scat(str,fstr=walk(useval != 0,level,ops[node+1].ival,&numarg,P_MIN));
	str_free(fstr);
	str_cat(str,")");
	break;
    case OPANDAND:
	prec = P_ANDAND;
	str = walk(1,level,ops[node+1].ival,&numarg,prec);
	str_cat(str," && ");
	str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec+1));
	str_free(fstr);
	str_scat(str,fstr=walk(1,level,ops[node+3].ival,&numarg,prec+1));
	str_free(fstr);
	break;
    case OPOROR:
	prec = P_OROR;
	str = walk(1,level,ops[node+1].ival,&numarg,prec);
	str_cat(str," || ");
	str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec+1));
	str_free(fstr);
	str_scat(str,fstr=walk(1,level,ops[node+3].ival,&numarg,prec+1));
	str_free(fstr);
	break;
    case OPNOT:
	prec = P_UNARY;
	str = str_new(0);
	str_set(str,"!");
	str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,prec));
	str_free(fstr);
	break;
    case OCOND:
	prec = P_COND;
	str = walk(1,level,ops[node+1].ival,&numarg,prec);
	str_cat(str," ? ");
	str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec+1));
	str_free(fstr);
	str_cat(str," : ");
	str_scat(str,fstr=walk(1,level,ops[node+3].ival,&numarg,prec+1));
	str_free(fstr);
	break;
    case OCPAREN:
	str = str_new(0);
	str_set(str,"(");
	str_scat(str,fstr=walk(useval != 0,level,ops[node+1].ival,&numarg,P_MIN));
	str_free(fstr);
	numeric |= numarg;
	str_cat(str,")");
	break;
    case OCANDAND:
	prec = P_ANDAND;
	str = walk(1,level,ops[node+1].ival,&numarg,prec);
	numeric = 1;
	str_cat(str," && ");
	str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec+1));
	str_free(fstr);
	str_scat(str,fstr=walk(1,level,ops[node+3].ival,&numarg,prec+1));
	str_free(fstr);
	break;
    case OCOROR:
	prec = P_OROR;
	str = walk(1,level,ops[node+1].ival,&numarg,prec);
	numeric = 1;
	str_cat(str," || ");
	str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec+1));
	str_free(fstr);
	str_scat(str,fstr=walk(1,level,ops[node+3].ival,&numarg,prec+1));
	str_free(fstr);
	break;
    case OCNOT:
	prec = P_UNARY;
	str = str_new(0);
	str_set(str,"!");
	str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,prec));
	str_free(fstr);
	numeric = 1;
	break;
    case ORELOP:
	prec = P_REL;
	str = walk(1,level,ops[node+2].ival,&numarg,prec+1);
	numeric |= numarg;
	tmpstr = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
	tmp2str = walk(1,level,ops[node+3].ival,&numarg,prec+1);
	numeric |= numarg;
	if (!numeric ||
	 (!numarg && (*tmp2str->str_ptr == '"' || *tmp2str->str_ptr == '\''))) {
	    t = tmpstr->str_ptr;
	    if (strEQ(t,"=="))
		str_set(tmpstr,"eq");
	    else if (strEQ(t,"!="))
		str_set(tmpstr,"ne");
	    else if (strEQ(t,"<"))
		str_set(tmpstr,"lt");
	    else if (strEQ(t,"<="))
		str_set(tmpstr,"le");
	    else if (strEQ(t,">"))
		str_set(tmpstr,"gt");
	    else if (strEQ(t,">="))
		str_set(tmpstr,"ge");
	    if (!strchr(tmpstr->str_ptr,'\'') && !strchr(tmpstr->str_ptr,'"') &&
	      !strchr(tmp2str->str_ptr,'\'') && !strchr(tmp2str->str_ptr,'"') )
		numeric |= 2;
	}
	if (numeric & 2) {
	    if (numeric & 1)		/* numeric is very good guess */
		str_cat(str," ");
	    else
		str_cat(str,"\377");
	    numeric = 1;
	}
	else
	    str_cat(str," ");
	str_scat(str,tmpstr);
	str_free(tmpstr);
	str_cat(str," ");
	str_scat(str,tmp2str);
	str_free(tmp2str);
	numeric = 1;
	break;
    case ORPAREN:
	str = str_new(0);
	str_set(str,"(");
	str_scat(str,fstr=walk(useval != 0,level,ops[node+1].ival,&numarg,P_MIN));
	str_free(fstr);
	numeric |= numarg;
	str_cat(str,")");
	break;
    case OMATCHOP:
	prec = P_MATCH;
	str = walk(1,level,ops[node+2].ival,&numarg,prec+1);
	str_cat(str," ");
	tmpstr = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
	if (strEQ(tmpstr->str_ptr,"~"))
	    str_cat(str,"=~");
	else {
	    str_scat(str,tmpstr);
	    str_free(tmpstr);
	}
	str_cat(str," ");
	str_scat(str,fstr=walk(1,level,ops[node+3].ival,&numarg,prec+1));
	str_free(fstr);
	numeric = 1;
	break;
    case OMPAREN:
	str = str_new(0);
	str_set(str,"(");
	str_scat(str,
	  fstr=walk(useval != 0,level,ops[node+1].ival,&numarg,P_MIN));
	str_free(fstr);
	numeric |= numarg;
	str_cat(str,")");
	break;
    case OCONCAT:
	prec = P_ADD;
	type = ops[ops[node+1].ival].ival & 255;
	str = walk(1,level,ops[node+1].ival,&numarg,prec+(type != OCONCAT));
	str_cat(str," . ");
	type = ops[ops[node+2].ival].ival & 255;
	str_scat(str,
	  fstr=walk(1,level,ops[node+2].ival,&numarg,prec+(type != OCONCAT)));
	str_free(fstr);
	break;
    case OASSIGN:
	prec = P_ASSIGN;
	str = walk(0,level,ops[node+2].ival,&numarg,prec+1);
	str_cat(str," ");
	tmpstr = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
	str_scat(str,tmpstr);
	if (str_len(tmpstr) > 1)
	    numeric = 1;
	str_free(tmpstr);
	str_cat(str," ");
	str_scat(str,fstr=walk(1,level,ops[node+3].ival,&numarg,prec));
	str_free(fstr);
	numeric |= numarg;
	if (strEQ(str->str_ptr,"$/ = ''"))
	    str_set(str, "$/ = \"\\n\\n\"");
	break;
    case OADD:
	prec = P_ADD;
	str = walk(1,level,ops[node+1].ival,&numarg,prec);
	str_cat(str," + ");
	str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec+1));
	str_free(fstr);
	numeric = 1;
	break;
    case OSUBTRACT:
	prec = P_ADD;
	str = walk(1,level,ops[node+1].ival,&numarg,prec);
	str_cat(str," - ");
	str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec+1));
	str_free(fstr);
	numeric = 1;
	break;
    case OMULT:
	prec = P_MUL;
	str = walk(1,level,ops[node+1].ival,&numarg,prec);
	str_cat(str," * ");
	str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec+1));
	str_free(fstr);
	numeric = 1;
	break;
    case ODIV:
	prec = P_MUL;
	str = walk(1,level,ops[node+1].ival,&numarg,prec);
	str_cat(str," / ");
	str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec+1));
	str_free(fstr);
	numeric = 1;
	break;
    case OPOW:
	prec = P_POW;
	str = walk(1,level,ops[node+1].ival,&numarg,prec+1);
	str_cat(str," ** ");
	str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec));
	str_free(fstr);
	numeric = 1;
	break;
    case OMOD:
	prec = P_MUL;
	str = walk(1,level,ops[node+1].ival,&numarg,prec);
	str_cat(str," % ");
	str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec+1));
	str_free(fstr);
	numeric = 1;
	break;
    case OPOSTINCR:
	prec = P_AUTO;
	str = walk(1,level,ops[node+1].ival,&numarg,prec+1);
	str_cat(str,"++");
	numeric = 1;
	break;
    case OPOSTDECR:
	prec = P_AUTO;
	str = walk(1,level,ops[node+1].ival,&numarg,prec+1);
	str_cat(str,"--");
	numeric = 1;
	break;
    case OPREINCR:
	prec = P_AUTO;
	str = str_new(0);
	str_set(str,"++");
	str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,prec+1));
	str_free(fstr);
	numeric = 1;
	break;
    case OPREDECR:
	prec = P_AUTO;
	str = str_new(0);
	str_set(str,"--");
	str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,prec+1));
	str_free(fstr);
	numeric = 1;
	break;
    case OUMINUS:
	prec = P_UNARY;
	str = str_new(0);
	str_set(str,"-");
	str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,prec));
	str_free(fstr);
	numeric = 1;
	break;
    case OUPLUS:
	numeric = 1;
	goto def;
    case OPAREN:
	str = str_new(0);
	str_set(str,"(");
	str_scat(str,
	  fstr=walk(useval != 0,level,ops[node+1].ival,&numarg,P_MIN));
	str_free(fstr);
	str_cat(str,")");
	numeric |= numarg;
	break;
    case OGETLINE:
	str = str_new(0);
	if (useval)
	    str_cat(str,"(");
	if (len > 0) {
	    str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN));
	    if (!*fstr->str_ptr) {
		str_cat(str,"$_");
		len = 2;		/* a legal fiction */
	    }
	    str_free(fstr);
	}
	else
	    str_cat(str,"$_");
	if (len > 1) {
	    tmpstr=walk(1,level,ops[node+3].ival,&numarg,P_MIN);
	    fstr=walk(1,level,ops[node+2].ival,&numarg,P_MIN);
	    if (!do_fancy_opens) {
		t = tmpstr->str_ptr;
		if (*t == '"' || *t == '\'')
		    t = cpytill(tokenbuf,t+1,*t);
		else
		    fatal("Internal error: OGETLINE %s", t);
		d = savestr(t);
		s = savestr(tokenbuf);
		for (t = tokenbuf; *t; t++) {
		    *t &= 127;
		    if (isLOWER(*t))
			*t = toUPPER(*t);
		    if (!isALPHA(*t) && !isDIGIT(*t))
			*t = '_';
		}
		if (!strchr(tokenbuf,'_'))
		    strcpy(t,"_FH");
		tmp3str = hfetch(symtab,tokenbuf);
		if (!tmp3str) {
		    do_opens = TRUE;
		    str_cat(opens,"open(");
		    str_cat(opens,tokenbuf);
		    str_cat(opens,", ");
		    d[1] = '\0';
		    str_cat(opens,d);
		    str_cat(opens,tmpstr->str_ptr+1);
		    opens->str_cur--;
		    if (*fstr->str_ptr == '|')
			str_cat(opens,"|");
		    str_cat(opens,d);
		    if (*fstr->str_ptr == '|')
			str_cat(opens,") || die 'Cannot pipe from \"");
		    else
			str_cat(opens,") || die 'Cannot open file \"");
		    if (*d == '"')
			str_cat(opens,"'.\"");
		    str_cat(opens,s);
		    if (*d == '"')
			str_cat(opens,"\".'");
		    str_cat(opens,"\".';\n");
		    hstore(symtab,tokenbuf,str_make("x"));
		}
		safefree(s);
		safefree(d);
		str_set(tmpstr,"'");
		str_cat(tmpstr,tokenbuf);
		str_cat(tmpstr,"'");
	    }
	    if (*fstr->str_ptr == '|')
		str_cat(tmpstr,", '|'");
	    str_free(fstr);
	}
	else
	    tmpstr = str_make("");
	sprintf(tokenbuf," = &Getline%d(%s)",len,tmpstr->str_ptr);
	str_cat(str,tokenbuf); 
	str_free(tmpstr);
	if (useval)
	    str_cat(str,",$getline_ok)");
	saw_getline |= 1 << len;
	break;
    case OSPRINTF:
	str = str_new(0);
	str_set(str,"sprintf(");
	str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN));
	str_free(fstr);
	str_cat(str,")");
	break;
    case OSUBSTR:
	str = str_new(0);
	str_set(str,"substr(");
	str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_COMMA+1));
	str_free(fstr);
	str_cat(str,", ");
	str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,P_COMMA+1));
	str_free(fstr);
	if (len == 3) {
	    str_cat(str,", ");
	    str_scat(str,fstr=walk(1,level,ops[node+3].ival,&numarg,P_COMMA+1));
	    str_free(fstr);
	}
	str_cat(str,")");
	break;
    case OSTRING:
	str = str_new(0);
	str_set(str,ops[node+1].cval);
	break;
    case OSPLIT:
	str = str_new(0);
	limit = ", -1)";
	numeric = 1;
	tmpstr = walk(1,level,ops[node+2].ival,&numarg,P_MIN);
	if (useval)
	    str_set(str,"(@");
	else
	    str_set(str,"@");
	str_scat(str,tmpstr);
	str_cat(str," = split(");
	if (len == 3) {
	    fstr = walk(1,level,ops[node+3].ival,&numarg,P_COMMA+1);
	    if (str_len(fstr) == 3 && *fstr->str_ptr == '\'') {
		i = fstr->str_ptr[1] & 127;
		if (strchr("*+?.[]()|^$\\",i))
		    sprintf(tokenbuf,"/\\%c/",i);
		else if (i == ' ')
		    sprintf(tokenbuf,"' '");
		else
		    sprintf(tokenbuf,"/%c/",i);
		str_cat(str,tokenbuf);
	    }
	    else
		str_scat(str,fstr);
	    str_free(fstr);
	}
	else if (const_FS) {
	    sprintf(tokenbuf,"/[%c\\n]/",const_FS);
	    str_cat(str,tokenbuf);
	}
	else if (saw_FS)
	    str_cat(str,"$FS");
	else {
	    str_cat(str,"' '");
	    limit = ")";
	}
	str_cat(str,", ");
	str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_COMMA+1));
	str_free(fstr);
	str_cat(str,limit);
	if (useval) {
	    str_cat(str,")");
	}
	str_free(tmpstr);
	break;
    case OINDEX:
	str = str_new(0);
	str_set(str,"index(");
	str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_COMMA+1));
	str_free(fstr);
	str_cat(str,", ");
	str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,P_COMMA+1));
	str_free(fstr);
	str_cat(str,")");
	numeric = 1;
	break;
    case OMATCH:
	str = str_new(0);
	prec = P_ANDAND;
	str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_MATCH+1));
	str_free(fstr);
	str_cat(str," =~ ");
	str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,P_MATCH+1));
	str_free(fstr);
	str_cat(str," && ($RLENGTH = length($&), $RSTART = length($`)+1)");
	numeric = 1;
	break;
    case OUSERDEF:
	str = str_new(0);
	subretnum = FALSE;
	fstr=walk(1,level-1,ops[node+2].ival,&numarg,P_MIN);
	curargs = str_new(0);
	str_sset(curargs,fstr);
	str_cat(curargs,",");
	tmp2str=walk(1,level,ops[node+5].ival,&numarg,P_MIN);
	str_free(curargs);
	curargs = NULL;
	level--;
	subretnum |= numarg;
	s = NULL;
	t = tmp2str->str_ptr;
	while ((t = instr(t,"return ")))
	    s = t++;
	if (s) {
	    i = 0;
	    for (t = s+7; *t; t++) {
		if (*t == ';' || *t == '}')
		    i++;
	    }
	    if (i == 1) {
		strcpy(s,s+7);
		tmp2str->str_cur -= 7;
	    }
	}
	str_set(str,"\n");
	tab(str,level);
	str_cat(str,"sub ");
	str_scat(str,tmpstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN));
	str_cat(str," {\n");
	tab(str,++level);
	if (fstr->str_cur) {
	    str_cat(str,"local(");
	    str_scat(str,fstr);
	    str_cat(str,") = @_;");
	}
	str_free(fstr);
	str_scat(str,fstr=walk(1,level,ops[node+3].ival,&numarg,P_MIN));
	str_free(fstr);
	fixtab(str,level);
	str_scat(str,fstr=walk(1,level,ops[node+4].ival,&numarg,P_MIN));
	str_free(fstr);
	fixtab(str,level);
	str_scat(str,tmp2str);
	str_free(tmp2str);
	fixtab(str,--level);
	str_cat(str,"}\n");
	tab(str,level);
	str_scat(subs,str);
	str_set(str,"");
	str_cat(tmpstr,"(");
	tmp2str = str_new(0);
	if (subretnum)
	    str_set(tmp2str,"1");
	hstore(symtab,tmpstr->str_ptr,tmp2str);
	str_free(tmpstr);
	level++;
	break;
    case ORETURN:
	str = str_new(0);
	if (len > 0) {
	    str_cat(str,"return ");
	    str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_UNI+1));
	    str_free(fstr);
	    if (numarg)
		subretnum = TRUE;
	}
	else
	    str_cat(str,"return");
	break;
    case OUSERFUN:
	str = str_new(0);
	str_set(str,"&");
	str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN));
	str_free(fstr);
	str_cat(str,"(");
	tmpstr = hfetch(symtab,str->str_ptr+3);
	if (tmpstr && tmpstr->str_ptr)
	    numeric |= atoi(tmpstr->str_ptr);
	str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,P_MIN));
	str_free(fstr);
	str_cat(str,")");
	break;
    case OGSUB:
    case OSUB: {
	int gsub = type == OGSUB ? 1 : 0;
	str = str_new(0);
	tmpstr = str_new(0);
	i = 0;
	if (len == 3) {
	    tmpstr = walk(1,level,ops[node+3].ival,&numarg,P_MATCH+1);
	    if (strNE(tmpstr->str_ptr,"$_")) {
		str_cat(tmpstr, " =~ s");
		i++;
	    }
	    else
		str_set(tmpstr, "s");
	}
	else
	    str_set(tmpstr, "s");
	type = ops[ops[node+2].ival].ival;
	len = type >> 8;
	type &= 255;
	tmp3str = str_new(0);
	{
	  const char *s;
	  if (type == OSTR) {
	    tmp2str=walk(1,level,ops[ops[node+2].ival+1].ival,&numarg,P_MIN);
	    for (t = tmp2str->str_ptr, d=tokenbuf; *t; d++,t++) {
		if (*t == '&')
                   *d++ = '$' + (char)128;
		else if (*t == '$')
                   *d++ = '\\' + (char)128;
		*d = *t + 128;
	    }
	    *d = '\0';
	    str_set(tmp2str,tokenbuf);
	    s = (gsub ? "/g" : "/");
	  }
	  else {
	    tmp2str=walk(1,level,ops[node+2].ival,&numarg,P_MIN);
	    str_set(tmp3str,"($s_ = '\"'.(");
	    str_scat(tmp3str,tmp2str);
	    str_cat(tmp3str,").'\"') =~ s/&/\\$&/g, ");
	    str_set(tmp2str,"eval $s_");
	    s = (gsub ? "/ge" : "/e");
	    i++;
	  }
	  str_cat(tmp2str,s);
	}
	type = ops[ops[node+1].ival].ival;
	len = type >> 8;
	type &= 255;
	fstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN);
	if (type == OREGEX) {
	    if (useval && i)
		str_cat(str,"(");
	    str_scat(str,tmp3str);
	    str_scat(str,tmpstr);
	    str_scat(str,fstr);
	    str_scat(str,tmp2str);
	}
	else if ((type == OFLD && !split_to_array) || (type == OVAR && len == 1)) {
	    if (useval && i)
		str_cat(str,"(");
	    str_scat(str,tmp3str);
	    str_scat(str,tmpstr);
	    str_cat(str,"/");
	    str_scat(str,fstr);
	    str_cat(str,"/");
	    str_scat(str,tmp2str);
	}
	else {
	    i++;
	    if (useval)
		str_cat(str,"(");
	    str_cat(str,"$s = ");
	    str_scat(str,fstr);
	    str_cat(str,", ");
	    str_scat(str,tmp3str);
	    str_scat(str,tmpstr);
	    str_cat(str,"/$s/");
	    str_scat(str,tmp2str);
	}
	if (useval && i)
	    str_cat(str,")");
	str_free(fstr);
	str_free(tmpstr);
	str_free(tmp2str);
	str_free(tmp3str);
	numeric = 1;
	break; }
    case ONUM:
	str = walk(1,level,ops[node+1].ival,&numarg,P_MIN);
	numeric = 1;
	break;
    case OSTR:
	tmpstr = walk(1,level,ops[node+1].ival,&numarg,P_MIN);
	{
	  const char *s = "'";
	  for (t = tmpstr->str_ptr, d=tokenbuf; *t; d++,t++) {
	    if (*t == '\'')
		s = "\"";
	    else if (*t == '\\') {
		s = "\"";
		*d++ = *t++ + 128;
		switch (*t) {
		case '\\': case '"': case 'n': case 't': case '$':
		    break;
		default:	/* hide this from perl */
                   *d++ = '\\' + (char)128;
		}
	    }
	    *d = *t + 128;
	  }
	  *d = '\0';
	  str = str_new(0);
	  str_set(str,s);
	  str_cat(str,tokenbuf);
	  str_free(tmpstr);
	  str_cat(str,s);
	}
	break;
    case ODEFINED:
	prec = P_UNI;
	str = str_new(0);
	str_set(str,"defined $");
	goto addvar;
    case ODELETE:
	str = str_new(0);
	str_set(str,"delete $");
	goto addvar;
    case OSTAR:
	str = str_new(0);
	str_set(str,"*");
	goto addvar;
    case OVAR:
	str = str_new(0);
	str_set(str,"$");
      addvar:
	str_scat(str,tmpstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN));
	if (len == 1) {
	    tmp2str = hfetch(symtab,tmpstr->str_ptr);
	    if (tmp2str && atoi(tmp2str->str_ptr))
		numeric = 2;
	    if (strEQ(str->str_ptr,"$FNR")) {
		numeric = 1;
		saw_FNR++;
		str_set(str,"($.-$FNRbase)");
	    }
	    else if (strEQ(str->str_ptr,"$NR")) {
		numeric = 1;
		str_set(str,"$.");
	    }
	    else if (strEQ(str->str_ptr,"$NF")) {
		numeric = 1;
		str_set(str,"$#Fld");
	    }
	    else if (strEQ(str->str_ptr,"$0"))
		str_set(str,"$_");
	    else if (strEQ(str->str_ptr,"$ARGC"))
		str_set(str,"($#ARGV+1)");
	}
	else {
#ifdef NOTDEF
	    if (curargs) {
		sprintf(tokenbuf,"$%s,",tmpstr->str_ptr);
	???	if (instr(curargs->str_ptr,tokenbuf))
		    str_cat(str,"\377");	/* can't translate yet */
	    }
#endif
	    str_cat(tmpstr,"[]");
	    tmp2str = hfetch(symtab,tmpstr->str_ptr);
	    if (tmp2str && atoi(tmp2str->str_ptr))
		str_cat(str,"[");
	    else
		str_cat(str,"{");
	    str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,P_MIN));
	    str_free(fstr);
	    if (strEQ(str->str_ptr,"$ARGV[0")) {
		str_set(str,"$ARGV0");
		saw_argv0++;
	    }
	    else {
		if (tmp2str && atoi(tmp2str->str_ptr))
		    strcpy(tokenbuf,"]");
		else
		    strcpy(tokenbuf,"}");
               *tokenbuf += (char)128;
		str_cat(str,tokenbuf);
	    }
	}
	str_free(tmpstr);
	break;
    case OFLD:
	str = str_new(0);
	if (split_to_array) {
	    str_set(str,"$Fld");
	    str_cat(str,"[");
	    str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN));
	    str_free(fstr);
	    str_cat(str,"]");
	}
	else {
	    i = atoi(walk(1,level,ops[node+1].ival,&numarg,P_MIN)->str_ptr);
	    if (i <= arymax)
		sprintf(tokenbuf,"$%s",nameary[i]);
	    else
		sprintf(tokenbuf,"$Fld%d",i);
	    str_set(str,tokenbuf);
	}
	break;
    case OVFLD:
	str = str_new(0);
	str_set(str,"$Fld[");
	i = ops[node+1].ival;
	if ((ops[i].ival & 255) == OPAREN)
	    i = ops[i+1].ival;
	tmpstr=walk(1,level,i,&numarg,P_MIN);
	str_scat(str,tmpstr);
	str_free(tmpstr);
	str_cat(str,"]");
	break;
    case OJUNK:
	goto def;
    case OSNEWLINE:
	str = str_new(2);
	str_set(str,";\n");
	tab(str,level);
	break;
    case ONEWLINE:
	str = str_new(1);
	str_set(str,"\n");
	tab(str,level);
	break;
    case OSCOMMENT:
	str = str_new(0);
	str_set(str,";");
	tmpstr = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
	for (s = tmpstr->str_ptr; *s && *s != '\n'; s++)
           *s += (char)128;
	str_scat(str,tmpstr);
	str_free(tmpstr);
	tab(str,level);
	break;
    case OCOMMENT:
	str = str_new(0);
	tmpstr = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
	for (s = tmpstr->str_ptr; *s && *s != '\n'; s++)
           *s += (char)128;
	str_scat(str,tmpstr);
	str_free(tmpstr);
	tab(str,level);
	break;
    case OCOMMA:
	prec = P_COMMA;
	str = walk(1,level,ops[node+1].ival,&numarg,prec);
	str_cat(str,", ");
	str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,P_MIN));
	str_free(fstr);
	str_scat(str,fstr=walk(1,level,ops[node+3].ival,&numarg,prec+1));
	str_free(fstr);
	break;
    case OSEMICOLON:
	str = str_new(1);
	str_set(str,";\n");
	tab(str,level);
	break;
    case OSTATES:
	str = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
	str_scat(str,fstr=walk(0,level,ops[node+2].ival,&numarg,P_MIN));
	str_free(fstr);
	break;
    case OSTATE:
	str = str_new(0);
	if (len >= 1) {
	    str_scat(str,fstr=walk(0,level,ops[node+1].ival,&numarg,P_MIN));
	    str_free(fstr);
	    if (len >= 2) {
		tmpstr = walk(0,level,ops[node+2].ival,&numarg,P_MIN);
		if (*tmpstr->str_ptr == ';') {
		    addsemi(str);
		    str_cat(str,tmpstr->str_ptr+1);
		}
		str_free(tmpstr);
	    }
	}
	break;
    case OCLOSE:
	str = str_make("close(");
	tmpstr = walk(1,level,ops[node+1].ival,&numarg,P_MIN);
	if (!do_fancy_opens) {
	    t = tmpstr->str_ptr;
	    if (*t == '"' || *t == '\'')
		t = cpytill(tokenbuf,t+1,*t);
	    else
		fatal("Internal error: OCLOSE %s",t);
	    s = savestr(tokenbuf);
	    for (t = tokenbuf; *t; t++) {
		*t &= 127;
		if (isLOWER(*t))
		    *t = toUPPER(*t);
		if (!isALPHA(*t) && !isDIGIT(*t))
		    *t = '_';
	    }
	    if (!strchr(tokenbuf,'_'))
		strcpy(t,"_FH");
	    str_free(tmpstr);
	    safefree(s);
	    str_set(str,"close ");
	    str_cat(str,tokenbuf);
	}
	else {
	    sprintf(tokenbuf,"delete $opened{%s} && close(%s)",
	       tmpstr->str_ptr, tmpstr->str_ptr);
	    str_free(tmpstr);
	    str_set(str,tokenbuf);
	}
	break;
    case OPRINTF:
    case OPRINT:
	lparen = "";	/* set to parens if necessary */
	rparen = "";
	str = str_new(0);
	if (len == 3) {		/* output redirection */
	    tmpstr = walk(1,level,ops[node+3].ival,&numarg,P_MIN);
	    tmp2str = walk(1,level,ops[node+2].ival,&numarg,P_MIN);
	    if (!do_fancy_opens) {
		t = tmpstr->str_ptr;
		if (*t == '"' || *t == '\'')
		    t = cpytill(tokenbuf,t+1,*t);
		else
		    fatal("Internal error: OPRINT");
		d = savestr(t);
		s = savestr(tokenbuf);
		for (t = tokenbuf; *t; t++) {
		    *t &= 127;
		    if (isLOWER(*t))
			*t = toUPPER(*t);
		    if (!isALPHA(*t) && !isDIGIT(*t))
			*t = '_';
		}
		if (!strchr(tokenbuf,'_'))
		    strcpy(t,"_FH");
		tmp3str = hfetch(symtab,tokenbuf);
		if (!tmp3str) {
		    str_cat(opens,"open(");
		    str_cat(opens,tokenbuf);
		    str_cat(opens,", ");
		    d[1] = '\0';
		    str_cat(opens,d);
		    str_scat(opens,tmp2str);
		    str_cat(opens,tmpstr->str_ptr+1);
		    if (*tmp2str->str_ptr == '|')
			str_cat(opens,") || die 'Cannot pipe to \"");
		    else
			str_cat(opens,") || die 'Cannot create file \"");
		    if (*d == '"')
			str_cat(opens,"'.\"");
		    str_cat(opens,s);
		    if (*d == '"')
			str_cat(opens,"\".'");
		    str_cat(opens,"\".';\n");
		    hstore(symtab,tokenbuf,str_make("x"));
		}
		str_free(tmpstr);
		str_free(tmp2str);
		safefree(s);
		safefree(d);
	    }
	    else {
		sprintf(tokenbuf,"&Pick('%s', %s) &&\n",
		   tmp2str->str_ptr, tmpstr->str_ptr);
		str_cat(str,tokenbuf);
		tab(str,level+1);
		strcpy(tokenbuf,"$fh");
		str_free(tmpstr);
		str_free(tmp2str);
		lparen = "(";
		rparen = ")";
	    }
	}
	else
	    strcpy(tokenbuf,"");
	str_cat(str,lparen);	/* may be null */
	if (type == OPRINTF)
	    str_cat(str,"printf");
	else
	    str_cat(str,"print");
	saw_fh = 0;
	if (len == 3 || do_fancy_opens) {
	    if (*tokenbuf) {
		str_cat(str," ");
		saw_fh = 1;
	    }
	    str_cat(str,tokenbuf);
	}
	tmpstr = walk(1+(type==OPRINT),level,ops[node+1].ival,&numarg,P_MIN);
	if (!*tmpstr->str_ptr && lval_field) {
	    const char *t = (saw_OFS ? "$," : "' '");
	    if (split_to_array) {
		sprintf(tokenbuf,"join(%s,@Fld)",t);
		str_cat(tmpstr,tokenbuf);
	    }
	    else {
		for (i = 1; i < maxfld; i++) {
		    if (i <= arymax)
			sprintf(tokenbuf,"$%s, ",nameary[i]);
		    else
			sprintf(tokenbuf,"$Fld%d, ",i);
		    str_cat(tmpstr,tokenbuf);
		}
		if (maxfld <= arymax)
		    sprintf(tokenbuf,"$%s",nameary[maxfld]);
		else
		    sprintf(tokenbuf,"$Fld%d",maxfld);
		str_cat(tmpstr,tokenbuf);
	    }
	}
	if (*tmpstr->str_ptr) {
	    str_cat(str," ");
	    if (!saw_fh && *tmpstr->str_ptr == '(') {
		str_cat(str,"(");
		str_scat(str,tmpstr);
		str_cat(str,")");
	    }
	    else
		str_scat(str,tmpstr);
	}
	else {
	    str_cat(str," $_");
	}
	str_cat(str,rparen);	/* may be null */
	str_free(tmpstr);
	break;
    case ORAND:
	str = str_make("rand(1)");
	break;
    case OSRAND:
	str = str_make("srand(");
	goto maybe0;
    case OATAN2:
	str = str_make("atan2(");
	goto maybe0;
    case OSIN:
	str = str_make("sin(");
	goto maybe0;
    case OCOS:
	str = str_make("cos(");
	goto maybe0;
    case OSYSTEM:
	str = str_make("system(");
	goto maybe0;
    case OLENGTH:
	str = str_make("length(");
	goto maybe0;
    case OLOG:
	str = str_make("log(");
	goto maybe0;
    case OEXP:
	str = str_make("exp(");
	goto maybe0;
    case OSQRT:
	str = str_make("sqrt(");
	goto maybe0;
    case OINT:
	str = str_make("int(");
      maybe0:
	numeric = 1;
	if (len > 0)
	    tmpstr = walk(1,level,ops[node+1].ival,&numarg,P_MIN);
	else
	    tmpstr = str_new(0);
	if (!tmpstr->str_ptr || !*tmpstr->str_ptr) {
	    if (lval_field) {
		const char *t = (saw_OFS ? "$," : "' '");
		if (split_to_array) {
		    sprintf(tokenbuf,"join(%s,@Fld)",t);
		    str_cat(tmpstr,tokenbuf);
		}
		else {
		    sprintf(tokenbuf,"join(%s, ",t);
		    str_cat(tmpstr,tokenbuf);
		    for (i = 1; i < maxfld; i++) {
			if (i <= arymax)
			    sprintf(tokenbuf,"$%s,",nameary[i]);
			else
			    sprintf(tokenbuf,"$Fld%d,",i);
			str_cat(tmpstr,tokenbuf);
		    }
		    if (maxfld <= arymax)
			sprintf(tokenbuf,"$%s)",nameary[maxfld]);
		    else
			sprintf(tokenbuf,"$Fld%d)",maxfld);
		    str_cat(tmpstr,tokenbuf);
		}
	    }
	    else
		str_cat(tmpstr,"$_");
	}
	if (strEQ(tmpstr->str_ptr,"$_")) {
	    if (type == OLENGTH && !do_chop) {
		str = str_make("(length(");
		str_cat(tmpstr,") - 1");
	    }
	}
	str_scat(str,tmpstr);
	str_free(tmpstr);
	str_cat(str,")");
	break;
    case OBREAK:
	str = str_new(0);
	str_set(str,"last");
	break;
    case ONEXT:
	str = str_new(0);
	str_set(str,"next line");
	break;
    case OEXIT:
	str = str_new(0);
	if (realexit) {
	    prec = P_UNI;
	    str_set(str,"exit");
	    if (len == 1) {
		str_cat(str," ");
		exitval = TRUE;
		str_scat(str,
		  fstr=walk(1,level,ops[node+1].ival,&numarg,prec+1));
		str_free(fstr);
	    }
	}
	else {
	    if (len == 1) {
		str_set(str,"$ExitValue = ");
		exitval = TRUE;
		str_scat(str,
		  fstr=walk(1,level,ops[node+1].ival,&numarg,P_ASSIGN));
		str_free(fstr);
		str_cat(str,"; ");
	    }
	    str_cat(str,"last line");
	}
	break;
    case OCONTINUE:
	str = str_new(0);
	str_set(str,"next");
	break;
    case OREDIR:
	goto def;
    case OIF:
	str = str_new(0);
	str_set(str,"if (");
	str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN));
	str_free(fstr);
	str_cat(str,") ");
	str_scat(str,fstr=walk(0,level,ops[node+2].ival,&numarg,P_MIN));
	str_free(fstr);
	if (len == 3) {
	    i = ops[node+3].ival;
	    if (i) {
		if ((ops[i].ival & 255) == OBLOCK) {
		    i = ops[i+1].ival;
		    if (i) {
			if ((ops[i].ival & 255) != OIF)
			    i = 0;
		    }
		}
		else
		    i = 0;
	    }
	    if (i) {
		str_cat(str,"els");
		str_scat(str,fstr=walk(0,level,i,&numarg,P_MIN));
		str_free(fstr);
	    }
	    else {
		str_cat(str,"else ");
		str_scat(str,fstr=walk(0,level,ops[node+3].ival,&numarg,P_MIN));
		str_free(fstr);
	    }
	}
	break;
    case OWHILE:
	str = str_new(0);
	str_set(str,"while (");
	str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN));
	str_free(fstr);
	str_cat(str,") ");
	str_scat(str,fstr=walk(0,level,ops[node+2].ival,&numarg,P_MIN));
	str_free(fstr);
	break;
    case ODO:
	str = str_new(0);
	str_set(str,"do ");
	str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN));
	str_free(fstr);
	if (str->str_ptr[str->str_cur - 1] == '\n')
	    --str->str_cur;
	str_cat(str," while (");
	str_scat(str,fstr=walk(0,level,ops[node+2].ival,&numarg,P_MIN));
	str_free(fstr);
	str_cat(str,");");
	break;
    case OFOR:
	str = str_new(0);
	str_set(str,"for (");
	str_scat(str,tmpstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN));
	i = numarg;
	if (i) {
	    t = s = tmpstr->str_ptr;
	    while (isALPHA(*t) || isDIGIT(*t) || *t == '$' || *t == '_')
		t++;
	    i = t - s;
	    if (i < 2)
		i = 0;
	}
	str_cat(str,"; ");
	fstr=walk(1,level,ops[node+2].ival,&numarg,P_MIN);
	if (i && (t = strchr(fstr->str_ptr,0377))) {
	    if (strnEQ(fstr->str_ptr,s,i))
		*t = ' ';
	}
	str_scat(str,fstr);
	str_free(fstr);
	str_free(tmpstr);
	str_cat(str,"; ");
	str_scat(str,fstr=walk(1,level,ops[node+3].ival,&numarg,P_MIN));
	str_free(fstr);
	str_cat(str,") ");
	str_scat(str,fstr=walk(0,level,ops[node+4].ival,&numarg,P_MIN));
	str_free(fstr);
	break;
    case OFORIN:
	tmpstr = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
	d = strchr(tmpstr->str_ptr,'$');
	if (!d)
	    fatal("Illegal for loop: %s",tmpstr->str_ptr);
	s = strchr(d,'{');
	if (!s)
	    s = strchr(d,'[');
	if (!s)
	    fatal("Illegal for loop: %s",d);
	*s++ = '\0';
	for (t = s; (i = *t); t++) {
	    i &= 127;
	    if (i == '}' || i == ']')
		break;
	}
	if (*t)
	    *t = '\0';
	str = str_new(0);
	str_set(str,d+1);
	str_cat(str,"[]");
	tmp2str = hfetch(symtab,str->str_ptr);
	if (tmp2str && atoi(tmp2str->str_ptr)) {
	    sprintf(tokenbuf,
	      "foreach %s ($[ .. $#%s) ",
	      s,
	      d+1);
	}
	else {
	    sprintf(tokenbuf,
	      "foreach %s (keys %%%s) ",
	      s,
	      d+1);
	}
	str_set(str,tokenbuf);
	str_scat(str,fstr=walk(0,level,ops[node+2].ival,&numarg,P_MIN));
	str_free(fstr);
	str_free(tmpstr);
	break;
    case OBLOCK:
	str = str_new(0);
	str_set(str,"{");
	if (len >= 2 && ops[node+2].ival) {
	    str_scat(str,fstr=walk(0,level,ops[node+2].ival,&numarg,P_MIN));
	    str_free(fstr);
	}
	fixtab(str,++level);
	str_scat(str,fstr=walk(0,level,ops[node+1].ival,&numarg,P_MIN));
	str_free(fstr);
	addsemi(str);
	fixtab(str,--level);
	str_cat(str,"}\n");
	tab(str,level);
	if (len >= 3) {
	    str_scat(str,fstr=walk(0,level,ops[node+3].ival,&numarg,P_MIN));
	    str_free(fstr);
	}
	break;
    default:
      def:
	if (len) {
	    if (len > 5)
		fatal("Garbage length in walk");
	    str = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
	    for (i = 2; i<= len; i++) {
		str_scat(str,fstr=walk(0,level,ops[node+i].ival,&numarg,P_MIN));
		str_free(fstr);
	    }
	}
	else {
	    str = NULL;
	}
	break;
    }
    if (!str)
	str = str_new(0);

    if (useval && prec < minprec) {		/* need parens? */
	fstr = str_new(str->str_cur+2);
	str_nset(fstr,"(",1);
	str_scat(fstr,str);
	str_ncat(fstr,")",1);
	str_free(str);
	str = fstr;
    }

    *numericptr = numeric;
#ifdef DEBUGGING
    if (debug & 4) {
	printf("%3d %5d %15s %d %4d ",level,node,opname[type],len,str->str_cur);
	for (t = str->str_ptr; *t && t - str->str_ptr < 40; t++)
	    if (*t == '\n')
		printf("\\n");
	    else if (*t == '\t')
		printf("\\t");
	    else
		putchar(*t);
	putchar('\n');
    }
#endif
    return str;
}

static void
tab(register STR *str, register int lvl)
{
    while (lvl > 1) {
	str_cat(str,"\t");
	lvl -= 2;
    }
    if (lvl)
	str_cat(str,"    ");
}

static void
fixtab(register STR *str, register int lvl)
{
    register char *s;

    /* strip trailing white space */

    s = str->str_ptr+str->str_cur - 1;
    while (s >= str->str_ptr && (*s == ' ' || *s == '\t' || *s == '\n'))
	s--;
    s[1] = '\0';
    str->str_cur = s + 1 - str->str_ptr;
    if (s >= str->str_ptr && *s != '\n')
	str_cat(str,"\n");

    tab(str,lvl);
}

static void
addsemi(register STR *str)
{
    register char *s;

    s = str->str_ptr+str->str_cur - 1;
    while (s >= str->str_ptr && (*s == ' ' || *s == '\t' || *s == '\n'))
	s--;
    if (s >= str->str_ptr && *s != ';' && *s != '}')
	str_cat(str,";");
}

static void
emit_split(register STR *str, int level)
{
    register int i;

    if (split_to_array)
	str_cat(str,"@Fld");
    else {
	str_cat(str,"(");
	for (i = 1; i < maxfld; i++) {
	    if (i <= arymax)
		sprintf(tokenbuf,"$%s,",nameary[i]);
	    else
		sprintf(tokenbuf,"$Fld%d,",i);
	    str_cat(str,tokenbuf);
	}
	if (maxfld <= arymax)
	    sprintf(tokenbuf,"$%s)",nameary[maxfld]);
	else
	    sprintf(tokenbuf,"$Fld%d)",maxfld);
	str_cat(str,tokenbuf);
    }
    if (const_FS) {
	sprintf(tokenbuf," = split(/[%c\\n]/, $_, -1);\n",const_FS);
	str_cat(str,tokenbuf);
    }
    else if (saw_FS)
	str_cat(str," = split($FS, $_, -1);\n");
    else
	str_cat(str," = split(' ', $_, -1);\n");
    tab(str,level);
}

int
prewalk(int numit, int level, register int node, int *numericptr)
{
    register int len;
    register int type;
    register int i;
    int numarg;
    int numeric = FALSE;
    STR *tmpstr;
    STR *tmp2str;

    if (!node) {
	*numericptr = 0;
	return 0;
    }
    type = ops[node].ival;
    len = type >> 8;
    type &= 255;
    switch (type) {
    case OPROG:
	prewalk(0,level,ops[node+1].ival,&numarg);
	if (ops[node+2].ival) {
	    prewalk(0,level,ops[node+2].ival,&numarg);
	}
	++level;
	prewalk(0,level,ops[node+3].ival,&numarg);
	--level;
	if (ops[node+3].ival) {
	    prewalk(0,level,ops[node+4].ival,&numarg);
	}
	break;
    case OHUNKS:
	prewalk(0,level,ops[node+1].ival,&numarg);
	prewalk(0,level,ops[node+2].ival,&numarg);
	if (len == 3) {
	    prewalk(0,level,ops[node+3].ival,&numarg);
	}
	break;
    case ORANGE:
	prewalk(1,level,ops[node+1].ival,&numarg);
	prewalk(1,level,ops[node+2].ival,&numarg);
	break;
    case OPAT:
	goto def;
    case OREGEX:
	prewalk(0,level,ops[node+1].ival,&numarg);
	break;
    case OHUNK:
	if (len == 1) {
	    prewalk(0,level,ops[node+1].ival,&numarg);
	}
	else {
	    i = prewalk(0,level,ops[node+1].ival,&numarg);
	    if (i) {
		++level;
		prewalk(0,level,ops[node+2].ival,&numarg);
		--level;
	    }
	    else {
		prewalk(0,level,ops[node+2].ival,&numarg);
	    }
	}
	break;
    case OPPAREN:
	prewalk(0,level,ops[node+1].ival,&numarg);
	break;
    case OPANDAND:
	prewalk(0,level,ops[node+1].ival,&numarg);
	prewalk(0,level,ops[node+2].ival,&numarg);
	break;
    case OPOROR:
	prewalk(0,level,ops[node+1].ival,&numarg);
	prewalk(0,level,ops[node+2].ival,&numarg);
	break;
    case OPNOT:
	prewalk(0,level,ops[node+1].ival,&numarg);
	break;
    case OCPAREN:
	prewalk(0,level,ops[node+1].ival,&numarg);
	numeric |= numarg;
	break;
    case OCANDAND:
	prewalk(0,level,ops[node+1].ival,&numarg);
	numeric = 1;
	prewalk(0,level,ops[node+2].ival,&numarg);
	break;
    case OCOROR:
	prewalk(0,level,ops[node+1].ival,&numarg);
	numeric = 1;
	prewalk(0,level,ops[node+2].ival,&numarg);
	break;
    case OCNOT:
	prewalk(0,level,ops[node+1].ival,&numarg);
	numeric = 1;
	break;
    case ORELOP:
	prewalk(0,level,ops[node+2].ival,&numarg);
	numeric |= numarg;
	prewalk(0,level,ops[node+1].ival,&numarg);
	prewalk(0,level,ops[node+3].ival,&numarg);
	numeric |= numarg;
	numeric = 1;
	break;
    case ORPAREN:
	prewalk(0,level,ops[node+1].ival,&numarg);
	numeric |= numarg;
	break;
    case OMATCHOP:
	prewalk(0,level,ops[node+2].ival,&numarg);
	prewalk(0,level,ops[node+1].ival,&numarg);
	prewalk(0,level,ops[node+3].ival,&numarg);
	numeric = 1;
	break;
    case OMPAREN:
	prewalk(0,level,ops[node+1].ival,&numarg);
	numeric |= numarg;
	break;
    case OCONCAT:
	prewalk(0,level,ops[node+1].ival,&numarg);
	prewalk(0,level,ops[node+2].ival,&numarg);
	break;
    case OASSIGN:
	prewalk(0,level,ops[node+2].ival,&numarg);
	prewalk(0,level,ops[node+1].ival,&numarg);
	prewalk(0,level,ops[node+3].ival,&numarg);
	if (numarg || strlen(ops[ops[node+1].ival+1].cval) > (Size_t)1) {
	    numericize(ops[node+2].ival);
	    if (!numarg)
		numericize(ops[node+3].ival);
	}
	numeric |= numarg;
	break;
    case OADD:
	prewalk(1,level,ops[node+1].ival,&numarg);
	prewalk(1,level,ops[node+2].ival,&numarg);
	numeric = 1;
	break;
    case OSUBTRACT:
	prewalk(1,level,ops[node+1].ival,&numarg);
	prewalk(1,level,ops[node+2].ival,&numarg);
	numeric = 1;
	break;
    case OMULT:
	prewalk(1,level,ops[node+1].ival,&numarg);
	prewalk(1,level,ops[node+2].ival,&numarg);
	numeric = 1;
	break;
    case ODIV:
	prewalk(1,level,ops[node+1].ival,&numarg);
	prewalk(1,level,ops[node+2].ival,&numarg);
	numeric = 1;
	break;
    case OPOW:
	prewalk(1,level,ops[node+1].ival,&numarg);
	prewalk(1,level,ops[node+2].ival,&numarg);
	numeric = 1;
	break;
    case OMOD:
	prewalk(1,level,ops[node+1].ival,&numarg);
	prewalk(1,level,ops[node+2].ival,&numarg);
	numeric = 1;
	break;
    case OPOSTINCR:
	prewalk(1,level,ops[node+1].ival,&numarg);
	numeric = 1;
	break;
    case OPOSTDECR:
	prewalk(1,level,ops[node+1].ival,&numarg);
	numeric = 1;
	break;
    case OPREINCR:
	prewalk(1,level,ops[node+1].ival,&numarg);
	numeric = 1;
	break;
    case OPREDECR:
	prewalk(1,level,ops[node+1].ival,&numarg);
	numeric = 1;
	break;
    case OUMINUS:
	prewalk(1,level,ops[node+1].ival,&numarg);
	numeric = 1;
	break;
    case OUPLUS:
	prewalk(1,level,ops[node+1].ival,&numarg);
	numeric = 1;
	break;
    case OPAREN:
	prewalk(0,level,ops[node+1].ival,&numarg);
	numeric |= numarg;
	break;
    case OGETLINE:
	break;
    case OSPRINTF:
	prewalk(0,level,ops[node+1].ival,&numarg);
	break;
    case OSUBSTR:
	prewalk(0,level,ops[node+1].ival,&numarg);
	prewalk(1,level,ops[node+2].ival,&numarg);
	if (len == 3) {
	    prewalk(1,level,ops[node+3].ival,&numarg);
	}
	break;
    case OSTRING:
	break;
    case OSPLIT:
	numeric = 1;
	prewalk(0,level,ops[node+2].ival,&numarg);
	if (len == 3)
	    prewalk(0,level,ops[node+3].ival,&numarg);
	prewalk(0,level,ops[node+1].ival,&numarg);
	break;
    case OINDEX:
	prewalk(0,level,ops[node+1].ival,&numarg);
	prewalk(0,level,ops[node+2].ival,&numarg);
	numeric = 1;
	break;
    case OMATCH:
	prewalk(0,level,ops[node+1].ival,&numarg);
	prewalk(0,level,ops[node+2].ival,&numarg);
	numeric = 1;
	break;
    case OUSERDEF:
	subretnum = FALSE;
	--level;
	tmpstr = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
	++level;
	prewalk(0,level,ops[node+2].ival,&numarg);
	prewalk(0,level,ops[node+4].ival,&numarg);
	prewalk(0,level,ops[node+5].ival,&numarg);
	--level;
	str_cat(tmpstr,"(");
	tmp2str = str_new(0);
	if (subretnum || numarg)
	    str_set(tmp2str,"1");
	hstore(symtab,tmpstr->str_ptr,tmp2str);
	str_free(tmpstr);
	level++;
	break;
    case ORETURN:
	if (len > 0) {
	    prewalk(0,level,ops[node+1].ival,&numarg);
	    if (numarg)
		subretnum = TRUE;
	}
	break;
    case OUSERFUN:
	tmp2str = str_new(0);
	str_scat(tmp2str,tmpstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN));
	fixrargs(tmpstr->str_ptr,ops[node+2].ival,0);
	str_free(tmpstr);
	str_cat(tmp2str,"(");
	tmpstr = hfetch(symtab,tmp2str->str_ptr);
	if (tmpstr && tmpstr->str_ptr)
	    numeric |= atoi(tmpstr->str_ptr);
	prewalk(0,level,ops[node+2].ival,&numarg);
	str_free(tmp2str);
	break;
    case OGSUB:
    case OSUB:
	if (len >= 3)
	    prewalk(0,level,ops[node+3].ival,&numarg);
	prewalk(0,level,ops[ops[node+2].ival+1].ival,&numarg);
	prewalk(0,level,ops[node+1].ival,&numarg);
	numeric = 1;
	break;
    case ONUM:
	prewalk(0,level,ops[node+1].ival,&numarg);
	numeric = 1;
	break;
    case OSTR:
	prewalk(0,level,ops[node+1].ival,&numarg);
	break;
    case ODEFINED:
    case ODELETE:
    case OSTAR:
    case OVAR:
	prewalk(0,level,ops[node+1].ival,&numarg);
	if (len == 1) {
	    if (numit)
		numericize(node);
	}
	else {
	    prewalk(0,level,ops[node+2].ival,&numarg);
	}
	break;
    case OFLD:
	prewalk(0,level,ops[node+1].ival,&numarg);
	break;
    case OVFLD:
	i = ops[node+1].ival;
	prewalk(0,level,i,&numarg);
	break;
    case OJUNK:
	goto def;
    case OSNEWLINE:
	break;
    case ONEWLINE:
	break;
    case OSCOMMENT:
	break;
    case OCOMMENT:
	break;
    case OCOMMA:
	prewalk(0,level,ops[node+1].ival,&numarg);
	prewalk(0,level,ops[node+2].ival,&numarg);
	prewalk(0,level,ops[node+3].ival,&numarg);
	break;
    case OSEMICOLON:
	break;
    case OSTATES:
	prewalk(0,level,ops[node+1].ival,&numarg);
	prewalk(0,level,ops[node+2].ival,&numarg);
	break;
    case OSTATE:
	if (len >= 1) {
	    prewalk(0,level,ops[node+1].ival,&numarg);
	    if (len >= 2) {
		prewalk(0,level,ops[node+2].ival,&numarg);
	    }
	}
	break;
    case OCLOSE:
	prewalk(0,level,ops[node+1].ival,&numarg);
	break;
    case OPRINTF:
    case OPRINT:
	if (len == 3) {		/* output redirection */
	    prewalk(0,level,ops[node+3].ival,&numarg);
	    prewalk(0,level,ops[node+2].ival,&numarg);
	}
	prewalk(0+(type==OPRINT),level,ops[node+1].ival,&numarg);
	break;
    case ORAND:
	break;
    case OSRAND:
	goto maybe0;
    case OATAN2:
	goto maybe0;
    case OSIN:
	goto maybe0;
    case OCOS:
	goto maybe0;
    case OSYSTEM:
	goto maybe0;
    case OLENGTH:
	goto maybe0;
    case OLOG:
	goto maybe0;
    case OEXP:
	goto maybe0;
    case OSQRT:
	goto maybe0;
    case OINT:
      maybe0:
	numeric = 1;
	if (len > 0)
	    prewalk(type != OLENGTH && type != OSYSTEM,
	      level,ops[node+1].ival,&numarg);
	break;
    case OBREAK:
	break;
    case ONEXT:
	break;
    case OEXIT:
	if (len == 1) {
	    prewalk(1,level,ops[node+1].ival,&numarg);
	}
	break;
    case OCONTINUE:
	break;
    case OREDIR:
	goto def;
    case OIF:
	prewalk(0,level,ops[node+1].ival,&numarg);
	prewalk(0,level,ops[node+2].ival,&numarg);
	if (len == 3) {
	    prewalk(0,level,ops[node+3].ival,&numarg);
	}
	break;
    case OWHILE:
	prewalk(0,level,ops[node+1].ival,&numarg);
	prewalk(0,level,ops[node+2].ival,&numarg);
	break;
    case OFOR:
	prewalk(0,level,ops[node+1].ival,&numarg);
	prewalk(0,level,ops[node+2].ival,&numarg);
	prewalk(0,level,ops[node+3].ival,&numarg);
	prewalk(0,level,ops[node+4].ival,&numarg);
	break;
    case OFORIN:
	prewalk(0,level,ops[node+2].ival,&numarg);
	prewalk(0,level,ops[node+1].ival,&numarg);
	break;
    case OBLOCK:
	if (len == 2) {
	    prewalk(0,level,ops[node+2].ival,&numarg);
	}
	++level;
	prewalk(0,level,ops[node+1].ival,&numarg);
	--level;
	break;
    default:
      def:
	if (len) {
	    if (len > 5)
		fatal("Garbage length in prewalk");
	    prewalk(0,level,ops[node+1].ival,&numarg);
	    for (i = 2; i<= len; i++) {
		prewalk(0,level,ops[node+i].ival,&numarg);
	    }
	}
	break;
    }
    *numericptr = numeric;
    return 1;
}

static void
numericize(register int node)
{
    register int len;
    register int type;
    STR *tmpstr;
    STR *tmp2str;
    int numarg;

    type = ops[node].ival;
    len = type >> 8;
    type &= 255;
    if (type == OVAR && len == 1) {
	tmpstr=walk(0,0,ops[node+1].ival,&numarg,P_MIN);
	tmp2str = str_make("1");
	hstore(symtab,tmpstr->str_ptr,tmp2str);
    }
}
