/* $Header: eval.c,v 3.0.1.11 91/01/11 17:58:30 lwall Locked $
 *
 *    Copyright (c) 1989, Larry Wall
 *
 *    You may distribute under the terms of the GNU General Public License
 *    as specified in the README file that comes with the perl 3.0 kit.
 *
 * $Log:	eval.c,v $
 * Revision 3.0.1.11  91/01/11  17:58:30  lwall
 * patch42: ANSIfied the stat mode checking
 * patch42: perl -D14 crashed on ..
 * patch42: waitpid() emulation was useless because of #ifdef WAITPID
 * 
 * Revision 3.0.1.10  90/11/10  01:33:22  lwall
 * patch38: random cleanup
 * patch38: couldn't return from sort routine
 * patch38: added hooks for unexec()
 * patch38: added alarm function
 * 
 * Revision 3.0.1.9  90/10/15  16:46:13  lwall
 * patch29: added caller
 * patch29: added scalar
 * patch29: added cmp and <=>
 * patch29: added sysread and syswrite
 * patch29: added -M, -A and -C
 * patch29: index and substr now have optional 3rd args
 * patch29: you can now read into the middle string
 * patch29: ~ now works on vector string
 * patch29: non-existent array values no longer cause core dumps
 * patch29: eof; core dumped
 * patch29: oct and hex now produce unsigned result
 * patch29: unshift did not return the documented value
 * 
 * Revision 3.0.1.8  90/08/13  22:17:14  lwall
 * patch28: the NSIG hack didn't work right on Xenix
 * patch28: defined(@array) and defined(%array) didn't work right
 * patch28: rename was busted on systems without rename system call
 * 
 * Revision 3.0.1.7  90/08/09  03:33:44  lwall
 * patch19: made ~ do vector operation on strings like &, | and ^
 * patch19: dbmopen(%name...) didn't work right
 * patch19: dbmopen(name, 'filename', undef) now refrains from creating
 * patch19: empty %array now returns 0 in scalar context
 * patch19: die with no arguments no longer exits unconditionally
 * patch19: return outside a subroutine now returns a reasonable message
 * patch19: rename done with unlink()/link()/unlink() now checks for clobbering
 * patch19: -s now returns size of file
 * 
 * Revision 3.0.1.6  90/03/27  15:53:51  lwall
 * patch16: MSDOS support
 * patch16: support for machines that can't cast negative floats to unsigned ints
 * patch16: ioctl didn't return values correctly
 * 
 * Revision 3.0.1.5  90/03/12  16:37:40  lwall
 * patch13: undef $/ didn't work as advertised
 * patch13: added list slice operator (LIST)[LIST]
 * patch13: added splice operator: @oldelems = splice(@array,$offset,$len,LIST)
 * 
 * Revision 3.0.1.4  90/02/28  17:36:59  lwall
 * patch9: added pipe function
 * patch9: a return in scalar context wouldn't return array
 * patch9: !~ now always returns scalar even in array context
 * patch9: some machines can't cast float to long with high bit set
 * patch9: piped opens returned undef in child
 * patch9: @array in scalar context now returns length of array
 * patch9: chdir; coredumped
 * patch9: wait no longer ignores signals
 * patch9: mkdir now handles odd versions of /bin/mkdir
 * patch9: -l FILEHANDLE now disallowed
 * 
 * Revision 3.0.1.3  89/12/21  20:03:05  lwall
 * patch7: errno may now be a macro with an lvalue
 * patch7: ANSI strerror() is now supported
 * patch7: send() didn't allow a TO argument
 * patch7: ord() now always returns positive even on signed char machines
 * 
 * Revision 3.0.1.2  89/11/17  15:19:34  lwall
 * patch5: constant numeric subscripts get lost inside ?:
 * 
 * Revision 3.0.1.1  89/11/11  04:31:51  lwall
 * patch2: mkdir and rmdir needed to quote argument when passed to shell
 * patch2: mkdir and rmdir now return better error codes
 * patch2: fileno, seekdir, rewinddir and closedir now disallow defaults
 * 
 * Revision 3.0  89/10/18  15:17:04  lwall
 * 3.0 baseline
 * 
 */

#include "EXTERN.h"
#include "perl.h"

#include <math.h>
#include <signal.h>

#ifdef I_FCNTL
#include <fcntl.h>
#endif
#ifdef I_VFORK
#   include <vfork.h>
#endif

ARG *debarg;
STR str_args;
static struct lstring *lstr;
static int old_record_separator;

char *getlogin PROTO((void));

int
eval(arg,gimme,sp)
register ARG *arg;
int gimme;
register int sp;
{
    register STR *str;
    register int anum;
    register int optype;
    register STR **st;
    int maxarg;
    double value;
    register char *tmps;
    char *tmps2;
    char *tmps3;
    int argflags;
    int argtype;
    union argptr argptr;
    int arglast[8];	/* highest sp for arg--valid only for non-O_LIST args */
    unsigned long tmplong;
    long tmplong2;
    TIME_T when;
    FILE *fp;
    STR *tmpstr;
    FCMD *form;
    STAB *stab;
    ARRAY *ary;
    bool assigning = FALSE;

    if (!arg)
	goto say_undef;
    optype = arg->arg_type;
    maxarg = arg->arg_len;
    arglast[0] = sp;
    str = arg->arg_ptr.arg_str;
    if (sp + maxarg > stack->ary_max)
	astore(stack, sp + maxarg, Nullstr);
    st = stack->ary_array;

#ifdef DEBUGGING
    if (debug) {
	if (debug & 8) {
	    deb("%s (%lx) %d args:\n",opname[optype],arg,maxarg);
	}
	debname[dlevel] = opname[optype][0];
	debdelim[dlevel] = ':';
	if (++dlevel >= dlmax)
	    grow_dlevel();
    }
#endif

#include "xc.evalargs"

    st += arglast[0];
    switch (optype) {
    case O_RCAT:
	STABSET(str);
	break;
    case O_ITEM:
	if (gimme == G_ARRAY)
	    goto array_return;
	/* FALL THROUGH */
    case O_SCALAR:
	STR_SSET(str,st[1]);
	STABSET(str);
	break;
    case O_ITEM2:
	if (gimme == G_ARRAY)
	    goto array_return;
	--anum;
	STR_SSET(str,st[arglast[anum]-arglast[0]]);
	STABSET(str);
	break;
    case O_ITEM3:
	if (gimme == G_ARRAY)
	    goto array_return;
	--anum;
	STR_SSET(str,st[arglast[anum]-arglast[0]]);
	STABSET(str);
	break;
    case O_CONCAT:
	STR_SSET(str,st[1]);
	str_scat(str,st[2]);
	STABSET(str);
	break;
    case O_REPEAT:
	STR_SSET(str,st[1]);
	anum = (int)str_gnum(st[2]);
	if (anum >= 1) {
	    tmpstr = Str_new(50, 0);
	    str_sset(tmpstr,str);
	    tmps = str_get(tmpstr);	/* force to be string */
	    STR_GROW(str, (anum * str->str_cur) + 1);
	    repeatcpy(str->str_ptr, tmps, tmpstr->str_cur, anum);
	    str->str_cur *= anum;
	    str->str_ptr[str->str_cur] = '\0';
	}
	else
	    str_sset(str,&str_no);
	STABSET(str);
	break;
    case O_MATCH:
	sp = do_match(str,arg,
	  gimme,arglast);
	if (gimme == G_ARRAY)
	    goto array_return;
	STABSET(str);
	break;
    case O_NMATCH:
	sp = do_match(str,arg,
	  G_SCALAR,arglast);
	str_sset(str, str_true(str) ? &str_no : &str_yes);
	STABSET(str);
	break;
    case O_SUBST:
	sp = do_subst(str,arg,arglast[0]);
	goto array_return;
    case O_NSUBST:
	sp = do_subst(str,arg,arglast[0]);
	str = arg->arg_ptr.arg_str;
	str_set(str, str_true(str) ? No : Yes);
	goto array_return;
    case O_ASSIGN:
	if (arg[1].arg_flags & AF_ARYOK) {
	    if (arg->arg_len == 1) {
		arg->arg_type = O_LOCAL;
		goto local;
	    }
	    else {
		arg->arg_type = O_AASSIGN;
		goto aassign;
	    }
	}
	else {
	    arg->arg_type = O_SASSIGN;
	    goto sassign;
	}
    case O_LOCAL:
      local:
	arglast[2] = arglast[1];	/* push a null array */
	/* FALL THROUGH */
    case O_AASSIGN:
      aassign:
	sp = do_assign(arg,
	  gimme,arglast);
	goto array_return;
    case O_SASSIGN:
      sassign:
	STR_SSET(str, st[2]);
	STABSET(str);
	break;
    case O_CHOP:
	st -= arglast[0];
	str = arg->arg_ptr.arg_str;
	for (sp = arglast[0] + 1; sp <= arglast[1]; sp++)
	    do_chop(str,st[sp]);
	st += arglast[0];
	break;
    case O_DEFINED:
	if (arg[1].arg_type & A_DONT) {
	    sp = do_defined(str,arg,
		  gimme,arglast);
	    goto array_return;
	}
	else if (str->str_pok || str->str_nok)
	    goto say_yes;
	goto say_no;
    case O_UNDEF:
	if (arg[1].arg_type & A_DONT) {
	    sp = do_undef(str,arg,
	      gimme,arglast);
	    goto array_return;
	}
	else if (str != stab_val(defstab)) {
	    str->str_pok = str->str_nok = 0;
	    STABSET(str);
	}
	goto say_undef;
    case O_STUDY:
	sp = do_study(str,arg,
	  gimme,arglast);
	goto array_return;
    case O_POW:
	value = str_gnum(st[1]);
	value = pow(value,str_gnum(st[2]));
	goto donumset;
    case O_MULTIPLY:
	value = str_gnum(st[1]);
	value *= str_gnum(st[2]);
	goto donumset;
    case O_DIVIDE:
    	if ((value = str_gnum(st[2])) == 0.0)
    	    fatal("Illegal division by zero");
	value = str_gnum(st[1]) / value;
	goto donumset;
    case O_MODULO:
	tmplong = (long) str_gnum(st[2]);
    	if (tmplong == 0L)
    	    fatal("Illegal modulus zero");
	tmplong2 = (long)str_gnum(st[1]);
#ifndef lint
	if (tmplong2 >= 0)
	    value = (double)(tmplong2 % tmplong);
	else
	    value = (double)(tmplong - ((-tmplong2 - 1) % tmplong)) - 1;
#endif
	goto donumset;
    case O_ADD:
	value = str_gnum(st[1]);
	value += str_gnum(st[2]);
	goto donumset;
    case O_SUBTRACT:
	value = str_gnum(st[1]);
	value -= str_gnum(st[2]);
	goto donumset;
    case O_LEFT_SHIFT:
	value = str_gnum(st[1]);
	anum = (int)str_gnum(st[2]);
#ifndef lint
	value = (double)(U_L(value) << anum);
#endif
	goto donumset;
    case O_RIGHT_SHIFT:
	value = str_gnum(st[1]);
	anum = (int)str_gnum(st[2]);
#ifndef lint
	value = (double)(U_L(value) >> anum);
#endif
	goto donumset;
    case O_LT:
	value = str_gnum(st[1]);
	value = (value < str_gnum(st[2])) ? 1.0 : 0.0;
	goto donumset;
    case O_GT:
	value = str_gnum(st[1]);
	value = (value > str_gnum(st[2])) ? 1.0 : 0.0;
	goto donumset;
    case O_LE:
	value = str_gnum(st[1]);
	value = (value <= str_gnum(st[2])) ? 1.0 : 0.0;
	goto donumset;
    case O_GE:
	value = str_gnum(st[1]);
	value = (value >= str_gnum(st[2])) ? 1.0 : 0.0;
	goto donumset;
    case O_EQ:
	if (dowarn) {
	    if ((!st[1]->str_nok && !looks_like_number(st[1])) ||
		(!st[2]->str_nok && !looks_like_number(st[2])) )
		warn("Possible use of == on string value");
	}
	value = str_gnum(st[1]);
	value = (value == str_gnum(st[2])) ? 1.0 : 0.0;
	goto donumset;
    case O_NE:
	value = str_gnum(st[1]);
	value = (value != str_gnum(st[2])) ? 1.0 : 0.0;
	goto donumset;
    case O_NCMP:
	value = str_gnum(st[1]);
	value -= str_gnum(st[2]);
	if (value > 0.0)
	    value = 1.0;
	else if (value < 0.0)
	    value = -1.0;
	goto donumset;
    case O_BIT_AND:
	if (!sawvec || st[1]->str_nok || st[2]->str_nok) {
	    value = str_gnum(st[1]);
#ifndef lint
	    value = (double)(U_L(value) & U_L(str_gnum(st[2])));
#endif
	    goto donumset;
	}
	else
	    do_vop(optype,str,st[1],st[2]);
	break;
    case O_XOR:
	if (!sawvec || st[1]->str_nok || st[2]->str_nok) {
	    value = str_gnum(st[1]);
#ifndef lint
	    value = (double)(U_L(value) ^ U_L(str_gnum(st[2])));
#endif
	    goto donumset;
	}
	else
	    do_vop(optype,str,st[1],st[2]);
	break;
    case O_BIT_OR:
	if (!sawvec || st[1]->str_nok || st[2]->str_nok) {
	    value = str_gnum(st[1]);
#ifndef lint
	    value = (double)(U_L(value) | U_L(str_gnum(st[2])));
#endif
	    goto donumset;
	}
	else
	    do_vop(optype,str,st[1],st[2]);
	break;
/* use register in evaluating str_true() */
    case O_AND:
	if (str_true(st[1])) {
	    anum = 2;
	    optype = O_ITEM2;
	    argflags = arg[anum].arg_flags;
	    if (gimme == G_ARRAY)
		argflags |= AF_ARYOK;
	    argtype = arg[anum].arg_type & A_MASK;
	    argptr = arg[anum].arg_ptr;
	    maxarg = anum = 1;
	    sp = arglast[0];
	    st -= sp;
	    goto re_eval;
	}
	else {
	    if (assigning) {
		str_sset(str, st[1]);
		STABSET(str);
	    }
	    else
		str = st[1];
	    break;
	}
    case O_OR:
	if (str_true(st[1])) {
	    if (assigning) {
		str_sset(str, st[1]);
		STABSET(str);
	    }
	    else
		str = st[1];
	    break;
	}
	else {
	    anum = 2;
	    optype = O_ITEM2;
	    argflags = arg[anum].arg_flags;
	    if (gimme == G_ARRAY)
		argflags |= AF_ARYOK;
	    argtype = arg[anum].arg_type & A_MASK;
	    argptr = arg[anum].arg_ptr;
	    maxarg = anum = 1;
	    sp = arglast[0];
	    st -= sp;
	    goto re_eval;
	}
    case O_COND_EXPR:
	anum = (str_true(st[1]) ? 2 : 3);
	optype = (anum == 2 ? O_ITEM2 : O_ITEM3);
	argflags = arg[anum].arg_flags;
	if (gimme == G_ARRAY)
	    argflags |= AF_ARYOK;
	argtype = arg[anum].arg_type & A_MASK;
	argptr = arg[anum].arg_ptr;
	maxarg = anum = 1;
	sp = arglast[0];
	st -= sp;
	goto re_eval;
    case O_COMMA:
	if (gimme == G_ARRAY)
	    goto array_return;
	str = st[2];
	break;
    case O_NEGATE:
	value = -str_gnum(st[1]);
	goto donumset;
    case O_NOT:
	value = (double) !str_true(st[1]);
	goto donumset;
    case O_COMPLEMENT:
	if (!sawvec || st[1]->str_nok) {
#ifndef lint
	    value = (double) ~U_L(str_gnum(st[1]));
#endif
	    goto donumset;
	}
	else {
	    STR_SSET(str,st[1]);
	    tmps = str_get(str);
	    for (anum = str->str_cur; anum; anum--, tmps++)
		*tmps = ~*tmps;
	}
	break;
    case O_SELECT:
	stab_fullname(str,defoutstab);
	if (maxarg > 0) {
	    if ((arg[1].arg_type & A_MASK) == A_WORD)
		defoutstab = arg[1].arg_ptr.arg_stab;
	    else
		defoutstab = stabent(str_get(st[1]),TRUE);
	    if (!stab_io(defoutstab))
		stab_io(defoutstab) = stio_new();
	    curoutstab = defoutstab;
	}
	STABSET(str);
	break;
    case O_WRITE:
	if (maxarg == 0)
	    stab = defoutstab;
	else if ((arg[1].arg_type & A_MASK) == A_WORD) {
	    if ((stab = arg[1].arg_ptr.arg_stab) == Nullstab)
		stab = defoutstab;
	}
	else
	    stab = stabent(str_get(st[1]),TRUE);
	if (!stab_io(stab)) {
	    str_set(str, No);
	    STABSET(str);
	    break;
	}
	curoutstab = stab;
	fp = stab_io(stab)->ofp;
	debarg = arg;
	if (stab_io(stab)->fmt_stab)
	    form = stab_form(stab_io(stab)->fmt_stab);
	else
	    form = stab_form(stab);
	if (!form || !fp) {
	    if (dowarn) {
		if (form)
		    warn("No format for filehandle");
		else {
		    if (stab_io(stab)->ifp)
			warn("Filehandle only opened for input");
		    else
			warn("Write on closed filehandle");
		}
	    }
	    str_set(str, No);
	    STABSET(str);
	    break;
	}
	format(&outrec,form,sp);
	do_write(&outrec,stab_io(stab),sp);
	if (stab_io(stab)->flags & IOF_FLUSH)
	    (void)fflush(fp);
	str_set(str, Yes);
	STABSET(str);
	break;
    case O_DBMOPEN:
#ifdef SOME_DBM
	stab = arg[1].arg_ptr.arg_stab;
	if (st[3]->str_nok || st[3]->str_pok)
	    anum = (int)str_gnum(st[3]);
	else
	    anum = -1;
	value = (double)hdbmopen(stab_hash(stab),str_get(st[2]),anum);
	goto donumset;
#else
	fatal("No dbm or ndbm on this machine");
#endif
    case O_DBMCLOSE:
#ifdef SOME_DBM
	stab = arg[1].arg_ptr.arg_stab;
	hdbmclose(stab_hash(stab));
	goto say_yes;
#else
	fatal("No dbm or ndbm on this machine");
#endif
    case O_OPEN:
	if ((arg[1].arg_type & A_MASK) == A_WORD)
	    stab = arg[1].arg_ptr.arg_stab;
	else
	    stab = stabent(str_get(st[1]),TRUE);
	tmps = str_get(st[2]);
	if (do_open(stab,tmps,st[2]->str_cur)) {
	    value = 1.0;
	    stab_io(stab)->lines = 0;
	    goto donumset;
	}
	else
	    goto say_undef;
	/* break; */
    case O_TRANS:
	value = (double) do_trans(str,arg);
	str = arg->arg_ptr.arg_str;
	goto donumset;
    case O_NTRANS:
	str_set(arg->arg_ptr.arg_str, do_trans(str,arg) == 0 ? Yes : No);
	str = arg->arg_ptr.arg_str;
	break;
    case O_CLOSE:
	if (maxarg == 0)
	    stab = defoutstab;
	else if ((arg[1].arg_type & A_MASK) == A_WORD)
	    stab = arg[1].arg_ptr.arg_stab;
	else
	    stab = stabent(str_get(st[1]),TRUE);
	str_set(str, do_close(stab,TRUE) ? Yes : No );
	STABSET(str);
	break;
    case O_EACH:
	sp = do_each(str,stab_hash(arg[1].arg_ptr.arg_stab),
	  gimme,arglast);
	goto array_return;
    case O_VALUES:
    case O_KEYS:
	sp = do_kv(str,stab_hash(arg[1].arg_ptr.arg_stab), optype,
	  gimme,arglast);
	goto array_return;
    case O_LARRAY:
	str->str_nok = str->str_pok = 0;
	str->str_u.str_stab = arg[1].arg_ptr.arg_stab;
	str->str_state = SS_ARY;
	break;
    case O_ARRAY:
	ary = stab_array(arg[1].arg_ptr.arg_stab);
	maxarg = ary->ary_fill + 1;
	if (gimme == G_ARRAY) { /* array wanted */
	    sp = arglast[0];
	    st -= sp;
	    if (maxarg > 0 && sp + maxarg > stack->ary_max) {
		astore(stack,sp + maxarg, Nullstr);
		st = stack->ary_array;
	    }
	    st += sp;
	    Copy(ary->ary_array, &st[1], maxarg, STR*);
	    sp += maxarg;
	    goto array_return;
	}
	else {
	    value = (double)maxarg;
	    goto donumset;
	}
    case O_AELEM:
	anum = ((int)str_gnum(st[2])) - arybase;
	str = afetch(stab_array(arg[1].arg_ptr.arg_stab),anum,FALSE);
	break;
    case O_DELETE:
	tmpstab = arg[1].arg_ptr.arg_stab;
	tmps = str_get(st[2]);
	str = hdelete(stab_hash(tmpstab),tmps,st[2]->str_cur);
	if (tmpstab == envstab)
	    setenv(tmps,Nullch);
	if (!str)
	    goto say_undef;
	break;
    case O_LHASH:
	str->str_nok = str->str_pok = 0;
	str->str_u.str_stab = arg[1].arg_ptr.arg_stab;
	str->str_state = SS_HASH;
	break;
    case O_HASH:
	if (gimme == G_ARRAY) { /* array wanted */
	    sp = do_kv(str,stab_hash(arg[1].arg_ptr.arg_stab), optype,
		gimme,arglast);
	    goto array_return;
	}
	else {
	    tmpstab = arg[1].arg_ptr.arg_stab;
	    if (!stab_hash(tmpstab)->tbl_fill)
		goto say_zero;
	    sprintf(buf,"%d/%d",stab_hash(tmpstab)->tbl_fill,
		stab_hash(tmpstab)->tbl_max+1);
	    str_set(str,buf);
	}
	break;
    case O_HELEM:
	tmpstab = arg[1].arg_ptr.arg_stab;
	tmps = str_get(st[2]);
	str = hfetch(stab_hash(tmpstab),tmps,st[2]->str_cur,FALSE);
	break;
    case O_LAELEM:
	anum = ((int)str_gnum(st[2])) - arybase;
	str = afetch(stab_array(arg[1].arg_ptr.arg_stab),anum,TRUE);
	if (!str || str == &str_undef)
	    fatal("Assignment to non-creatable value, subscript %d",anum);
	break;
    case O_LHELEM:
	tmpstab = arg[1].arg_ptr.arg_stab;
	tmps = str_get(st[2]);
	anum = st[2]->str_cur;
	str = hfetch(stab_hash(tmpstab),tmps,anum,TRUE);
	if (!str || str == &str_undef)
	    fatal("Assignment to non-creatable value, subscript \"%s\"",tmps);
	if (tmpstab == envstab)		/* heavy wizardry going on here */
	    str_magic(str, tmpstab, 'E', tmps, anum);	/* str is now magic */
					/* he threw the brick up into the air */
	else if (tmpstab == sigstab)
	    str_magic(str, tmpstab, 'S', tmps, anum);
#ifdef SOME_DBM
	else if (stab_hash(tmpstab)->tbl_dbm)
	    str_magic(str, tmpstab, 'D', tmps, anum);
#endif
	else if (perldb && tmpstab == DBline)
	    str_magic(str, tmpstab, 'L', tmps, anum);
	break;
    case O_LSLICE:
	anum = 2;
	argtype = FALSE;
	goto do_slice_already;
    case O_ASLICE:
	anum = 1;
	argtype = FALSE;
	goto do_slice_already;
    case O_HSLICE:
	anum = 0;
	argtype = FALSE;
	goto do_slice_already;
    case O_LASLICE:
	anum = 1;
	argtype = TRUE;
	goto do_slice_already;
    case O_LHSLICE:
	anum = 0;
	argtype = TRUE;
      do_slice_already:
	sp = do_slice(arg[1].arg_ptr.arg_stab,str,anum,argtype,
	    gimme,arglast);
	goto array_return;
    case O_SPLICE:
	sp = do_splice(stab_array(arg[1].arg_ptr.arg_stab),gimme,arglast);
	goto array_return;
    case O_PUSH:
	if (arglast[2] - arglast[1] != 1)
	    str = do_push(stab_array(arg[1].arg_ptr.arg_stab),arglast);
	else {
	    str = Str_new(51,0);		/* must copy the STR */
	    str_sset(str,st[2]);
	    (void)apush(stab_array(arg[1].arg_ptr.arg_stab),str);
	}
	break;
    case O_POP:
	str = apop(ary = stab_array(arg[1].arg_ptr.arg_stab));
	goto staticalization;
    case O_SHIFT:
	str = ashift(ary = stab_array(arg[1].arg_ptr.arg_stab));
      staticalization:
	if (!str)
	    goto say_undef;
	if (ary->ary_flags & ARF_REAL)
	    (void)str_2static(str);
	break;
    case O_UNPACK:
	sp = do_unpack(str,gimme,arglast);
	goto array_return;
    case O_SPLIT:
	value = str_gnum(st[3]);
	sp = do_split(str, arg[2].arg_ptr.arg_spat, (int)value,
	  gimme,arglast);
	goto array_return;
    case O_LENGTH:
	if (maxarg < 1)
	    value = (double)str_len(stab_val(defstab));
	else
	    value = (double)str_len(st[1]);
	goto donumset;
    case O_SPRINTF:
	do_sprintf(str, sp-arglast[0], st+1);
	break;
    case O_SUBSTR:
	anum = ((int)str_gnum(st[2])) - arybase;	/* anum=where to start*/
	tmps = str_get(st[1]);		/* force conversion to string */
	if ((argtype = (str == st[1])) != 0)
	    str = arg->arg_ptr.arg_str;
	if (anum < 0)
	    anum += st[1]->str_cur + arybase;
	if (anum < 0 || anum > st[1]->str_cur)
	    str_nset(str,"",0);
	else {
	    optype = maxarg < 3 ? st[1]->str_cur : (int)str_gnum(st[3]);
	    if (optype < 0)
		optype = 0;
	    tmps += anum;
	    anum = st[1]->str_cur - anum;	/* anum=how many bytes left*/
	    if (anum > optype)
		anum = optype;
	    str_nset(str, tmps, anum);
	    if (argtype) {			/* it's an lvalue! */
		lstr = (struct lstring*)str;
		str->str_magic = st[1];
		st[1]->str_rare = 's';
		lstr->lstr_offset = tmps - str_get(st[1]); 
		lstr->lstr_len = anum; 
	    }
	}
	break;
    case O_PACK:
	(void)do_pack(str,arglast);
	break;
    case O_GREP:
	sp = do_grep(arg,str,gimme,arglast);
	goto array_return;
    case O_JOIN:
	do_join(str,arglast);
	break;
    case O_SLT:
	tmps = str_get(st[1]);
	value = (double) (str_cmp(st[1],st[2]) < 0);
	goto donumset;
    case O_SGT:
	tmps = str_get(st[1]);
	value = (double) (str_cmp(st[1],st[2]) > 0);
	goto donumset;
    case O_SLE:
	tmps = str_get(st[1]);
	value = (double) (str_cmp(st[1],st[2]) <= 0);
	goto donumset;
    case O_SGE:
	tmps = str_get(st[1]);
	value = (double) (str_cmp(st[1],st[2]) >= 0);
	goto donumset;
    case O_SEQ:
	tmps = str_get(st[1]);
	value = (double) str_eq(st[1],st[2]);
	goto donumset;
    case O_SNE:
	tmps = str_get(st[1]);
	value = (double) !str_eq(st[1],st[2]);
	goto donumset;
    case O_SCMP:
	tmps = str_get(st[1]);
	value = (double) str_cmp(st[1],st[2]);
	goto donumset;
    case O_SUBR:
	sp = do_subr(arg,gimme,arglast);
	st = stack->ary_array + arglast[0];		/* maybe realloced */
	goto array_return;
    case O_DBSUBR:
	sp = do_subr(arg,gimme,arglast);
	st = stack->ary_array + arglast[0];		/* maybe realloced */
	goto array_return;
    case O_CALLER:
	sp = do_caller(arg,maxarg,gimme,arglast);
	st = stack->ary_array + arglast[0];		/* maybe realloced */
	goto array_return;
    case O_SORT:
	if ((arg[1].arg_type & A_MASK) == A_WORD)
	    stab = arg[1].arg_ptr.arg_stab;
	else
	    stab = stabent(str_get(st[1]),TRUE);
	sp = do_sort(str,stab,
	  gimme,arglast);
	goto array_return;
    case O_REVERSE:
	if (gimme == G_ARRAY)
	    sp = do_reverse(arglast);
	else
	    sp = do_sreverse(str, arglast);
	goto array_return;
    case O_WARN:
	if (arglast[2] - arglast[1] != 1) {
	    do_join(str,arglast);
	    tmps = str_get(st[1]);
	}
	else {
	    str = st[2];
	    tmps = str_get(st[2]);
	}
	if (!tmps || !*tmps)
	    tmps = "Warning: something's wrong";
	warn("%s",tmps);
	goto say_yes;
    case O_DIE:
	if (arglast[2] - arglast[1] != 1) {
	    do_join(str,arglast);
	    tmps = str_get(st[1]);
	}
	else {
	    str = st[2];
	    tmps = str_get(st[2]);
	}
	if (!tmps || !*tmps)
	    tmps = "Died";
	fatal("%s",tmps);
	goto say_zero;
    case O_PRTF:
    case O_PRINT:
	if ((arg[1].arg_type & A_MASK) == A_WORD)
	    stab = arg[1].arg_ptr.arg_stab;
	else
	    stab = stabent(str_get(st[1]),TRUE);
	if (!stab)
	    stab = defoutstab;
	if (!stab_io(stab)) {
	    if (dowarn)
		warn("Filehandle never opened");
	    goto say_zero;
	}
	if ((fp = stab_io(stab)->ofp) == Nullfp) {
	    if (dowarn)  {
		if (stab_io(stab)->ifp)
		    warn("Filehandle opened only for input");
		else
		    warn("Print on closed filehandle");
	    }
	    goto say_zero;
	}
	else {
	    if (optype == O_PRTF || arglast[2] - arglast[1] != 1)
		value = (double)do_aprint(arg,fp,arglast);
	    else {
		value = (double)do_print(st[2],fp);
		if (orslen && optype == O_PRINT)
		    if (fwrite(ors, 1, orslen, fp) == 0)
			goto say_zero;
	    }
	    if (stab_io(stab)->flags & IOF_FLUSH)
		if (fflush(fp) == EOF)
		    goto say_zero;
	}
	goto donumset;
    case O_CHDIR:
	if (maxarg < 1)
	    tmps = Nullch;
	else
	    tmps = str_get(st[1]);
	if (!tmps || !*tmps) {
	    tmps = "&"; /* User root directory */
	}
#ifdef TAINT
	taintproper("Insecure dependency in chdir");
#endif
	value = (double)(chdir(tmps) >= 0);
	goto donumset;
    case O_EXIT:
	if (maxarg < 1)
	    anum = 0;
	else
	    anum = (int)str_gnum(st[1]);
	exit(anum);
	goto say_zero;
    case O_RESET:
	if (maxarg < 1)
	    tmps = "";
	else
	    tmps = str_get(st[1]);
	str_reset(tmps,curcmd->c_stash);
	value = 1.0;
	goto donumset;
    case O_LIST:
	if (gimme == G_ARRAY)
	    goto array_return;
	if (maxarg > 0)
	    str = st[sp - arglast[0]];	/* unwanted list, return last item */
	else
	    str = &str_undef;
	break;
    case O_EOF:
	if (maxarg <= 0)
	    stab = last_in_stab;
	else if ((arg[1].arg_type & A_MASK) == A_WORD)
	    stab = arg[1].arg_ptr.arg_stab;
	else
	    stab = stabent(str_get(st[1]),TRUE);
	str_set(str, do_eof(stab) ? Yes : No);
	STABSET(str);
	break;
    case O_GETC:
	if (maxarg <= 0)
	    stab = stdinstab;
	else if ((arg[1].arg_type & A_MASK) == A_WORD)
	    stab = arg[1].arg_ptr.arg_stab;
	else
	    stab = stabent(str_get(st[1]),TRUE);
	if (!stab)
	    stab = argvstab;
	if (!stab || do_eof(stab)) /* make sure we have fp with something */
	    goto say_undef;
	else {
#ifdef TAINT
	    tainted = 1;
#endif
	    str_set(str," ");
	    *str->str_ptr = getc(stab_io(stab)->ifp); /* should never be EOF */
	}
	STABSET(str);
	break;
    case O_TELL:
	if (maxarg <= 0)
	    stab = last_in_stab;
	else if ((arg[1].arg_type & A_MASK) == A_WORD)
	    stab = arg[1].arg_ptr.arg_stab;
	else
	    stab = stabent(str_get(st[1]),TRUE);
#ifndef lint
	value = (double)do_tell(stab);
#else
	(void)do_tell(stab);
#endif
	goto donumset;
    case O_READ:
	if ((arg[1].arg_type & A_MASK) == A_WORD)
	    stab = arg[1].arg_ptr.arg_stab;
	else
	    stab = stabent(str_get(st[1]),TRUE);
	tmps = str_get(st[2]);
	anum = (int)str_gnum(st[3]);
	errno = 0;
	maxarg = sp - arglast[0];
	if (maxarg > 4)
	    warn("Too many args on read");
	if (maxarg == 4)
	    maxarg = (int)str_gnum(st[4]);
	else
	    maxarg = 0;
	if (!stab_io(stab) || !stab_io(stab)->ifp)
	    goto say_undef;
	STR_GROW(st[2], anum+maxarg+1), (tmps = str_get(st[2]));  /* sneaky */
	anum = fread(tmps+maxarg, 1, anum, stab_io(stab)->ifp);
	if (anum < 0)
	    goto say_undef;
	st[2]->str_cur = anum;
	st[2]->str_ptr[anum] = '\0';
	value = (double)anum;
	goto donumset;
    case O_SEEK:
	if ((arg[1].arg_type & A_MASK) == A_WORD)
	    stab = arg[1].arg_ptr.arg_stab;
	else
	    stab = stabent(str_get(st[1]),TRUE);
	value = str_gnum(st[2]);
	str_set(str, do_seek(stab,
	  (long)value, (int)str_gnum(st[3]) ) ? Yes : No);
	STABSET(str);
	break;
    case O_RETURN:
	tmps = "_SUB_";		/* just fake up a "last _SUB_" */
	optype = O_LAST;
	if (curcsv && curcsv->wantarray == G_ARRAY) {
	    lastretstr = Nullstr;
	    lastspbase = arglast[1];
	    lastsize = arglast[2] - arglast[1];
	}
	else
	    lastretstr = str_static(st[arglast[2] - arglast[0]]);
	goto dopop;
    case O_REDO:
    case O_NEXT:
    case O_LAST:
	if (maxarg > 0) {
	    tmps = str_get(arg[1].arg_ptr.arg_str);
	  dopop:
	    while (loop_ptr >= 0 && (!loop_stack[loop_ptr].loop_label ||
	      strNE(tmps,loop_stack[loop_ptr].loop_label) )) {
#ifdef DEBUGGING
		if (debug & 4) {
		    deb("(Skipping label #%d %s)\n",loop_ptr,
			loop_stack[loop_ptr].loop_label);
		}
#endif
		loop_ptr--;
	    }
#ifdef DEBUGGING
	    if (debug & 4) {
		deb("(Found label #%d %s)\n",loop_ptr,
		    loop_stack[loop_ptr].loop_label);
	    }
#endif
	}
	if (loop_ptr < 0) {
	    if (tmps && strEQ(tmps, "_SUB_"))
		fatal("Can't return outside a subroutine");
	    fatal("Bad label: %s", maxarg > 0 ? tmps : "<null>");
	}
	if (!lastretstr && optype == O_LAST && lastsize) {
	    st -= arglast[0];
	    st += lastspbase + 1;
	    optype = loop_stack[loop_ptr].loop_sp - lastspbase; /* negative */
	    if (optype) {
		for (anum = lastsize; anum > 0; anum--,st++)
		    st[optype] = str_static(st[0]);
	    }
	    longjmp(loop_stack[loop_ptr].loop_env, O_LAST);
	}
	longjmp(loop_stack[loop_ptr].loop_env, optype);
    case O_GOTO:/* shudder */
	goto_targ = str_get(arg[1].arg_ptr.arg_str);
	if (!*goto_targ)
	    goto_targ = Nullch;		/* just restart from top */
	longjmp(top_env, 1);
    case O_INDEX:
	tmps = str_get(st[1]);
	if (maxarg < 3)
	    anum = 0;
	else {
	    anum = (int) str_gnum(st[3]) - arybase;
	    if (anum < 0)
		anum = 0;
	    else if (anum > st[1]->str_cur)
		anum = st[1]->str_cur;
	}
#ifndef lint
	if ((tmps2 = fbminstr((unsigned char*)tmps + anum,
	  (unsigned char*)tmps + st[1]->str_cur, st[2])) == Nullch)
#else
	if (tmps2 = fbminstr(Null(unsigned char*),Null(unsigned char*),Nullstr))
#endif
	    value = (double)(-1 + arybase);
	else
	    value = (double)(tmps2 - tmps + arybase);
	goto donumset;
    case O_RINDEX:
	tmps = str_get(st[1]);
	tmps2 = str_get(st[2]);
	if (maxarg < 3)
	    anum = st[1]->str_cur;
	else {
	    anum = (int) str_gnum(st[3]) - arybase + st[2]->str_cur;
	    if (anum < 0)
		anum = 0;
	    else if (anum > st[1]->str_cur)
		anum = st[1]->str_cur;
	}
#ifndef lint
	if ((tmps2 = rninstr(tmps,  tmps  + anum,
			     tmps2, tmps2 + st[2]->str_cur)) == Nullch)
#else
	if (tmps2 = rninstr(Nullch,Nullch,Nullch,Nullch))
#endif
	    value = (double)(-1 + arybase);
	else
	    value = (double)(tmps2 - tmps + arybase);
	goto donumset;
    case O_TIME:
#ifndef lint
	value = (double) time(Null(TIME_T*));
#endif
	goto donumset;
    case O_LOCALTIME:
	if (maxarg < 1)
	    (void)time(&when);
	else
	    when = (TIME_T)str_gnum(st[1]);
	{
		/* Under RISC OS, the localtime() function does not take
		 * summer time into account. We therefore assume the system
		 * time is localtime(), but if the OS variable Time$DST is
		 * set, we flag it as daylight saving time.
		 */
		struct tm *localtm;
		char *dst = getenv("Time$DST");

		localtm = localtime(&when);
		localtm->tm_isdst = (dst && *dst) ? 1 : 0;

		sp = do_time(str,localtm,gimme,arglast);
	}
	goto array_return;
    case O_GMTIME:
	if (maxarg < 1)
	    (void)time(&when);
	else
	    when = (TIME_T)str_gnum(st[1]);
	{
		/* Under RISC OS, there is no gmtime() function!! We
		 * therefore use localtime(), but if the OS variable
		 * Time$DST is set, we subtract 1 hour.
		 */
		struct tm *gmtm;
		time_t when_adj = when;
		char *dst = getenv("Time$DST");

		if (dst && *dst)
		{
		    when_adj -= 3600;
		}

		gmtm = localtime(&when_adj);
		gmtm->tm_isdst = 0;

		sp = do_time(str,gmtm,gimme,arglast);
	}
	goto array_return;
    case O_TRUNCATE:
	sp = do_truncate(str,arg,
	  gimme,arglast);
	goto array_return;
    case O_STAT:
	sp = do_stat(str,arg,
	  gimme,arglast);
	goto array_return;
    case O_CRYPT:
#ifdef CRYPT
	tmps = str_get(st[1]);
#ifdef FCRYPT
	str_set(str,fcrypt(tmps,str_get(st[2])));
#else
	str_set(str,crypt(tmps,str_get(st[2])));
#endif
#else
	fatal(
	  "The crypt() function is unimplemented due to excessive paranoia.");
#endif
	break;
    case O_ATAN2:
	value = str_gnum(st[1]);
	value = atan2(value,str_gnum(st[2]));
	goto donumset;
    case O_SIN:
	if (maxarg < 1)
	    value = str_gnum(stab_val(defstab));
	else
	    value = str_gnum(st[1]);
	value = sin(value);
	goto donumset;
    case O_COS:
	if (maxarg < 1)
	    value = str_gnum(stab_val(defstab));
	else
	    value = str_gnum(st[1]);
	value = cos(value);
	goto donumset;
    case O_RAND:
	if (maxarg < 1)
	    value = 1.0;
	else
	    value = str_gnum(st[1]);
	if (value == 0.0)
	    value = 1.0;
#if RANDBITS == 31
	value = rand() * value / 2147483648.0;
#else
#if RANDBITS == 16
	value = rand() * value / 65536.0;
#else
#if RANDBITS == 15
	value = rand() * value / 32768.0;
#else
	value = rand() * value / (double)(((unsigned long)1) << RANDBITS);
#endif
#endif
#endif
	goto donumset;
    case O_SRAND:
	if (maxarg < 1) {
	    (void)time(&when);
	    anum = when;
	}
	else
	    anum = (int)str_gnum(st[1]);
	(void)srand(anum);
	goto say_yes;
    case O_EXP:
	if (maxarg < 1)
	    value = str_gnum(stab_val(defstab));
	else
	    value = str_gnum(st[1]);
	value = exp(value);
	goto donumset;
    case O_LOG:
	if (maxarg < 1)
	    value = str_gnum(stab_val(defstab));
	else
	    value = str_gnum(st[1]);
	value = log(value);
	goto donumset;
    case O_SQRT:
	if (maxarg < 1)
	    value = str_gnum(stab_val(defstab));
	else
	    value = str_gnum(st[1]);
	value = sqrt(value);
	goto donumset;
    case O_INT:
	if (maxarg < 1)
	    value = str_gnum(stab_val(defstab));
	else
	    value = str_gnum(st[1]);
	if (value >= 0.0)
	    (void)modf(value,&value);
	else {
	    (void)modf(-value,&value);
	    value = -value;
	}
	goto donumset;
    case O_ORD:
	if (maxarg < 1)
	    tmps = str_get(stab_val(defstab));
	else
	    tmps = str_get(st[1]);
#ifndef I286
	value = (double) (*tmps & 255);
#else
	anum = (int) *tmps;
	value = (double) (anum & 255);
#endif
	goto donumset;
    case O_KILL:
	if (maxarg < 1)
	    anum = SIGTERM;
	else {
	    tmps = str_get(st[1]);

	    if (isupper(*tmps)) {
		if (*tmps == 'S' && tmps[1] == 'I' && tmps[2] == 'G')
		    tmps += 3;
		if ((anum = whichsig(tmps)) == 0)
		    fatal("Unrecognized signal name \"%s\"",tmps);
	    }
	    else
		anum = (int)str_gnum(st[1]);
	}
	anum = raise(anum);

	if (anum == 0)
	    goto say_yes;
	else
	    goto say_no;
    case O_SLEEP:
	if (maxarg < 1)
	    tmps = Nullch;
	else
	    tmps = str_get(st[1]);
	(void)time(&when);
	if (!tmps || !*tmps)
	    sleep((32767<<16)+32767);
	else
	    sleep((unsigned int)atoi(tmps));
#ifndef lint
	value = (double)when;
	(void)time(&when);
	value = ((double)when) - value;
#endif
	goto donumset;
    case O_RANGE:
	sp = do_range(gimme,arglast);
	goto array_return;
    case O_F_OR_R:
	if (gimme == G_ARRAY) {		/* it's a range */
	    /* can we optimize to constant array? */
	    if ((arg[1].arg_type & A_MASK) == A_SINGLE &&
	      (arg[2].arg_type & A_MASK) == A_SINGLE) {
		st[2] = arg[2].arg_ptr.arg_str;
		sp = do_range(gimme,arglast);
		st = stack->ary_array;
		maxarg = sp - arglast[0];
		str_free(arg[1].arg_ptr.arg_str);
		str_free(arg[2].arg_ptr.arg_str);
		arg->arg_type = O_ARRAY;
		arg[1].arg_type = A_STAB|A_DONT;
		arg->arg_len = 1;
		stab = arg[1].arg_ptr.arg_stab = aadd(genstab());
		ary = stab_array(stab);
		afill(ary,maxarg - 1);
		anum = maxarg;
		st += arglast[0]+1;
		while (maxarg-- > 0)
		    ary->ary_array[maxarg] = str_smake(st[maxarg]);
		st -= arglast[0]+1;
		goto array_return;
	    }
	    arg->arg_type = optype = O_RANGE;
	    maxarg = arg->arg_len = 2;
	    anum = 2;
	    arg[anum].arg_flags &= ~AF_ARYOK;
	    argflags = arg[anum].arg_flags;
	    argtype = arg[anum].arg_type & A_MASK;
	    arg[anum].arg_type = argtype;
	    argptr = arg[anum].arg_ptr;
	    sp = arglast[0];
	    st -= sp;
	    sp++;
	    goto re_eval;
	}
	arg->arg_type = O_FLIP;
	/* FALL THROUGH */
    case O_FLIP:
	if ((arg[1].arg_type & A_MASK) == A_SINGLE ?
	  last_in_stab && (int)str_gnum(st[1]) == stab_io(last_in_stab)->lines
	  :
	  str_true(st[1]) ) {
	    str_numset(str,0.0);
	    anum = 2;
	    arg->arg_type = optype = O_FLOP;
	    arg[2].arg_type &= ~A_DONT;
	    arg[1].arg_type |= A_DONT;
	    argflags = arg[2].arg_flags;
	    argtype = arg[2].arg_type & A_MASK;
	    argptr = arg[2].arg_ptr;
	    sp = arglast[0];
	    st -= sp++;
	    goto re_eval;
	}
	str_set(str,"");
	break;
    case O_FLOP:
	str_inc(str);
	if ((arg[2].arg_type & A_MASK) == A_SINGLE ?
	  last_in_stab && (int)str_gnum(st[2]) == stab_io(last_in_stab)->lines
	  :
	  str_true(st[2]) ) {
	    arg->arg_type = O_FLIP;
	    arg[1].arg_type &= ~A_DONT;
	    arg[2].arg_type |= A_DONT;
	    str_cat(str,"E0");
	}
	break;
    case O_SYSTEM:
	if ((arg[1].arg_type & A_MASK) == A_STAB)
	    value = (double)do_aspawn(st[1],arglast);
	else if (arglast[2] - arglast[1] != 1)
	    value = (double)do_aspawn(Nullstr,arglast);
	else {
	    value = (double)do_spawn(str_get(str_static(st[2])));
	}
	goto donumset;
    case O_EXEC_OP:
	if ((arg[1].arg_type & A_MASK) == A_STAB)
	    value = (double)do_aexec(st[1],arglast);
	else if (arglast[2] - arglast[1] != 1)
	    value = (double)do_aexec(Nullstr,arglast);
	else {
	    value = (double)do_exec(str_get(str_static(st[2])));
	}
	goto donumset;
    case O_HEX:
	argtype = 4;
	goto snarfnum;

    case O_OCT:
	argtype = 3;

      snarfnum:
	tmplong = 0;
	if (maxarg < 1)
	    tmps = str_get(stab_val(defstab));
	else
	    tmps = str_get(st[1]);
	for (;;) {
	    switch (*tmps) {
	    default:
		goto out;
	    case '8': case '9':
		if (argtype != 4)
		    goto out;
		/* FALL THROUGH */
	    case '0': case '1': case '2': case '3': case '4':
	    case '5': case '6': case '7':
		tmplong <<= argtype;
		tmplong += *tmps++ & 15L;
		break;
	    case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
	    case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
		if (argtype != 4)
		    goto out;
		tmplong <<= 4;
		tmplong += (*tmps++ & 7L) + 9L;
		break;
	    case 'x':
		argtype = 4;
		tmps++;
		break;
	    }
	}
      out:
	value = (double)tmplong;
	goto donumset;
    case O_UNLINK:
	value = (double)do_unlink(arglast);
	goto donumset;
    case O_RENAME:
	tmps = str_get(st[1]);
	tmps2 = str_get(st[2]);
#ifdef TAINT
	taintproper("Insecure dependency in rename");
#endif
	value = (double)(rename(tmps,tmps2) >= 0);
	goto donumset;
    case O_MKDIR:
	if (maxarg < 1)
	    tmps = str_get(stab_val(defstab));
	else
	    tmps = str_get(st[1]);
#ifdef TAINT
	taintproper("Insecure dependency in mkdir");
#endif
	value = (double)(mkdir(tmps) >= 0);
	goto donumset;
    case O_RMDIR:
	if (maxarg < 1)
	    tmps = str_get(stab_val(defstab));
	else
	    tmps = str_get(st[1]);
#ifdef TAINT
	taintproper("Insecure dependency in rmdir");
#endif
	value = (double)(rmdir(tmps) >= 0);
	goto donumset;
    case O_UNSHIFT:
	ary = stab_array(arg[1].arg_ptr.arg_stab);
	if (arglast[2] - arglast[1] != 1)
	    do_unshift(ary,arglast);
	else {
	    STR *tmpstr = Str_new(52,0);	/* must copy the STR */
	    str_sset(tmpstr,st[2]);
	    aunshift(ary,1);
	    (void)astore(ary,0,tmpstr);
	}
	value = (double)(ary->ary_fill + 1);
	goto donumset;

    case O_REQUIRE:
    case O_DOFILE:
    case O_EVAL:
	if (maxarg < 1)
	    tmpstr = stab_val(defstab);
	else
	    tmpstr =
	      (arg[1].arg_type & A_MASK) != A_NULL ? st[1] : stab_val(defstab);
#ifdef TAINT
	tainted |= tmpstr->str_tainted;
	taintproper("Insecure dependency in eval");
#endif
	sp = do_eval(tmpstr, optype, curcmd->c_stash,
	    gimme,arglast);
	goto array_return;

    case O_FTREAD:
	anum = S_READ;
	goto check_attr;
    case O_FTWRITE:
	anum = S_WRITE;
	goto check_attr;
    case O_FTLOCK:
	anum = S_LOCK;
	goto check_attr;
    case O_FTPREAD:
	anum = S_PREAD;
	goto check_attr;
    case O_FTPWRITE:
	anum = S_PWRITE;
      check_attr:
	if (mystat(arg,st[1]) < 0)
	    goto say_undef;
	if (anum & statcache.st_attr)
	    goto say_yes;
	goto say_no;
    case O_FTIS:
	if (mystat(arg,st[1]) < 0)
	    goto say_undef;
	goto say_yes;
    case O_FTZERO:
	if (mystat(arg,st[1]) < 0)
	    goto say_undef;
	if (!statcache.st_length)
	    goto say_yes;
	goto say_no;
    case O_FTSIZE:
	if (mystat(arg,st[1]) < 0)
	    goto say_undef;
	value = (double)statcache.st_length;
	goto donumset;
    case O_FTTIME:
	if (mystat(arg,st[1]) < 0)
	    goto say_undef;
	value = (basetime - statcache.st_time) / 8640000.0;
	goto donumset;
    case O_FTFILE:
	anum = T_FILE;
	goto check_file_type;
    case O_FTDIR:
	anum = T_DIRECTORY;
      check_file_type:
	if (mystat(arg,st[1]) < 0)
	    goto say_undef;
	if (statcache.st_type == anum)
	    goto say_yes;
	goto say_no;
    case O_FTTTY:
	if (arg[1].arg_type & A_DONT) {
	    stab = arg[1].arg_ptr.arg_stab;
	    tmps = "";
	}
	else
	    stab = stabent(tmps = str_get(st[1]),FALSE);
	if (stab && stab_io(stab) && stab_io(stab)->ifp)
	    fp = stab_io(stab)->ifp;
	else if (isdigit(*tmps)) {
	    anum = atoi(tmps);
	    switch (anum)
	    {
	    case 0:  fp = stdin;  break;
	    case 1:  fp = stdout; break;
	    case 2:  fp = stderr; break;
	    default: goto say_undef;
	    }
	}
	else
	    goto say_undef;
	if (isatty(fp))
	    goto say_yes;
	goto say_no;
    case O_FTTEXT:
    case O_FTBINARY:
	str = do_fttext(arg,st[1]);
	break;
    case O_VEC:
	sp = do_vec(str == st[1], arg->arg_ptr.arg_str, arglast);
	goto array_return;
    case O_OPENDIR:
    case O_READDIR:
    case O_TELLDIR:
    case O_SEEKDIR:
    case O_REWINDDIR:
    case O_CLOSEDIR:
	if (maxarg < 1)
	    goto say_undef;
	if ((arg[1].arg_type & A_MASK) == A_WORD)
	    stab = arg[1].arg_ptr.arg_stab;
	else
	    stab = stabent(str_get(st[1]),TRUE);
	if (!stab)
	    goto say_undef;
	sp = do_dirop(optype,stab,gimme,arglast);
	goto array_return;
    case O_SYSCALL:
	str = do_syscall(arglast);
	if (!str)
	    goto say_undef;
	break;
    }

  normal_return:
    st[1] = str;
#ifdef DEBUGGING
    if (debug) {
	dlevel--;
	if (debug & 8)
	    deb("%s RETURNS \"%s\"\n",opname[optype],str_get(str));
    }
#endif
    return arglast[0] + 1;

array_return:
#ifdef DEBUGGING
    if (debug) {
	dlevel--;
	if (debug & 8) {
	    anum = sp - arglast[0];
	    switch (anum) {
	    case 0:
		deb("%s RETURNS ()\n",opname[optype]);
		break;
	    case 1:
		deb("%s RETURNS (\"%s\")\n",opname[optype],str_get(st[1]));
		break;
	    default:
		tmps = str_get(st[1]);
		deb("%s RETURNS %d ARGS (\"%s\",%s\"%s\")\n",opname[optype],
		  anum,tmps,anum==2?"":"...,",str_get(st[anum]));
		break;
	    }
	}
    }
#endif
    return sp;

say_yes:
    str = &str_yes;
    goto normal_return;

say_no:
    str = &str_no;
    goto normal_return;

say_undef:
    str = &str_undef;
    goto normal_return;

say_zero:
    value = 0.0;
    /* FALL THROUGH */

donumset:
    str_numset(str,value);
    STABSET(str);
    st[1] = str;
#ifdef DEBUGGING
    if (debug) {
	dlevel--;
	if (debug & 8)
	    deb("%s RETURNS \"%f\"\n",opname[optype],value);
    }
#endif
    return arglast[0] + 1;
}
