perl 2.0 (no announcement message available)
[p5sagit/p5-mst-13.2.git] / arg.c
diff --git a/arg.c b/arg.c
index 74da53c..4cdb889 100644 (file)
--- a/arg.c
+++ b/arg.c
-/* $Header: arg.c,v 1.0.1.7 88/02/02 11:22:19 root Exp $
+/* $Header: arg.c,v 2.0 88/06/05 00:08:04 root Exp $
  *
  * $Log:       arg.c,v $
- * Revision 1.0.1.7  88/02/02  11:22:19  root
- * patch13: fixed split(' ') to work right second time.  Added CRYPT dependency.
- * 
- * Revision 1.0.1.6  88/02/01  17:32:26  root
- * patch12: made split(' ') behave like awk in ignoring leading white space.
- * 
- * Revision 1.0.1.5  88/01/30  08:53:16  root
- * patch9: fixed some missing right parens introduced (?) by patch 2
- * 
- * Revision 1.0.1.4  88/01/28  10:22:06  root
- * patch8: added eval operator.
- * 
- * Revision 1.0.1.2  88/01/24  03:52:34  root
- * patch 2: added STATBLKS dependencies.
- * 
- * Revision 1.0.1.1  88/01/21  21:27:10  root
- * Now defines signal return values correctly using VOIDSIG.
- * 
- * Revision 1.0  87/12/18  13:04:33  root
- * Initial revision
+ * Revision 2.0  88/06/05  00:08:04  root
+ * Baseline version 2.0.
  * 
  */
 
-#include <signal.h>
-#include "handy.h"
 #include "EXTERN.h"
-#include "search.h"
-#include "util.h"
 #include "perl.h"
 
-ARG *debarg;
+#include <signal.h>
+#include <errno.h>
+
+extern int errno;
 
-bool
-do_match(s,arg)
-register char *s;
+STR *
+do_match(arg,retary,sarg,ptrmaxsarg,sargoff,cushion)
 register ARG *arg;
+STR ***retary;
+register STR **sarg;
+int *ptrmaxsarg;
+int sargoff;
+int cushion;
 {
     register SPAT *spat = arg[2].arg_ptr.arg_spat;
-    register char *d;
     register char *t;
+    register char *s = str_get(sarg[1]);
+    char *strend = s + sarg[1]->str_cur;
 
-    if (!spat || !s)
-       fatal("panic: do_match\n");
+    if (!spat)
+       return &str_yes;
+    if (!s)
+       fatal("panic: do_match");
+    if (retary) {
+       *retary = sarg;         /* assume no match */
+       *ptrmaxsarg = sargoff;
+    }
     if (spat->spat_flags & SPAT_USED) {
 #ifdef DEBUGGING
        if (debug & 8)
            deb("2.SPAT USED\n");
 #endif
-       return FALSE;
+       return &str_no;
     }
     if (spat->spat_runtime) {
-       t = str_get(eval(spat->spat_runtime,Null(STR***)));
+       t = str_get(eval(spat->spat_runtime,Null(STR***),-1));
 #ifdef DEBUGGING
        if (debug & 8)
            deb("2.SPAT /%s/\n",t);
 #endif
-       if (d = compile(&spat->spat_compex,t,TRUE,FALSE)) {
-#ifdef DEBUGGING
-           deb("/%s/: %s\n", t, d);
-#endif
-           return FALSE;
-       }
-       if (spat->spat_compex.complen <= 1 && curspat)
-           spat = curspat;
-       if (execute(&spat->spat_compex, s, TRUE, 0)) {
-           if (spat->spat_compex.numsubs)
+       spat->spat_regexp = regcomp(t,spat->spat_flags & SPAT_FOLD,1);
+       if (!*spat->spat_regexp->precomp && lastspat)
+           spat = lastspat;
+       if (regexec(spat->spat_regexp, s, strend, TRUE, 0,
+         sarg[1]->str_pok & 4 ? sarg[1] : Nullstr)) {
+           if (spat->spat_regexp->subbase)
                curspat = spat;
-           return TRUE;
+           lastspat = spat;
+           goto gotcha;
        }
        else
-           return FALSE;
+           return &str_no;
     }
     else {
 #ifdef DEBUGGING
        if (debug & 8) {
            char ch;
 
-           if (spat->spat_flags & SPAT_USE_ONCE)
+           if (spat->spat_flags & SPAT_ONCE)
                ch = '?';
            else
                ch = '/';
-           deb("2.SPAT %c%s%c\n",ch,spat->spat_compex.precomp,ch);
+           deb("2.SPAT %c%s%c\n",ch,spat->spat_regexp->precomp,ch);
        }
 #endif
-       if (spat->spat_compex.complen <= 1 && curspat)
-           spat = curspat;
-       if (spat->spat_first) {
+       if (!*spat->spat_regexp->precomp && lastspat)
+           spat = lastspat;
+       t = s;
+       if (hint) {
+           if (hint < s || hint > strend)
+               fatal("panic: hint in do_match");
+           s = hint;
+           hint = Nullch;
+           if (spat->spat_regexp->regback >= 0) {
+               s -= spat->spat_regexp->regback;
+               if (s < t)
+                   s = t;
+           }
+           else
+               s = t;
+       }
+       else if (spat->spat_short) {
            if (spat->spat_flags & SPAT_SCANFIRST) {
-               str_free(spat->spat_first);
-               spat->spat_first = Nullstr;     /* disable optimization */
+               if (sarg[1]->str_pok == 5) {
+                   if (screamfirst[spat->spat_short->str_rare] < 0)
+                       goto nope;
+                   else if (!(s = screaminstr(sarg[1],spat->spat_short)))
+                       goto nope;
+                   else if (spat->spat_flags & SPAT_ALL)
+                       goto yup;
+               }
+               else if (!(s = fbminstr(s, strend, spat->spat_short)))
+                   goto nope;
+               else if (spat->spat_flags & SPAT_ALL)
+                   goto yup;
+               else if (spat->spat_regexp->regback >= 0) {
+                   ++*(long*)&spat->spat_short->str_nval;
+                   s -= spat->spat_regexp->regback;
+                   if (s < t)
+                       s = t;
+               }
+               else
+                   s = t;
+           }
+           else if (!multiline && (*spat->spat_short->str_ptr != *s ||
+             strnNE(spat->spat_short->str_ptr, s, spat->spat_slen) ))
+               goto nope;
+           if (--*(long*)&spat->spat_short->str_nval < 0) {
+               str_free(spat->spat_short);
+               spat->spat_short = Nullstr;     /* opt is being useless */
            }
-           else if (*spat->spat_first->str_ptr != *s ||
-             strnNE(spat->spat_first->str_ptr, s, spat->spat_flen) )
-               return FALSE;
        }
-       if (execute(&spat->spat_compex, s, TRUE, 0)) {
-           if (spat->spat_compex.numsubs)
+       if (regexec(spat->spat_regexp, s, strend, s == t, 0,
+         sarg[1]->str_pok & 4 ? sarg[1] : Nullstr)) {
+           if (spat->spat_regexp->subbase)
                curspat = spat;
-           if (spat->spat_flags & SPAT_USE_ONCE)
+           lastspat = spat;
+           if (spat->spat_flags & SPAT_ONCE)
                spat->spat_flags |= SPAT_USED;
-           return TRUE;
+           goto gotcha;
        }
        else
-           return FALSE;
+           return &str_no;
     }
     /*NOTREACHED*/
+
+  gotcha:
+    if (retary && curspat == spat) {
+       int iters, i, len;
+
+       iters = spat->spat_regexp->nparens;
+       *ptrmaxsarg = iters + sargoff;
+       sarg = (STR**)saferealloc((char*)(sarg - sargoff),
+         (iters+2+cushion+sargoff)*sizeof(STR*)) + sargoff;
+
+       for (i = 1; i <= iters; i++) {
+           sarg[i] = str_static(&str_no);
+           if (s = spat->spat_regexp->startp[i]) {
+               len = spat->spat_regexp->endp[i] - s;
+               if (len > 0)
+                   str_nset(sarg[i],s,len);
+           }
+       }
+       *retary = sarg;
+    }
+    return &str_yes;
+
+yup:
+    ++*(long*)&spat->spat_short->str_nval;
+    return &str_yes;
+
+nope:
+    ++*(long*)&spat->spat_short->str_nval;
+    return &str_no;
 }
 
 int
@@ -116,63 +172,96 @@ register ARG *arg;
 {
     register SPAT *spat;
     register STR *dstr;
-    register char *s;
+    register char *s = str_get(str);
+    char *strend = s + str->str_cur;
     register char *m;
 
     spat = arg[2].arg_ptr.arg_spat;
-    s = str_get(str);
     if (!spat || !s)
-       fatal("panic: do_subst\n");
+       fatal("panic: do_subst");
     else if (spat->spat_runtime) {
-       char *d;
-
-       m = str_get(eval(spat->spat_runtime,Null(STR***)));
-       if (d = compile(&spat->spat_compex,m,TRUE,FALSE)) {
-#ifdef DEBUGGING
-           deb("/%s/: %s\n", m, d);
-#endif
-           return 0;
-       }
+       m = str_get(eval(spat->spat_runtime,Null(STR***),-1));
+       spat->spat_regexp = regcomp(m,spat->spat_flags & SPAT_FOLD,1);
     }
 #ifdef DEBUGGING
     if (debug & 8) {
-       deb("2.SPAT /%s/\n",spat->spat_compex.precomp);
+       deb("2.SPAT /%s/\n",spat->spat_regexp->precomp);
     }
 #endif
-    if (spat->spat_compex.complen <= 1 && curspat)
-       spat = curspat;
-    if (spat->spat_first) {
+    if (!*spat->spat_regexp->precomp && lastspat)
+       spat = lastspat;
+    m = s;
+    if (hint) {
+       if (hint < s || hint > strend)
+           fatal("panic: hint in do_match");
+       s = hint;
+       hint = Nullch;
+       if (spat->spat_regexp->regback >= 0) {
+           s -= spat->spat_regexp->regback;
+           if (s < m)
+               s = m;
+       }
+       else
+           s = m;
+    }
+    else if (spat->spat_short) {
        if (spat->spat_flags & SPAT_SCANFIRST) {
-           str_free(spat->spat_first);
-           spat->spat_first = Nullstr; /* disable optimization */
+           if (str->str_pok == 5) {
+               if (screamfirst[spat->spat_short->str_rare] < 0)
+                   goto nope;
+               else if (!(s = screaminstr(str,spat->spat_short)))
+                   goto nope;
+           }
+           else if (!(s = fbminstr(s, strend, spat->spat_short)))
+               goto nope;
+           else if (spat->spat_regexp->regback >= 0) {
+               ++*(long*)&spat->spat_short->str_nval;
+               s -= spat->spat_regexp->regback;
+               if (s < m)
+                   s = m;
+           }
+           else
+               s = m;
+       }
+       else if (!multiline && (*spat->spat_short->str_ptr != *s ||
+         strnNE(spat->spat_short->str_ptr, s, spat->spat_slen) ))
+           goto nope;
+       if (--*(long*)&spat->spat_short->str_nval < 0) {
+           str_free(spat->spat_short);
+           spat->spat_short = Nullstr; /* opt is being useless */
        }
-       else if (*spat->spat_first->str_ptr != *s ||
-         strnNE(spat->spat_first->str_ptr, s, spat->spat_flen) )
-           return 0;
     }
-    if (m = execute(&spat->spat_compex, s, TRUE, 1)) {
+    if (regexec(spat->spat_regexp, s, strend, s == m, 1,
+      str->str_pok & 4 ? str : Nullstr)) {
        int iters = 0;
 
        dstr = str_new(str_len(str));
-       if (spat->spat_compex.numsubs)
+       str_nset(dstr,m,s-m);
+       if (spat->spat_regexp->subbase)
            curspat = spat;
+       lastspat = spat;
        do {
+           m = spat->spat_regexp->startp[0];
            if (iters++ > 10000)
-               fatal("Substitution loop?\n");
-           if (spat->spat_compex.numsubs)
-               s = spat->spat_compex.subbase;
+               fatal("Substitution loop");
+           if (spat->spat_regexp->subbase)
+               s = spat->spat_regexp->subbase;
            str_ncat(dstr,s,m-s);
-           s = spat->spat_compex.subend[0];
-           str_scat(dstr,eval(spat->spat_repl,Null(STR***)));
-           if (spat->spat_flags & SPAT_USE_ONCE)
+           s = spat->spat_regexp->endp[0];
+           str_scat(dstr,eval(spat->spat_repl,Null(STR***),-1));
+           if (spat->spat_flags & SPAT_ONCE)
                break;
-       } while (m = execute(&spat->spat_compex, s, FALSE, 1));
+       } while (regexec(spat->spat_regexp, s, strend, FALSE, 1, Nullstr));
        str_cat(dstr,s);
        str_replace(str,dstr);
        STABSET(str);
        return iters;
     }
     return 0;
+
+nope:
+    ++*(long*)&spat->spat_short->str_nval;
+    return 0;
 }
 
 int
@@ -188,7 +277,7 @@ register ARG *arg;
     tbl = arg[2].arg_ptr.arg_cval;
     s = str_get(str);
     if (!tbl || !s)
-       fatal("panic: do_trans\n");
+       fatal("panic: do_trans");
 #ifdef DEBUGGING
     if (debug & 8) {
        deb("2.TBL\n");
@@ -206,28 +295,29 @@ register ARG *arg;
 }
 
 int
-do_split(s,spat,retary)
-register char *s;
+do_split(spat,retary,sarg,ptrmaxsarg,sargoff,cushion)
 register SPAT *spat;
 STR ***retary;
+register STR **sarg;
+int *ptrmaxsarg;
+int sargoff;
+int cushion;
 {
+    register char *s = str_get(sarg[1]);
+    char *strend = s + sarg[1]->str_cur;
     register STR *dstr;
     register char *m;
     register ARRAY *ary;
     static ARRAY *myarray = Null(ARRAY*);
     int iters = 0;
-    STR **sarg;
-    register char *e;
     int i;
 
     if (!spat || !s)
-       fatal("panic: do_split\n");
+       fatal("panic: do_split");
     else if (spat->spat_runtime) {
-       char *d;
-
-       m = str_get(eval(spat->spat_runtime,Null(STR***)));
+       m = str_get(eval(spat->spat_runtime,Null(STR***),-1));
        if (!*m || (*m == ' ' && !m[1])) {
-           m = "[ \\t\\n]+";
+           m = "\\s+";
            spat->spat_flags |= SPAT_SKIPWHITE;
        }
        if (spat->spat_runtime->arg_type == O_ITEM &&
@@ -235,16 +325,11 @@ STR ***retary;
            arg_free(spat->spat_runtime);       /* it won't change, so */
            spat->spat_runtime = Nullarg;       /* no point compiling again */
        }
-       if (d = compile(&spat->spat_compex,m,TRUE,FALSE)) {
-#ifdef DEBUGGING
-           deb("/%s/: %s\n", m, d);
-#endif
-           return FALSE;
-       }
+       spat->spat_regexp = regcomp(m,spat->spat_flags & SPAT_FOLD,1);
     }
 #ifdef DEBUGGING
     if (debug & 8) {
-       deb("2.SPAT /%s/\n",spat->spat_compex.precomp);
+       deb("2.SPAT /%s/\n",spat->spat_regexp->precomp);
     }
 #endif
     if (retary)
@@ -252,21 +337,36 @@ STR ***retary;
     else
        ary = spat->spat_repl[1].arg_ptr.arg_stab->stab_array;
     if (!ary)
-       myarray = ary = anew();
+       myarray = ary = anew(Nullstab);
     ary->ary_fill = -1;
     if (spat->spat_flags & SPAT_SKIPWHITE) {
        while (isspace(*s))
            s++;
     }
-    while (*s && (m = execute(&spat->spat_compex, s, (iters == 0), 1))) {
-       if (spat->spat_compex.numsubs)
-           s = spat->spat_compex.subbase;
-       dstr = str_new(m-s);
-       str_nset(dstr,s,m-s);
-       astore(ary, iters++, dstr);
-       if (iters > 10000)
-           fatal("Substitution loop?\n");
-       s = spat->spat_compex.subend[0];
+    if (spat->spat_short) {
+       i = spat->spat_short->str_cur;
+       while (*s && (m = fbminstr(s, strend, spat->spat_short))) {
+           dstr = str_new(m-s);
+           str_nset(dstr,s,m-s);
+           astore(ary, iters++, dstr);
+           if (iters > 10000)
+               fatal("Substitution loop");
+           s = m + i;
+       }
+    }
+    else {
+       while (*s && regexec(spat->spat_regexp, s, strend, (iters == 0), 1,
+         Nullstr)) {
+           m = spat->spat_regexp->startp[0];
+           if (spat->spat_regexp->subbase)
+               s = spat->spat_regexp->subbase;
+           dstr = str_new(m-s);
+           str_nset(dstr,s,m-s);
+           astore(ary, iters++, dstr);
+           if (iters > 10000)
+               fatal("Substitution loop");
+           s = spat->spat_regexp->endp[0];
+       }
     }
     if (*s) {                  /* ignore field after final "whitespace" */
        dstr = str_new(0);      /*   if they interpolate, it's null anyway */
@@ -278,10 +378,10 @@ STR ***retary;
            iters--;
     }
     if (retary) {
-       sarg = (STR**)safemalloc((iters+2)*sizeof(STR*));
+       *ptrmaxsarg = iters + sargoff;
+       sarg = (STR**)saferealloc((char*)(sarg - sargoff),
+         (iters+2+cushion+sargoff)*sizeof(STR*)) + sargoff;
 
-       sarg[0] = Nullstr;
-       sarg[iters+1] = Nullstr;
        for (i = 1; i <= iters; i++)
            sarg[i] = afetch(ary,i-1);
        *retary = sarg;
@@ -297,12 +397,14 @@ register STR *str;
 {
     STR **tmpary;      /* must not be register */
     register STR **elem;
+    register int items;
 
-    (void)eval(arg[2].arg_ptr.arg_arg,&tmpary);
+    (void)eval(arg[2].arg_ptr.arg_arg,&tmpary,-1);
+    items = (int)str_gnum(*tmpary);
     elem = tmpary+1;
-    if (*elem)
-    str_sset(str,*elem++);
-    for (; *elem; elem++) {
+    if (items-- > 0)
+       str_sset(str,*elem++);
+    for (; items > 0; items--,elem++) {
        str_cat(str,delim);
        str_scat(str,*elem);
     }
@@ -310,6 +412,49 @@ register STR *str;
     safefree((char*)tmpary);
 }
 
+FILE *
+forkopen(name,mode)
+char *name;
+char *mode;
+{
+    int pfd[2];
+
+    if (pipe(pfd) < 0)
+       return Nullfp;
+    while ((forkprocess = fork()) == -1) {
+       if (errno != EAGAIN)
+           return Nullfp;
+       sleep(5);
+    }
+    if (*mode == 'w') {
+       if (forkprocess) {
+           close(pfd[0]);
+           return fdopen(pfd[1],"w");
+       }
+       else {
+           close(pfd[1]);
+           close(0);
+           dup(pfd[0]);        /* substitute our pipe for stdin */
+           close(pfd[0]);
+           return Nullfp;
+       }
+    }
+    else {
+       if (forkprocess) {
+           close(pfd[1]);
+           return fdopen(pfd[0],"r");
+       }
+       else {
+           close(pfd[0]);
+           close(1);
+           if (dup(pfd[1]) == 0)
+               dup(pfd[1]);    /* substitute our pipe for stdout */
+           close(pfd[1]);
+           return Nullfp;
+       }
+    }
+}
+
 bool
 do_open(stab,name)
 STAB *stab;
@@ -318,27 +463,61 @@ register char *name;
     FILE *fp;
     int len = strlen(name);
     register STIO *stio = stab->stab_io;
+    char *myname = savestr(name);
+    int result;
+    int fd;
 
+    name = myname;
+    forkprocess = 1;           /* assume true if no fork */
     while (len && isspace(name[len-1]))
        name[--len] = '\0';
     if (!stio)
        stio = stab->stab_io = stio_new();
     if (stio->fp) {
+       fd = fileno(stio->fp);
        if (stio->type == '|')
-           pclose(stio->fp);
+           result = pclose(stio->fp);
        else if (stio->type != '-')
-           fclose(stio->fp);
+           result = fclose(stio->fp);
+       else
+           result = 0;
+       if (result == EOF && fd > 2)
+           fprintf(stderr,"Warning: unable to close filehandle %s properly.\n",
+             stab->stab_name);
        stio->fp = Nullfp;
     }
     stio->type = *name;
     if (*name == '|') {
        for (name++; isspace(*name); name++) ;
-       fp = popen(name,"w");
+       if (strNE(name,"-"))
+           fp = popen(name,"w");
+       else {
+           fp = forkopen(name,"w");
+           stio->subprocess = forkprocess;
+           stio->type = '%';
+       }
     }
     else if (*name == '>' && name[1] == '>') {
+       stio->type = 'a';
        for (name += 2; isspace(*name); name++) ;
        fp = fopen(name,"a");
     }
+    else if (*name == '>' && name[1] == '&') {
+       for (name += 2; isspace(*name); name++) ;
+       if (isdigit(*name))
+           fd = atoi(name);
+       else {
+           stab = stabent(name,FALSE);
+           if (stab->stab_io && stab->stab_io->fp) {
+               fd = fileno(stab->stab_io->fp);
+               stio->type = stab->stab_io->type;
+           }
+           else
+               fd = -1;
+       }
+       fp = fdopen(dup(fd),stio->type == 'a' ? "a" :
+         (stio->type == '<' ? "r" : "w") );
+    }
     else if (*name == '>') {
        for (name++; isspace(*name); name++) ;
        if (strEQ(name,"-")) {
@@ -363,8 +542,15 @@ register char *name;
            while (len && isspace(name[len-1]))
                name[--len] = '\0';
            for (; isspace(*name); name++) ;
-           fp = popen(name,"r");
-           stio->type = '|';
+           if (strNE(name,"-")) {
+               fp = popen(name,"r");
+               stio->type = '|';
+           }
+           else {
+               fp = forkopen(name,"r");
+               stio->subprocess = forkprocess;
+               stio->type = '%';
+           }
        }
        else {
            stio->type = '<';
@@ -377,9 +563,11 @@ register char *name;
                fp = fopen(name,"r");
        }
     }
+    safefree(myname);
     if (!fp)
        return FALSE;
-    if (stio->type != '|' && stio->type != '-') {
+    if (stio->type &&
+      stio->type != '|' && stio->type != '-' && stio->type != '%') {
        if (fstat(fileno(fp),&statbuf) < 0) {
            fclose(fp);
            return FALSE;
@@ -400,14 +588,18 @@ register STAB *stab;
 {
     register STR *str;
     char *oldname;
+    int filemode,fileuid,filegid;
 
-    while (alen(stab->stab_array) >= 0L) {
+    while (alen(stab->stab_array) >= 0) {
        str = ashift(stab->stab_array);
        str_sset(stab->stab_val,str);
        STABSET(stab->stab_val);
        oldname = str_get(stab->stab_val);
        if (do_open(stab,oldname)) {
            if (inplace) {
+               filemode = statbuf.st_mode;
+               fileuid = statbuf.st_uid;
+               filegid = statbuf.st_gid;
                if (*inplace) {
                    str_cat(str,inplace);
 #ifdef RENAME
@@ -418,9 +610,23 @@ register STAB *stab;
                    UNLINK(oldname);
 #endif
                }
+               else {
+                   UNLINK(oldname);
+               }
                sprintf(tokenbuf,">%s",oldname);
+               errno = 0;              /* in case sprintf set errno */
                do_open(argvoutstab,tokenbuf);
                defoutstab = argvoutstab;
+#ifdef FCHMOD
+               fchmod(fileno(argvoutstab->stab_io->fp),filemode);
+#else
+               chmod(oldname,filemode);
+#endif
+#ifdef FCHOWN
+               fchown(fileno(argvoutstab->stab_io->fp),fileuid,filegid);
+#else
+               chown(oldname,fileuid,filegid);
+#endif
            }
            str_free(str);
            return stab->stab_io->fp;
@@ -443,16 +649,30 @@ bool explicit;
 {
     bool retval = FALSE;
     register STIO *stio = stab->stab_io;
+    int status;
+    int tmp;
 
-    if (!stio)         /* never opened */
+    if (!stio) {               /* never opened */
+       if (dowarn && explicit)
+           warn("Close on unopened file <%s>",stab->stab_name);
        return FALSE;
+    }
     if (stio->fp) {
        if (stio->type == '|')
            retval = (pclose(stio->fp) >= 0);
        else if (stio->type == '-')
            retval = TRUE;
-       else
+       else {
            retval = (fclose(stio->fp) != EOF);
+           if (stio->type == '%' && stio->subprocess) {
+               while ((tmp = wait(&status)) != stio->subprocess && tmp != -1)
+                   ;
+               if (tmp == -1)
+                   statusvalue = -1;
+               else
+                   statusvalue = (unsigned)status & 0xffff;
+           }
+       }
        stio->fp = Nullfp;
     }
     if (explicit)
@@ -468,10 +688,11 @@ STAB *stab;
     register STIO *stio;
     int ch;
 
-    if (!stab)
-       return TRUE;
+    if (!stab)                 /* eof() */
+       stio = argvstab->stab_io;
+    else
+       stio = stab->stab_io;
 
-    stio = stab->stab_io;
     if (!stio)
        return TRUE;
 
@@ -487,8 +708,8 @@ STAB *stab;
            ungetc(ch, stio->fp);
            return FALSE;
        }
-       if (stio->flags & IOF_ARGV) {   /* not necessarily a real EOF yet? */
-           if (!nextargv(stab))        /* get another fp handy */
+       if (!stab) {                    /* not necessarily a real EOF yet? */
+           if (!nextargv(argvstab))    /* get another fp handy */
                return TRUE;
        }
        else
@@ -502,16 +723,20 @@ do_tell(stab)
 STAB *stab;
 {
     register STIO *stio;
-    int ch;
 
     if (!stab)
-       return -1L;
+       goto phooey;
 
     stio = stab->stab_io;
     if (!stio || !stio->fp)
-       return -1L;
+       goto phooey;
 
     return ftell(stio->fp);
+
+phooey:
+    if (dowarn)
+       warn("tell() on unopened file");
+    return -1L;
 }
 
 bool
@@ -523,19 +748,113 @@ int whence;
     register STIO *stio;
 
     if (!stab)
-       return FALSE;
+       goto nuts;
 
     stio = stab->stab_io;
     if (!stio || !stio->fp)
-       return FALSE;
+       goto nuts;
 
     return fseek(stio->fp, pos, whence) >= 0;
+
+nuts:
+    if (dowarn)
+       warn("seek() on unopened file");
+    return FALSE;
 }
 
-do_stat(arg,sarg,retary)
+static CMD *sortcmd;
+static STAB *firststab = Nullstab;
+static STAB *secondstab = Nullstab;
+
+do_sort(arg,stab,retary,sarg,ptrmaxsarg,sargoff,cushion)
 register ARG *arg;
+STAB *stab;
+STR ***retary;
 register STR **sarg;
+int *ptrmaxsarg;
+int sargoff;
+int cushion;
+{
+    STR **tmpary;      /* must not be register */
+    register STR **elem;
+    register bool retval;
+    register int max;
+    register int i;
+    int sortcmp();
+    int sortsub();
+    STR *oldfirst;
+    STR *oldsecond;
+
+    (void)eval(arg[1].arg_ptr.arg_arg,&tmpary,-1);
+    max = (int)str_gnum(*tmpary);
+
+    if (retary) {
+       sarg = (STR**)saferealloc((char*)(sarg - sargoff),
+         (max+2+cushion+sargoff)*sizeof(STR*)) + sargoff;
+       for (i = 1; i <= max; i++)
+           sarg[i] = tmpary[i];
+       *retary = sarg;
+       if (max > 1) {
+           if (stab->stab_sub && (sortcmd = stab->stab_sub->cmd)) {
+               if (!firststab) {
+                   firststab = stabent("a",TRUE);
+                   secondstab = stabent("b",TRUE);
+               }
+               oldfirst = firststab->stab_val;
+               oldsecond = secondstab->stab_val;
+               qsort((char*)(sarg+1),max,sizeof(STR*),sortsub);
+               firststab->stab_val = oldfirst;
+               secondstab->stab_val = oldsecond;
+           }
+           else
+               qsort((char*)(sarg+1),max,sizeof(STR*),sortcmp);
+       }
+       while (max > 0 && !sarg[max])
+           max--;
+       *ptrmaxsarg = max + sargoff;
+    }
+    safefree((char*)tmpary);
+    return max;
+}
+
+int
+sortcmp(str1,str2)
+STR **str1;
+STR **str2;
+{
+    char *tmps;
+
+    if (!*str1)
+       return -1;
+    if (!*str2)
+       return 1;
+    tmps = str_get(*str1);
+    return strcmp(tmps,str_get(*str2));
+}
+
+int
+sortsub(str1,str2)
+STR **str1;
+STR **str2;
+{
+    STR *str;
+
+    if (!*str1)
+       return -1;
+    if (!*str2)
+       return 1;
+    firststab->stab_val = *str1;
+    secondstab->stab_val = *str2;
+    return (int)str_gnum(cmd_exec(sortcmd));
+}
+
+do_stat(arg,retary,sarg,ptrmaxsarg,sargoff,cushion)
+register ARG *arg;
 STR ***retary;
+register STR **sarg;
+int *ptrmaxsarg;
+int sargoff;
+int cushion;
 {
     register ARRAY *ary;
     static ARRAY *myarray = Null(ARRAY*);
@@ -544,7 +863,7 @@ STR ***retary;
 
     ary = myarray;
     if (!ary)
-       myarray = ary = anew();
+       myarray = ary = anew(Nullstab);
     ary->ary_fill = -1;
     if (arg[1].arg_type == A_LVAL) {
        tmpstab = arg[1].arg_ptr.arg_stab;
@@ -578,9 +897,9 @@ STR ***retary;
            apush(ary,str_make(""));
 #endif
        }
-       sarg = (STR**)safemalloc((max+2)*sizeof(STR*));
-       sarg[0] = Nullstr;
-       sarg[max+1] = Nullstr;
+       *ptrmaxsarg = max + sargoff;
+       sarg = (STR**)saferealloc((char*)(sarg - sargoff),
+         (max+2+cushion+sargoff)*sizeof(STR*)) + sargoff;
        for (i = 1; i <= max; i++)
            sarg[i] = afetch(ary,i-1);
        *retary = sarg;
@@ -588,32 +907,38 @@ STR ***retary;
     return max;
 }
 
-do_tms(retary)
+do_tms(retary,sarg,ptrmaxsarg,sargoff,cushion)
 STR ***retary;
+STR **sarg;
+int *ptrmaxsarg;
+int sargoff;
+int cushion;
 {
     register ARRAY *ary;
     static ARRAY *myarray = Null(ARRAY*);
-    register STR **sarg;
     int max = 4;
     register int i;
 
     ary = myarray;
     if (!ary)
-       myarray = ary = anew();
+       myarray = ary = anew(Nullstab);
     ary->ary_fill = -1;
-    if (times(&timesbuf) < 0)
-       max = 0;
+    times(&timesbuf);
+
+#ifndef HZ
+#define HZ 60
+#endif
 
     if (retary) {
        if (max) {
-           apush(ary,str_nmake(((double)timesbuf.tms_utime)/60.0));
-           apush(ary,str_nmake(((double)timesbuf.tms_stime)/60.0));
-           apush(ary,str_nmake(((double)timesbuf.tms_cutime)/60.0));
-           apush(ary,str_nmake(((double)timesbuf.tms_cstime)/60.0));
-       }
-       sarg = (STR**)safemalloc((max+2)*sizeof(STR*));
-       sarg[0] = Nullstr;
-       sarg[max+1] = Nullstr;
+           apush(ary,str_nmake(((double)timesbuf.tms_utime)/HZ));
+           apush(ary,str_nmake(((double)timesbuf.tms_stime)/HZ));
+           apush(ary,str_nmake(((double)timesbuf.tms_cutime)/HZ));
+           apush(ary,str_nmake(((double)timesbuf.tms_cstime)/HZ));
+       }
+       *ptrmaxsarg = max + sargoff;
+       sarg = (STR**)saferealloc((char*)(sarg - sargoff),
+         (max+2+cushion+sargoff)*sizeof(STR*)) + sargoff;
        for (i = 1; i <= max; i++)
            sarg[i] = afetch(ary,i-1);
        *retary = sarg;
@@ -621,20 +946,22 @@ STR ***retary;
     return max;
 }
 
-do_time(tmbuf,retary)
+do_time(tmbuf,retary,sarg,ptrmaxsarg,sargoff,cushion)
 struct tm *tmbuf;
 STR ***retary;
+STR **sarg;
+int *ptrmaxsarg;
+int sargoff;
+int cushion;
 {
     register ARRAY *ary;
     static ARRAY *myarray = Null(ARRAY*);
-    register STR **sarg;
     int max = 9;
     register int i;
-    STR *str;
 
     ary = myarray;
     if (!ary)
-       myarray = ary = anew();
+       myarray = ary = anew(Nullstab);
     ary->ary_fill = -1;
     if (!tmbuf)
        max = 0;
@@ -651,9 +978,9 @@ STR ***retary;
            apush(ary,str_nmake((double)tmbuf->tm_yday));
            apush(ary,str_nmake((double)tmbuf->tm_isdst));
        }
-       sarg = (STR**)safemalloc((max+2)*sizeof(STR*));
-       sarg[0] = Nullstr;
-       sarg[max+1] = Nullstr;
+       *ptrmaxsarg = max + sargoff;
+       sarg = (STR**)saferealloc((char*)(sarg - sargoff),
+         (max+2+cushion+sargoff)*sizeof(STR*)) + sargoff;
        for (i = 1; i <= max; i++)
            sarg[i] = afetch(ary,i-1);
        *retary = sarg;
@@ -688,6 +1015,7 @@ register STR **sarg;
        for (t++; *sarg && *t && t != s; t++) {
            switch (*t) {
            case '\0':
+               t--;
                break;
            case '%':
                ch = *(++t);
@@ -702,7 +1030,7 @@ register STR **sarg;
            case 'D': case 'X': case 'O':
                dolong = TRUE;
                /* FALL THROUGH */
-           case 'd': case 'x': case 'o': case 'c':
+           case 'd': case 'x': case 'o': case 'c': case 'u':
                ch = *(++t);
                *t = '\0';
                if (dolong)
@@ -722,7 +1050,12 @@ register STR **sarg;
            case 's':
                ch = *(++t);
                *t = '\0';
-               sprintf(buf,s,str_get(*(sarg++)));
+               if (strEQ(s,"%s")) {    /* some printfs fail on >128 chars */
+                   *buf = '\0';
+                   str_scat(str,*(sarg++));  /* so handle simple case */
+               }
+               else
+                   sprintf(buf,s,str_get(*(sarg++)));
                s = t;
                *(t--) = ch;
                break;
@@ -736,13 +1069,22 @@ register STR **sarg;
 }
 
 bool
-do_print(s,fp)
-char *s;
+do_print(str,fp)
+register STR *str;
 FILE *fp;
 {
-    if (!fp || !s)
+    if (!fp) {
+       if (dowarn)
+           warn("print to unopened file");
+       return FALSE;
+    }
+    if (!str)
        return FALSE;
-    fputs(s,fp);
+    if (ofmt &&
+      ((str->str_nok && str->str_nval != 0.0) || str_gnum(str) != 0.0) )
+       fprintf(fp, ofmt, str->str_nval);
+    else
+       fputs(str_get(str),fp);
     return TRUE;
 }
 
@@ -754,30 +1096,30 @@ register FILE *fp;
     STR **tmpary;      /* must not be register */
     register STR **elem;
     register bool retval;
-    double value;
+    register int items;
 
-    (void)eval(arg[1].arg_ptr.arg_arg,&tmpary);
+    if (!fp) {
+       if (dowarn)
+           warn("print to unopened file");
+       return FALSE;
+    }
+    (void)eval(arg[1].arg_ptr.arg_arg,&tmpary,-1);
+    items = (int)str_gnum(*tmpary);
     if (arg->arg_type == O_PRTF) {
-       do_sprintf(arg->arg_ptr.arg_str,32767,tmpary);
-       retval = do_print(str_get(arg->arg_ptr.arg_str),fp);
+       do_sprintf(arg->arg_ptr.arg_str,items,tmpary);
+       retval = do_print(arg->arg_ptr.arg_str,fp);
     }
     else {
        retval = FALSE;
-       for (elem = tmpary+1; *elem; elem++) {
+       for (elem = tmpary+1; items > 0; items--,elem++) {
            if (retval && ofs)
-               do_print(ofs, fp);
-           if (ofmt && fp) {
-               if ((*elem)->str_nok || str_gnum(*elem) != 0.0)
-                   fprintf(fp, ofmt, str_gnum(*elem));
-               retval = TRUE;
-           }
-           else
-               retval = do_print(str_get(*elem), fp);
+               fputs(ofs, fp);
+           retval = do_print(*elem, fp);
            if (!retval)
                break;
        }
        if (ors)
-           retval = do_print(ors, fp);
+           fputs(ors, fp);
     }
     safefree((char*)tmpary);
     return retval;
@@ -790,18 +1132,19 @@ register ARG *arg;
     STR **tmpary;      /* must not be register */
     register STR **elem;
     register char **a;
-    register int i;
+    register int items;
     char **argv;
 
-    (void)eval(arg[1].arg_ptr.arg_arg,&tmpary);
-    i = 0;
-    for (elem = tmpary+1; *elem; elem++)
-       i++;
-    if (i) {
-       argv = (char**)safemalloc((i+1)*sizeof(char*));
+    (void)eval(arg[1].arg_ptr.arg_arg,&tmpary,-1);
+    items = (int)str_gnum(*tmpary);
+    if (items) {
+       argv = (char**)safemalloc((items+1)*sizeof(char*));
        a = argv;
-       for (elem = tmpary+1; *elem; elem++) {
-           *a++ = str_get(*elem);
+       for (elem = tmpary+1; items > 0; items--,elem++) {
+           if (*elem)
+               *a++ = str_get(*elem);
+           else
+               *a++ = "";
        }
        *a = Nullch;
        execvp(argv[0],argv);
@@ -812,19 +1155,19 @@ register ARG *arg;
 }
 
 bool
-do_exec(cmd)
-char *cmd;
+do_exec(str)
+STR *str;
 {
-    STR **tmpary;      /* must not be register */
     register char **a;
     register char *s;
     char **argv;
+    char *cmd = str_get(str);
 
     /* see if there are shell metacharacters in it */
 
     for (s = cmd; *s; s++) {
        if (*s != ' ' && !isalpha(*s) && index("$&*(){}[]'\";\\|?<>~`",*s)) {
-           execl("/bin/sh","sh","-c",cmd,0);
+           execl("/bin/sh","sh","-c",cmd,(char*)0);
            return FALSE;
        }
     }
@@ -854,11 +1197,14 @@ register ARRAY *ary;
     STR **tmpary;      /* must not be register */
     register STR **elem;
     register STR *str = &str_no;
+    register int items;
 
-    (void)eval(arg[1].arg_ptr.arg_arg,&tmpary);
-    for (elem = tmpary+1; *elem; elem++) {
+    (void)eval(arg[1].arg_ptr.arg_arg,&tmpary,-1);
+    items = (int)str_gnum(*tmpary);
+    for (elem = tmpary+1; items > 0; items--,elem++) {
        str = str_new(0);
-       str_sset(str,*elem);
+       if (*elem)
+           str_sset(str,*elem);
        apush(ary,str);
     }
     safefree((char*)tmpary);
@@ -873,17 +1219,16 @@ register ARRAY *ary;
     register STR **elem;
     register STR *str = &str_no;
     register int i;
+    register int items;
 
-    (void)eval(arg[1].arg_ptr.arg_arg,&tmpary);
-    i = 0;
-    for (elem = tmpary+1; *elem; elem++)
-       i++;
-    aunshift(ary,i);
+    (void)eval(arg[1].arg_ptr.arg_arg,&tmpary,-1);
+    items = (int)str_gnum(*tmpary);
+    aunshift(ary,items);
     i = 0;
-    for (elem = tmpary+1; *elem; elem++) {
+    for (elem = tmpary+1; i < items; i++,elem++) {
        str = str_new(0);
        str_sset(str,*elem);
-       astore(ary,i++,str);
+       astore(ary,i,str);
     }
     safefree((char*)tmpary);
 }
@@ -895,69 +1240,133 @@ STR **sarg;
 {
     STR **tmpary;      /* must not be register */
     register STR **elem;
-    register int i;
+    register int items;
     register int val;
     register int val2;
+    char *s;
 
-    if (sarg)
+    if (sarg) {
        tmpary = sarg;
-    else
-       (void)eval(arg[1].arg_ptr.arg_arg,&tmpary);
-    i = 0;
-    for (elem = tmpary+1; *elem; elem++)
-       i++;
+       items = 0;
+       for (elem = tmpary+1; *elem; elem++)
+           items++;
+    }
+    else {
+       (void)eval(arg[1].arg_ptr.arg_arg,&tmpary,-1);
+       items = (int)str_gnum(*tmpary);
+    }
     switch (type) {
     case O_CHMOD:
-       if (--i > 0) {
+       if (--items > 0) {
            val = (int)str_gnum(tmpary[1]);
            for (elem = tmpary+2; *elem; elem++)
                if (chmod(str_get(*elem),val))
-                   i--;
+                   items--;
        }
        break;
     case O_CHOWN:
-       if (i > 2) {
-           i -= 2;
+       if (items > 2) {
+           items -= 2;
            val = (int)str_gnum(tmpary[1]);
            val2 = (int)str_gnum(tmpary[2]);
            for (elem = tmpary+3; *elem; elem++)
                if (chown(str_get(*elem),val,val2))
-                   i--;
+                   items--;
        }
        else
-           i = 0;
+           items = 0;
        break;
     case O_KILL:
-       if (--i > 0) {
+       if (--items > 0) {
            val = (int)str_gnum(tmpary[1]);
-           if (val < 0)
+           if (val < 0) {
                val = -val;
-           for (elem = tmpary+2; *elem; elem++)
-               if (kill(atoi(str_get(*elem)),val))
-                   i--;
+               for (elem = tmpary+2; *elem; elem++)
+#ifdef KILLPG
+                   if (killpg((int)(str_gnum(*elem)),val))     /* BSD */
+#else
+                   if (kill(-(int)(str_gnum(*elem)),val))      /* SYSV */
+#endif
+                       items--;
+           }
+           else {
+               for (elem = tmpary+2; *elem; elem++)
+                   if (kill((int)(str_gnum(*elem)),val))
+                       items--;
+           }
        }
        break;
     case O_UNLINK:
-       for (elem = tmpary+1; *elem; elem++)
-           if (UNLINK(str_get(*elem)))
-               i--;
+       for (elem = tmpary+1; *elem; elem++) {
+           s = str_get(*elem);
+           if (euid || unsafe) {
+               if (UNLINK(s))
+                   items--;
+           }
+           else {      /* don't let root wipe out directories without -U */
+               if (stat(s,&statbuf) < 0 ||
+                 (statbuf.st_mode & S_IFMT) == S_IFDIR )
+                   items--;
+               else {
+                   if (UNLINK(s))
+                       items--;
+               }
+           }
+       }
+       break;
+    case O_UTIME:
+       if (items > 2) {
+           struct {
+               long    atime,
+                       mtime;
+           } utbuf;
+
+           utbuf.atime = (long)str_gnum(tmpary[1]);    /* time accessed */
+           utbuf.mtime = (long)str_gnum(tmpary[2]);    /* time modified */
+           items -= 2;
+           for (elem = tmpary+3; *elem; elem++)
+               if (utime(str_get(*elem),&utbuf))
+                   items--;
+       }
+       else
+           items = 0;
        break;
     }
     if (!sarg)
        safefree((char*)tmpary);
-    return i;
+    return items;
 }
 
 STR *
 do_subr(arg,sarg)
 register ARG *arg;
-register char **sarg;
+register STR **sarg;
 {
+    register SUBR *sub;
     ARRAY *savearray;
     STR *str;
+    STAB *stab;
+    char *oldfile = filename;
+    int oldsave = savestack->ary_fill;
+    int oldtmps_base = tmps_base;
 
+    if (arg[2].arg_type == A_WORD)
+       stab = arg[2].arg_ptr.arg_stab;
+    else
+       stab = stabent(str_get(arg[2].arg_ptr.arg_stab->stab_val),TRUE);
+    if (!stab) {
+       if (dowarn)
+           warn("Undefined subroutine called");
+       return &str_no;
+    }
+    sub = stab->stab_sub;
+    if (!sub) {
+       if (dowarn)
+           warn("Undefined subroutine \"%s\" called", stab->stab_name);
+       return &str_no;
+    }
     savearray = defstab->stab_array;
-    defstab->stab_array = anew();
+    defstab->stab_array = anew(defstab);
     if (arg[1].arg_flags & AF_SPECIAL)
        (void)do_push(arg,defstab->stab_array);
     else if (arg[1].arg_type != A_NULL) {
@@ -965,16 +1374,34 @@ register char **sarg;
        str_sset(str,sarg[1]);
        apush(defstab->stab_array,str);
     }
-    str = cmd_exec(arg[2].arg_ptr.arg_stab->stab_sub);
+    sub->depth++;
+    if (sub->depth >= 2) {     /* save temporaries on recursion? */
+       if (sub->depth == 100 && dowarn)
+           warn("Deep recursion on subroutine \"%s\"",stab->stab_name);
+       savelist(sub->tosave->ary_array,sub->tosave->ary_fill);
+    }
+    filename = sub->filename;
+    tmps_base = tmps_max;
+
+    str = cmd_exec(sub->cmd);          /* so do it already */
+
+    sub->depth--;      /* assuming no longjumps out of here */
     afree(defstab->stab_array);  /* put back old $_[] */
     defstab->stab_array = savearray;
+    filename = oldfile;
+    tmps_base = oldtmps_base;
+    if (savestack->ary_fill > oldsave) {
+       str = str_static(str);  /* in case restore wipes old str */
+       restorelist(oldsave);
+    }
     return str;
 }
 
 void
-do_assign(retstr,arg)
+do_assign(retstr,arg,sarg)
 STR *retstr;
 register ARG *arg;
+register STR **sarg;
 {
     STR **tmpary;      /* must not be register */
     register ARG *larg = arg[1].arg_ptr.arg_arg;
@@ -982,60 +1409,76 @@ register ARG *arg;
     register STR *str;
     register ARRAY *ary;
     register int i;
-    register int lasti;
-    char *s;
+    register int items;
+    STR *tmpstr;
 
-    (void)eval(arg[2].arg_ptr.arg_arg,&tmpary);
+    if (arg[2].arg_flags & AF_SPECIAL) {
+       (void)eval(arg[2].arg_ptr.arg_arg,&tmpary,-1);
+       items = (int)str_gnum(*tmpary);
+    }
+    else {
+       tmpary = sarg;
+       sarg[1] = sarg[2];
+       sarg[2] = Nullstr;
+       items = 1;
+    }
 
-    if (arg->arg_flags & AF_COMMON) {
+    if (arg->arg_flags & AF_COMMON) {  /* always true currently, alas */
        if (*(tmpary+1)) {
-           for (elem=tmpary+2; *elem; elem++) {
+           for (i=2,elem=tmpary+2; i <= items; i++,elem++) {
                *elem = str_static(*elem);
            }
        }
     }
     if (larg->arg_type == O_LIST) {
-       lasti = larg->arg_len;
-       for (i=1,elem=tmpary+1; i <= lasti; i++) {
-           if (*elem)
-               s = str_get(*(elem++));
-           else
-               s = "";
+       for (i=1,elem=tmpary+1; i <= larg->arg_len; i++) {
            switch (larg[i].arg_type) {
            case A_STAB:
            case A_LVAL:
                str = STAB_STR(larg[i].arg_ptr.arg_stab);
                break;
            case A_LEXPR:
-               str = eval(larg[i].arg_ptr.arg_arg,Null(STR***));
+               str = eval(larg[i].arg_ptr.arg_arg,Null(STR***),-1);
                break;
            }
-           str_set(str,s);
+           if (larg->arg_flags & AF_LOCAL) {
+               apush(savestack,str);   /* save pointer */
+               tmpstr = str_new(0);
+               str_sset(tmpstr,str);
+               apush(savestack,tmpstr); /* save value */
+           }
+           if (*elem)
+               str_sset(str,*(elem++));
+           else
+               str_set(str,"");
            STABSET(str);
        }
-       i = elem - tmpary - 1;
     }
     else {                     /* should be an array name */
        ary = larg[1].arg_ptr.arg_stab->stab_array;
-       for (i=0,elem=tmpary+1; *elem; i++) {
+       for (i=0,elem=tmpary+1; i < items; i++) {
            str = str_new(0);
            if (*elem)
                str_sset(str,*(elem++));
            astore(ary,i,str);
        }
-       ary->ary_fill = i - 1;  /* they can get the extra ones back by */
-    }                          /*   setting an element larger than old fill */
-    str_numset(retstr,(double)i);
+       ary->ary_fill = items - 1;/* they can get the extra ones back by */
+    }                          /*   setting $#ary larger than old fill */
+    str_numset(retstr,(double)items);
     STABSET(retstr);
-    safefree((char*)tmpary);
+    if (tmpary != sarg);
+       safefree((char*)tmpary);
 }
 
 int
-do_kv(hash,kv,sarg,retary)
+do_kv(hash,kv,retary,sarg,ptrmaxsarg,sargoff,cushion)
 HASH *hash;
 int kv;
-register STR **sarg;
 STR ***retary;
+register STR **sarg;
+int *ptrmaxsarg;
+int sargoff;
+int cushion;
 {
     register ARRAY *ary;
     int max = 0;
@@ -1045,7 +1488,7 @@ STR ***retary;
 
     ary = myarray;
     if (!ary)
-       myarray = ary = anew();
+       myarray = ary = anew(Nullstab);
     ary->ary_fill = -1;
 
     hiterinit(hash);
@@ -1057,9 +1500,9 @@ STR ***retary;
            apush(ary,str_make(str_get(hiterval(entry))));
     }
     if (retary) { /* array wanted */
-       sarg = (STR**)saferealloc((char*)sarg,(max+2)*sizeof(STR*));
-       sarg[0] = Nullstr;
-       sarg[max+1] = Nullstr;
+       *ptrmaxsarg = max + sargoff;
+       sarg = (STR**)saferealloc((char*)(sarg - sargoff),
+         (max+2+cushion+sargoff)*sizeof(STR*)) + sargoff;
        for (i = 1; i <= max; i++)
            sarg[i] = afetch(ary,i-1);
        *retary = sarg;
@@ -1068,10 +1511,13 @@ STR ***retary;
 }
 
 STR *
-do_each(hash,sarg,retary)
+do_each(hash,retary,sarg,ptrmaxsarg,sargoff,cushion)
 HASH *hash;
-register STR **sarg;
 STR ***retary;
+STR **sarg;
+int *ptrmaxsarg;
+int sargoff;
+int cushion;
 {
     static STR *mystr = Nullstr;
     STR *retstr;
@@ -1084,17 +1530,18 @@ STR ***retary;
 
     if (retary) { /* array wanted */
        if (entry) {
-           sarg = (STR**)saferealloc((char*)sarg,4*sizeof(STR*));
-           sarg[0] = Nullstr;
-           sarg[3] = Nullstr;
+           *ptrmaxsarg = 2 + sargoff;
+           sarg = (STR**)saferealloc((char*)(sarg - sargoff),
+             (2+2+cushion+sargoff)*sizeof(STR*)) + sargoff;
            sarg[1] = mystr = str_make(hiterkey(entry));
            retstr = sarg[2] = hiterval(entry);
            *retary = sarg;
        }
        else {
-           sarg = (STR**)saferealloc((char*)sarg,2*sizeof(STR*));
-           sarg[0] = Nullstr;
-           sarg[1] = retstr = Nullstr;
+           *ptrmaxsarg = sargoff;
+           sarg = (STR**)saferealloc((char*)(sarg - sargoff),
+             (2+cushion+sargoff)*sizeof(STR*)) + sargoff;
+           retstr = Nullstr;
            *retary = sarg;
        }
     }
@@ -1104,32 +1551,172 @@ STR ***retary;
     return retstr;
 }
 
-init_eval()
+int
+mystat(arg,str)
+ARG *arg;
+STR *str;
 {
-    register int i;
+    STIO *stio;
 
-#define A(e1,e2,e3) (e1+(e2<<1)+(e3<<2))
-    opargs[O_ITEM] =           A(1,0,0);
-    opargs[O_ITEM2] =          A(0,0,0);
-    opargs[O_ITEM3] =          A(0,0,0);
-    opargs[O_CONCAT] =         A(1,1,0);
-    opargs[O_MATCH] =          A(1,0,0);
-    opargs[O_NMATCH] =         A(1,0,0);
-    opargs[O_SUBST] =          A(1,0,0);
-    opargs[O_NSUBST] =         A(1,0,0);
-    opargs[O_ASSIGN] =         A(1,1,0);
-    opargs[O_MULTIPLY] =       A(1,1,0);
-    opargs[O_DIVIDE] =         A(1,1,0);
-    opargs[O_MODULO] =         A(1,1,0);
-    opargs[O_ADD] =            A(1,1,0);
-    opargs[O_SUBTRACT] =       A(1,1,0);
-    opargs[O_LEFT_SHIFT] =     A(1,1,0);
-    opargs[O_RIGHT_SHIFT] =    A(1,1,0);
-    opargs[O_LT] =             A(1,1,0);
-    opargs[O_GT] =             A(1,1,0);
-    opargs[O_LE] =             A(1,1,0);
-    opargs[O_GE] =             A(1,1,0);
-    opargs[O_EQ] =             A(1,1,0);
+    if (arg[1].arg_flags & AF_SPECIAL) {
+       stio = arg[1].arg_ptr.arg_stab->stab_io;
+       if (stio && stio->fp)
+           return fstat(fileno(stio->fp), &statbuf);
+       else {
+           if (dowarn)
+               warn("Stat on unopened file <%s>",
+                 arg[1].arg_ptr.arg_stab->stab_name);
+           return -1;
+       }
+    }
+    else
+       return stat(str_get(str),&statbuf);
+}
+
+STR *
+do_fttext(arg,str)
+register ARG *arg;
+STR *str;
+{
+    int i;
+    int len;
+    int odd = 0;
+    STDCHAR tbuf[512];
+    register STDCHAR *s;
+    register STIO *stio;
+
+    if (arg[1].arg_flags & AF_SPECIAL) {
+       stio = arg[1].arg_ptr.arg_stab->stab_io;
+       if (stio && stio->fp) {
+#ifdef STDSTDIO
+           if (stio->fp->_cnt <= 0) {
+               i = getc(stio->fp);
+               ungetc(i,stio->fp);
+           }
+           if (stio->fp->_cnt <= 0)    /* null file is anything */
+               return &str_yes;
+           len = stio->fp->_cnt + (stio->fp->_ptr - stio->fp->_base);
+           s = stio->fp->_base;
+#else
+           fatal("-T and -B not implemented on filehandles\n");
+#endif
+       }
+       else {
+           if (dowarn)
+               warn("Test on unopened file <%s>",
+                 arg[1].arg_ptr.arg_stab->stab_name);
+           return &str_no;
+       }
+    }
+    else {
+       i = open(str_get(str),0);
+       if (i < 0)
+           return &str_no;
+       len = read(i,tbuf,512);
+       if (len <= 0)           /* null file is anything */
+           return &str_yes;
+       close(i);
+       s = tbuf;
+    }
+
+    /* now scan s to look for textiness */
+
+    for (i = 0; i < len; i++,s++) {
+       if (!*s) {                      /* null never allowed in text */
+           odd += len;
+           break;
+       }
+       else if (*s & 128)
+           odd++;
+       else if (*s < 32 &&
+         *s != '\n' && *s != '\r' && *s != '\b' &&
+         *s != '\t' && *s != '\f' && *s != 27)
+           odd++;
+    }
+
+    if ((odd * 10 > len) == (arg->arg_type == O_FTTEXT)) /* allow 10% odd */
+       return &str_no;
+    else
+       return &str_yes;
+}
+
+int
+do_study(str)
+STR *str;
+{
+    register char *s = str_get(str);
+    register int pos = str->str_cur;
+    register int ch;
+    register int *sfirst;
+    register int *snext;
+    static int maxscream = -1;
+    static STR *lastscream = Nullstr;
+
+    if (lastscream && lastscream->str_pok == 5)
+       lastscream->str_pok &= ~4;
+    lastscream = str;
+    if (pos <= 0)
+       return 0;
+    if (pos > maxscream) {
+       if (maxscream < 0) {
+           maxscream = pos + 80;
+           screamfirst = (int*)safemalloc((MEM_SIZE)(256 * sizeof(int)));
+           screamnext = (int*)safemalloc((MEM_SIZE)(maxscream * sizeof(int)));
+       }
+       else {
+           maxscream = pos + pos / 4;
+           screamnext = (int*)saferealloc((char*)screamnext,
+               (MEM_SIZE)(maxscream * sizeof(int)));
+       }
+    }
+
+    sfirst = screamfirst;
+    snext = screamnext;
+
+    if (!sfirst || !snext)
+       fatal("do_study: out of memory");
+
+    for (ch = 256; ch; --ch)
+       *sfirst++ = -1;
+    sfirst -= 256;
+
+    while (--pos >= 0) {
+       ch = s[pos];
+       if (sfirst[ch] >= 0)
+           snext[pos] = sfirst[ch] - pos;
+       else
+           snext[pos] = -pos;
+       sfirst[ch] = pos;
+    }
+
+    str->str_pok |= 4;
+    return 1;
+}
+
+init_eval()
+{
+#define A(e1,e2,e3) (e1+(e2<<1)+(e3<<2))
+    opargs[O_ITEM] =           A(1,0,0);
+    opargs[O_ITEM2] =          A(0,0,0);
+    opargs[O_ITEM3] =          A(0,0,0);
+    opargs[O_CONCAT] =         A(1,1,0);
+    opargs[O_MATCH] =          A(1,0,0);
+    opargs[O_NMATCH] =         A(1,0,0);
+    opargs[O_SUBST] =          A(1,0,0);
+    opargs[O_NSUBST] =         A(1,0,0);
+    opargs[O_ASSIGN] =         A(1,1,0);
+    opargs[O_MULTIPLY] =       A(1,1,0);
+    opargs[O_DIVIDE] =         A(1,1,0);
+    opargs[O_MODULO] =         A(1,1,0);
+    opargs[O_ADD] =            A(1,1,0);
+    opargs[O_SUBTRACT] =       A(1,1,0);
+    opargs[O_LEFT_SHIFT] =     A(1,1,0);
+    opargs[O_RIGHT_SHIFT] =    A(1,1,0);
+    opargs[O_LT] =             A(1,1,0);
+    opargs[O_GT] =             A(1,1,0);
+    opargs[O_LE] =             A(1,1,0);
+    opargs[O_GE] =             A(1,1,0);
+    opargs[O_EQ] =             A(1,1,0);
     opargs[O_NE] =             A(1,1,0);
     opargs[O_BIT_AND] =                A(1,1,0);
     opargs[O_XOR] =            A(1,1,0);
@@ -1165,15 +1752,15 @@ init_eval()
     opargs[O_SEQ] =            A(1,1,0);
     opargs[O_SNE] =            A(1,1,0);
     opargs[O_SUBR] =           A(1,0,0);
-    opargs[O_PRINT] =          A(1,0,0);
+    opargs[O_PRINT] =          A(1,1,0);
     opargs[O_CHDIR] =          A(1,0,0);
     opargs[O_DIE] =            A(1,0,0);
     opargs[O_EXIT] =           A(1,0,0);
     opargs[O_RESET] =          A(1,0,0);
     opargs[O_LIST] =           A(0,0,0);
-    opargs[O_EOF] =            A(0,0,0);
-    opargs[O_TELL] =           A(0,0,0);
-    opargs[O_SEEK] =           A(0,1,1);
+    opargs[O_EOF] =            A(1,0,0);
+    opargs[O_TELL] =           A(1,0,0);
+    opargs[O_SEEK] =           A(1,1,1);
     opargs[O_LAST] =           A(1,0,0);
     opargs[O_NEXT] =           A(1,0,0);
     opargs[O_REDO] =           A(1,0,0);
@@ -1189,7 +1776,7 @@ init_eval()
     opargs[O_LOG] =            A(1,0,0);
     opargs[O_SQRT] =           A(1,0,0);
     opargs[O_INT] =            A(1,0,0);
-    opargs[O_PRTF] =           A(1,0,0);
+    opargs[O_PRTF] =           A(1,1,0);
     opargs[O_ORD] =            A(1,0,0);
     opargs[O_SLEEP] =          A(1,0,0);
     opargs[O_FLIP] =           A(1,0,0);
@@ -1213,956 +1800,35 @@ init_eval()
     opargs[O_LINK] =           A(1,1,0);
     opargs[O_REPEAT] =         A(1,1,0);
     opargs[O_EVAL] =           A(1,0,0);
-}
-
-#ifdef VOIDSIG
-static void (*ihand)();
-static void (*qhand)();
-#else
-static int (*ihand)();
-static int (*qhand)();
-#endif
-
-STR *
-eval(arg,retary)
-register ARG *arg;
-STR ***retary;         /* where to return an array to, null if nowhere */
-{
-    register STR *str;
-    register int anum;
-    register int optype;
-    register int maxarg;
-    double value;
-    STR *quicksarg[5];
-    register STR **sarg = quicksarg;
-    register char *tmps;
-    char *tmps2;
-    int argflags;
-    long tmplong;
-    FILE *fp;
-    STR *tmpstr;
-    FCMD *form;
-    STAB *stab;
-    ARRAY *ary;
-    bool assigning = FALSE;
-    double exp(), log(), sqrt(), modf();
-    char *crypt(), *getenv();
-
-    if (!arg)
-       return &str_no;
-    str = arg->arg_ptr.arg_str;
-    optype = arg->arg_type;
-    maxarg = arg->arg_len;
-    if (maxarg > 3 || retary) {
-       sarg = (STR **)safemalloc((maxarg+2) * sizeof(STR*));
-    }
-#ifdef DEBUGGING
-    if (debug & 8) {
-       deb("%s (%lx) %d args:\n",opname[optype],arg,maxarg);
-    }
-    debname[dlevel] = opname[optype][0];
-    debdelim[dlevel++] = ':';
-#endif
-    for (anum = 1; anum <= maxarg; anum++) {
-       argflags = arg[anum].arg_flags;
-       if (argflags & AF_SPECIAL)
-           continue;
-      re_eval:
-       switch (arg[anum].arg_type) {
-       default:
-           sarg[anum] = &str_no;
-#ifdef DEBUGGING
-           tmps = "NULL";
-#endif
-           break;
-       case A_EXPR:
-#ifdef DEBUGGING
-           if (debug & 8) {
-               tmps = "EXPR";
-               deb("%d.EXPR =>\n",anum);
-           }
-#endif
-           sarg[anum] = eval(arg[anum].arg_ptr.arg_arg, Null(STR***));
-           break;
-       case A_CMD:
-#ifdef DEBUGGING
-           if (debug & 8) {
-               tmps = "CMD";
-               deb("%d.CMD (%lx) =>\n",anum,arg[anum].arg_ptr.arg_cmd);
-           }
-#endif
-           sarg[anum] = cmd_exec(arg[anum].arg_ptr.arg_cmd);
-           break;
-       case A_STAB:
-           sarg[anum] = STAB_STR(arg[anum].arg_ptr.arg_stab);
-#ifdef DEBUGGING
-           if (debug & 8) {
-               sprintf(buf,"STAB $%s ==",arg[anum].arg_ptr.arg_stab->stab_name);
-               tmps = buf;
-           }
-#endif
-           break;
-       case A_LEXPR:
-#ifdef DEBUGGING
-           if (debug & 8) {
-               tmps = "LEXPR";
-               deb("%d.LEXPR =>\n",anum);
-           }
-#endif
-           str = eval(arg[anum].arg_ptr.arg_arg,Null(STR***));
-           if (!str)
-               fatal("panic: A_LEXPR\n");
-           goto do_crement;
-       case A_LVAL:
-#ifdef DEBUGGING
-           if (debug & 8) {
-               sprintf(buf,"LVAL $%s ==",arg[anum].arg_ptr.arg_stab->stab_name);
-               tmps = buf;
-           }
-#endif
-           str = STAB_STR(arg[anum].arg_ptr.arg_stab);
-           if (!str)
-               fatal("panic: A_LVAL\n");
-         do_crement:
-           assigning = TRUE;
-           if (argflags & AF_PRE) {
-               if (argflags & AF_UP)
-                   str_inc(str);
-               else
-                   str_dec(str);
-               STABSET(str);
-               sarg[anum] = str;
-               str = arg->arg_ptr.arg_str;
-           }
-           else if (argflags & AF_POST) {
-               sarg[anum] = str_static(str);
-               if (argflags & AF_UP)
-                   str_inc(str);
-               else
-                   str_dec(str);
-               STABSET(str);
-               str = arg->arg_ptr.arg_str;
-           }
-           else {
-               sarg[anum] = str;
-           }
-           break;
-       case A_ARYLEN:
-           sarg[anum] = str_static(&str_no);
-           str_numset(sarg[anum],
-               (double)alen(arg[anum].arg_ptr.arg_stab->stab_array));
-#ifdef DEBUGGING
-           tmps = "ARYLEN";
-#endif
-           break;
-       case A_SINGLE:
-           sarg[anum] = arg[anum].arg_ptr.arg_str;
-#ifdef DEBUGGING
-           tmps = "SINGLE";
-#endif
-           break;
-       case A_DOUBLE:
-           (void) interp(str,str_get(arg[anum].arg_ptr.arg_str));
-           sarg[anum] = str;
-#ifdef DEBUGGING
-           tmps = "DOUBLE";
-#endif
-           break;
-       case A_BACKTICK:
-           tmps = str_get(arg[anum].arg_ptr.arg_str);
-           fp = popen(str_get(interp(str,tmps)),"r");
-           tmpstr = str_new(80);
-           str_set(str,"");
-           if (fp) {
-               while (str_gets(tmpstr,fp) != Nullch) {
-                   str_scat(str,tmpstr);
-               }
-               statusvalue = pclose(fp);
-           }
-           else
-               statusvalue = -1;
-           str_free(tmpstr);
-
-           sarg[anum] = str;
-#ifdef DEBUGGING
-           tmps = "BACK";
-#endif
-           break;
-       case A_READ:
-           fp = Nullfp;
-           last_in_stab = arg[anum].arg_ptr.arg_stab;
-           if (last_in_stab->stab_io) {
-               fp = last_in_stab->stab_io->fp;
-               if (!fp && (last_in_stab->stab_io->flags & IOF_ARGV)) {
-                   if (last_in_stab->stab_io->flags & IOF_START) {
-                       last_in_stab->stab_io->flags &= ~IOF_START;
-                       last_in_stab->stab_io->lines = 0;
-                       if (alen(last_in_stab->stab_array) < 0L) {
-                           tmpstr = str_make("-");     /* assume stdin */
-                           apush(last_in_stab->stab_array, tmpstr);
-                       }
-                   }
-                   fp = nextargv(last_in_stab);
-                   if (!fp)    /* Note: fp != last_in_stab->stab_io->fp */
-                       do_close(last_in_stab,FALSE);   /* now it does */
-               }
-           }
-         keepgoing:
-           if (!fp)
-               sarg[anum] = &str_no;
-           else if (!str_gets(str,fp)) {
-               if (last_in_stab->stab_io->flags & IOF_ARGV) {
-                   fp = nextargv(last_in_stab);
-                   if (fp)
-                       goto keepgoing;
-                   do_close(last_in_stab,FALSE);
-                   last_in_stab->stab_io->flags |= IOF_START;
-               }
-               if (fp == stdin) {
-                   clearerr(fp);
-               }
-               sarg[anum] = &str_no;
-               break;
-           }
-           else {
-               last_in_stab->stab_io->lines++;
-               sarg[anum] = str;
-           }
-#ifdef DEBUGGING
-           tmps = "READ";
-#endif
-           break;
-       }
-#ifdef DEBUGGING
-       if (debug & 8)
-           deb("%d.%s = '%s'\n",anum,tmps,str_peek(sarg[anum]));
-#endif
-    }
-    switch (optype) {
-    case O_ITEM:
-       if (str != sarg[1])
-           str_sset(str,sarg[1]);
-       STABSET(str);
-       break;
-    case O_ITEM2:
-       if (str != sarg[2])
-           str_sset(str,sarg[2]);
-       STABSET(str);
-       break;
-    case O_ITEM3:
-       if (str != sarg[3])
-           str_sset(str,sarg[3]);
-       STABSET(str);
-       break;
-    case O_CONCAT:
-       if (str != sarg[1])
-           str_sset(str,sarg[1]);
-       str_scat(str,sarg[2]);
-       STABSET(str);
-       break;
-    case O_REPEAT:
-       if (str != sarg[1])
-           str_sset(str,sarg[1]);
-       anum = (long)str_gnum(sarg[2]);
-       if (anum >= 1) {
-           tmpstr = str_new(0);
-           str_sset(tmpstr,str);
-           for (anum--; anum; anum--)
-               str_scat(str,tmpstr);
-       }
-       else
-           str_sset(str,&str_no);
-       STABSET(str);
-       break;
-    case O_MATCH:
-       str_set(str, do_match(str_get(sarg[1]),arg) ? Yes : No);
-       STABSET(str);
-       break;
-    case O_NMATCH:
-       str_set(str, do_match(str_get(sarg[1]),arg) ? No : Yes);
-       STABSET(str);
-       break;
-    case O_SUBST:
-       value = (double) do_subst(str, arg);
-       str = arg->arg_ptr.arg_str;
-       goto donumset;
-    case O_NSUBST:
-       str_set(arg->arg_ptr.arg_str, do_subst(str, arg) ? No : Yes);
-       str = arg->arg_ptr.arg_str;
-       break;
-    case O_ASSIGN:
-       if (arg[2].arg_flags & AF_SPECIAL)
-           do_assign(str,arg);
-       else {
-           if (str != sarg[2])
-               str_sset(str, sarg[2]);
-           STABSET(str);
-       }
-       break;
-    case O_CHOP:
-       tmps = str_get(str);
-       tmps += str->str_cur - (str->str_cur != 0);
-       str_set(arg->arg_ptr.arg_str,tmps);     /* remember last char */
-       *tmps = '\0';                           /* wipe it out */
-       str->str_cur = tmps - str->str_ptr;
-       str->str_nok = 0;
-       str = arg->arg_ptr.arg_str;
-       break;
-    case O_MULTIPLY:
-       value = str_gnum(sarg[1]);
-       value *= str_gnum(sarg[2]);
-       goto donumset;
-    case O_DIVIDE:
-       value = str_gnum(sarg[1]);
-       value /= str_gnum(sarg[2]);
-       goto donumset;
-    case O_MODULO:
-       value = str_gnum(sarg[1]);
-       value = (double)(((long)value) % (long)str_gnum(sarg[2]));
-       goto donumset;
-    case O_ADD:
-       value = str_gnum(sarg[1]);
-       value += str_gnum(sarg[2]);
-       goto donumset;
-    case O_SUBTRACT:
-       value = str_gnum(sarg[1]);
-       value -= str_gnum(sarg[2]);
-       goto donumset;
-    case O_LEFT_SHIFT:
-       value = str_gnum(sarg[1]);
-       value = (double)(((long)value) << (long)str_gnum(sarg[2]));
-       goto donumset;
-    case O_RIGHT_SHIFT:
-       value = str_gnum(sarg[1]);
-       value = (double)(((long)value) >> (long)str_gnum(sarg[2]));
-       goto donumset;
-    case O_LT:
-       value = str_gnum(sarg[1]);
-       value = (double)(value < str_gnum(sarg[2]));
-       goto donumset;
-    case O_GT:
-       value = str_gnum(sarg[1]);
-       value = (double)(value > str_gnum(sarg[2]));
-       goto donumset;
-    case O_LE:
-       value = str_gnum(sarg[1]);
-       value = (double)(value <= str_gnum(sarg[2]));
-       goto donumset;
-    case O_GE:
-       value = str_gnum(sarg[1]);
-       value = (double)(value >= str_gnum(sarg[2]));
-       goto donumset;
-    case O_EQ:
-       value = str_gnum(sarg[1]);
-       value = (double)(value == str_gnum(sarg[2]));
-       goto donumset;
-    case O_NE:
-       value = str_gnum(sarg[1]);
-       value = (double)(value != str_gnum(sarg[2]));
-       goto donumset;
-    case O_BIT_AND:
-       value = str_gnum(sarg[1]);
-       value = (double)(((long)value) & (long)str_gnum(sarg[2]));
-       goto donumset;
-    case O_XOR:
-       value = str_gnum(sarg[1]);
-       value = (double)(((long)value) ^ (long)str_gnum(sarg[2]));
-       goto donumset;
-    case O_BIT_OR:
-       value = str_gnum(sarg[1]);
-       value = (double)(((long)value) | (long)str_gnum(sarg[2]));
-       goto donumset;
-    case O_AND:
-       if (str_true(sarg[1])) {
-           anum = 2;
-           optype = O_ITEM2;
-           maxarg = 0;
-           argflags = arg[anum].arg_flags;
-           goto re_eval;
-       }
-       else {
-           if (assigning) {
-               str_sset(str, sarg[1]);
-               STABSET(str);
-           }
-           else
-               str = sarg[1];
-           break;
-       }
-    case O_OR:
-       if (str_true(sarg[1])) {
-           if (assigning) {
-               str_set(str, sarg[1]);
-               STABSET(str);
-           }
-           else
-               str = sarg[1];
-           break;
-       }
-       else {
-           anum = 2;
-           optype = O_ITEM2;
-           maxarg = 0;
-           argflags = arg[anum].arg_flags;
-           goto re_eval;
-       }
-    case O_COND_EXPR:
-       anum = (str_true(sarg[1]) ? 2 : 3);
-       optype = (anum == 2 ? O_ITEM2 : O_ITEM3);
-       maxarg = 0;
-       argflags = arg[anum].arg_flags;
-       goto re_eval;
-    case O_COMMA:
-       str = sarg[2];
-       break;
-    case O_NEGATE:
-       value = -str_gnum(sarg[1]);
-       goto donumset;
-    case O_NOT:
-       value = (double) !str_true(sarg[1]);
-       goto donumset;
-    case O_COMPLEMENT:
-       value = (double) ~(long)str_gnum(sarg[1]);
-       goto donumset;
-    case O_SELECT:
-       if (arg[1].arg_type == A_LVAL)
-           defoutstab = arg[1].arg_ptr.arg_stab;
-       else
-           defoutstab = stabent(str_get(sarg[1]),TRUE);
-       if (!defoutstab->stab_io)
-           defoutstab->stab_io = stio_new();
-       curoutstab = defoutstab;
-       str_set(str,curoutstab->stab_io->fp ? Yes : No);
-       STABSET(str);
-       break;
-    case O_WRITE:
-       if (maxarg == 0)
-           stab = defoutstab;
-       else if (arg[1].arg_type == A_LVAL)
-           stab = arg[1].arg_ptr.arg_stab;
-       else
-           stab = stabent(str_get(sarg[1]),TRUE);
-       if (!stab->stab_io) {
-           str_set(str, No);
-           STABSET(str);
-           break;
-       }
-       curoutstab = stab;
-       fp = stab->stab_io->fp;
-       debarg = arg;
-       if (stab->stab_io->fmt_stab)
-           form = stab->stab_io->fmt_stab->stab_form;
-       else
-           form = stab->stab_form;
-       if (!form || !fp) {
-           str_set(str, No);
-           STABSET(str);
-           break;
-       }
-       format(&outrec,form);
-       do_write(&outrec,stab->stab_io);
-       if (stab->stab_io->flags & IOF_FLUSH)
-           fflush(fp);
-       str_set(str, Yes);
-       STABSET(str);
-       break;
-    case O_OPEN:
-       if (do_open(arg[1].arg_ptr.arg_stab,str_get(sarg[2]))) {
-           str_set(str, Yes);
-           arg[1].arg_ptr.arg_stab->stab_io->lines = 0;
-       }
-       else
-           str_set(str, No);
-       STABSET(str);
-       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:
-       str_set(str,
-           do_close(arg[1].arg_ptr.arg_stab,TRUE) ? Yes : No );
-       STABSET(str);
-       break;
-    case O_EACH:
-       str_sset(str,do_each(arg[1].arg_ptr.arg_stab->stab_hash,sarg,retary));
-       retary = Null(STR***);          /* do_each already did retary */
-       STABSET(str);
-       break;
-    case O_VALUES:
-    case O_KEYS:
-       value = (double) do_kv(arg[1].arg_ptr.arg_stab->stab_hash,
-         optype,sarg,retary);
-       retary = Null(STR***);          /* do_keys already did retary */
-       goto donumset;
-    case O_ARRAY:
-       if (maxarg == 1) {
-           ary = arg[1].arg_ptr.arg_stab->stab_array;
-           maxarg = ary->ary_fill;
-           if (retary) { /* array wanted */
-               sarg =
-                 (STR **)saferealloc((char*)sarg,(maxarg+3)*sizeof(STR*));
-               for (anum = 0; anum <= maxarg; anum++) {
-                   sarg[anum+1] = str = afetch(ary,anum);
-               }
-               maxarg++;
-           }
-           else
-               str = afetch(ary,maxarg);
-       }
-       else
-           str = afetch(arg[2].arg_ptr.arg_stab->stab_array,
-               ((int)str_gnum(sarg[1])) - arybase);
-       if (!str)
-           return &str_no;
-       break;
-    case O_HASH:
-       tmpstab = arg[2].arg_ptr.arg_stab;              /* XXX */
-       str = hfetch(tmpstab->stab_hash,str_get(sarg[1]));
-       if (!str)
-           return &str_no;
-       break;
-    case O_LARRAY:
-       anum = ((int)str_gnum(sarg[1])) - arybase;
-       str = afetch(arg[2].arg_ptr.arg_stab->stab_array,anum);
-       if (!str || str == &str_no) {
-           str = str_new(0);
-           astore(arg[2].arg_ptr.arg_stab->stab_array,anum,str);
-       }
-       break;
-    case O_LHASH:
-       tmpstab = arg[2].arg_ptr.arg_stab;
-       str = hfetch(tmpstab->stab_hash,str_get(sarg[1]));
-       if (!str) {
-           str = str_new(0);
-           hstore(tmpstab->stab_hash,str_get(sarg[1]),str);
-       }
-       if (tmpstab == envstab) {       /* heavy wizardry going on here */
-           str->str_link.str_magic = tmpstab;/* str is now magic */
-           envname = savestr(str_get(sarg[1]));
-                                       /* he threw the brick up into the air */
-       }
-       else if (tmpstab == sigstab) {  /* same thing, only different */
-           str->str_link.str_magic = tmpstab;
-           signame = savestr(str_get(sarg[1]));
-       }
-       break;
-    case O_PUSH:
-       if (arg[1].arg_flags & AF_SPECIAL)
-           str = do_push(arg,arg[2].arg_ptr.arg_stab->stab_array);
-       else {
-           str = str_new(0);           /* must copy the STR */
-           str_sset(str,sarg[1]);
-           apush(arg[2].arg_ptr.arg_stab->stab_array,str);
-       }
-       break;
-    case O_POP:
-       str = apop(arg[1].arg_ptr.arg_stab->stab_array);
-       if (!str)
-           return &str_no;
-#ifdef STRUCTCOPY
-       *(arg->arg_ptr.arg_str) = *str;
-#else
-       bcopy((char*)str, (char*)arg->arg_ptr.arg_str, sizeof *str);
-#endif
-       safefree((char*)str);
-       str = arg->arg_ptr.arg_str;
-       break;
-    case O_SHIFT:
-       str = ashift(arg[1].arg_ptr.arg_stab->stab_array);
-       if (!str)
-           return &str_no;
-#ifdef STRUCTCOPY
-       *(arg->arg_ptr.arg_str) = *str;
-#else
-       bcopy((char*)str, (char*)arg->arg_ptr.arg_str, sizeof *str);
-#endif
-       safefree((char*)str);
-       str = arg->arg_ptr.arg_str;
-       break;
-    case O_SPLIT:
-       value = (double) do_split(str_get(sarg[1]),arg[2].arg_ptr.arg_spat,retary);
-       retary = Null(STR***);          /* do_split already did retary */
-       goto donumset;
-    case O_LENGTH:
-       value = (double) str_len(sarg[1]);
-       goto donumset;
-    case O_SPRINTF:
-       sarg[maxarg+1] = Nullstr;
-       do_sprintf(str,arg->arg_len,sarg);
-       break;
-    case O_SUBSTR:
-       anum = ((int)str_gnum(sarg[2])) - arybase;
-       for (tmps = str_get(sarg[1]); *tmps && anum > 0; tmps++,anum--) ;
-       anum = (int)str_gnum(sarg[3]);
-       if (anum >= 0 && strlen(tmps) > anum)
-           str_nset(str, tmps, anum);
-       else
-           str_set(str, tmps);
-       break;
-    case O_JOIN:
-       if (arg[2].arg_flags & AF_SPECIAL && arg[2].arg_type == A_EXPR)
-           do_join(arg,str_get(sarg[1]),str);
-       else
-           ajoin(arg[2].arg_ptr.arg_stab->stab_array,str_get(sarg[1]),str);
-       break;
-    case O_SLT:
-       tmps = str_get(sarg[1]);
-       value = (double) strLT(tmps,str_get(sarg[2]));
-       goto donumset;
-    case O_SGT:
-       tmps = str_get(sarg[1]);
-       value = (double) strGT(tmps,str_get(sarg[2]));
-       goto donumset;
-    case O_SLE:
-       tmps = str_get(sarg[1]);
-       value = (double) strLE(tmps,str_get(sarg[2]));
-       goto donumset;
-    case O_SGE:
-       tmps = str_get(sarg[1]);
-       value = (double) strGE(tmps,str_get(sarg[2]));
-       goto donumset;
-    case O_SEQ:
-       tmps = str_get(sarg[1]);
-       value = (double) strEQ(tmps,str_get(sarg[2]));
-       goto donumset;
-    case O_SNE:
-       tmps = str_get(sarg[1]);
-       value = (double) strNE(tmps,str_get(sarg[2]));
-       goto donumset;
-    case O_SUBR:
-       str_sset(str,do_subr(arg,sarg));
-       STABSET(str);
-       break;
-    case O_PRTF:
-    case O_PRINT:
-       if (maxarg <= 1)
-           stab = defoutstab;
-       else {
-           stab = arg[2].arg_ptr.arg_stab;
-           if (!stab)
-               stab = defoutstab;
-       }
-       if (!stab->stab_io)
-           value = 0.0;
-       else if (arg[1].arg_flags & AF_SPECIAL)
-           value = (double)do_aprint(arg,stab->stab_io->fp);
-       else {
-           value = (double)do_print(str_get(sarg[1]),stab->stab_io->fp);
-           if (ors && optype == O_PRINT)
-               do_print(ors, stab->stab_io->fp);
-       }
-       if (stab->stab_io->flags & IOF_FLUSH)
-           fflush(stab->stab_io->fp);
-       goto donumset;
-    case O_CHDIR:
-       tmps = str_get(sarg[1]);
-       if (!tmps || !*tmps)
-           tmps = getenv("HOME");
-       if (!tmps || !*tmps)
-           tmps = getenv("LOGDIR");
-       value = (double)(chdir(tmps) >= 0);
-       goto donumset;
-    case O_DIE:
-       tmps = str_get(sarg[1]);
-       if (!tmps || !*tmps)
-           exit(1);
-       fatal("%s\n",str_get(sarg[1]));
-       value = 0.0;
-       goto donumset;
-    case O_EXIT:
-       exit((int)str_gnum(sarg[1]));
-       value = 0.0;
-       goto donumset;
-    case O_RESET:
-       str_reset(str_get(sarg[1]));
-       value = 1.0;
-       goto donumset;
-    case O_LIST:
-       if (maxarg > 0)
-           str = sarg[maxarg]; /* unwanted list, return last item */
-       else
-           str = &str_no;
-       break;
-    case O_EOF:
-       str_set(str, do_eof(maxarg > 0 ? arg[1].arg_ptr.arg_stab : last_in_stab) ? Yes : No);
-       STABSET(str);
-       break;
-    case O_TELL:
-       value = (double)do_tell(maxarg > 0 ? arg[1].arg_ptr.arg_stab : last_in_stab);
-       goto donumset;
-       break;
-    case O_SEEK:
-       value = str_gnum(sarg[2]);
-       str_set(str, do_seek(arg[1].arg_ptr.arg_stab,
-         (long)value, (int)str_gnum(sarg[3]) ) ? Yes : No);
-       STABSET(str);
-       break;
-    case O_REDO:
-    case O_NEXT:
-    case O_LAST:
-       if (maxarg > 0) {
-           tmps = str_get(sarg[1]);
-           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)
-           fatal("Bad label: %s\n", maxarg > 0 ? tmps : "<null>");
-       longjmp(loop_stack[loop_ptr].loop_env, optype);
-    case O_GOTO:/* shudder */
-       goto_targ = str_get(sarg[1]);
-       longjmp(top_env, 1);
-    case O_INDEX:
-       tmps = str_get(sarg[1]);
-       if (!(tmps2 = instr(tmps,str_get(sarg[2]))))
-           value = (double)(-1 + arybase);
-       else
-           value = (double)(tmps2 - tmps + arybase);
-       goto donumset;
-    case O_TIME:
-       value = (double) time(0);
-       goto donumset;
-    case O_TMS:
-       value = (double) do_tms(retary);
-       retary = Null(STR***);          /* do_tms already did retary */
-       goto donumset;
-    case O_LOCALTIME:
-       tmplong = (long) str_gnum(sarg[1]);
-       value = (double) do_time(localtime(&tmplong),retary);
-       retary = Null(STR***);          /* do_localtime already did retary */
-       goto donumset;
-    case O_GMTIME:
-       tmplong = (long) str_gnum(sarg[1]);
-       value = (double) do_time(gmtime(&tmplong),retary);
-       retary = Null(STR***);          /* do_gmtime already did retary */
-       goto donumset;
-    case O_STAT:
-       value = (double) do_stat(arg,sarg,retary);
-       retary = Null(STR***);          /* do_stat already did retary */
-       goto donumset;
-    case O_CRYPT:
-#ifdef CRYPT
-       tmps = str_get(sarg[1]);
-       str_set(str,crypt(tmps,str_get(sarg[2])));
-#else
-       fatal(
-         "The crypt() function is unimplemented due to excessive paranoia.");
-#endif
-       break;
-    case O_EXP:
-       value = exp(str_gnum(sarg[1]));
-       goto donumset;
-    case O_LOG:
-       value = log(str_gnum(sarg[1]));
-       goto donumset;
-    case O_SQRT:
-       value = sqrt(str_gnum(sarg[1]));
-       goto donumset;
-    case O_INT:
-       modf(str_gnum(sarg[1]),&value);
-       goto donumset;
-    case O_ORD:
-       value = (double) *str_get(sarg[1]);
-       goto donumset;
-    case O_SLEEP:
-       tmps = str_get(sarg[1]);
-       time(&tmplong);
-       if (!tmps || !*tmps)
-           sleep((32767<<16)+32767);
-       else
-           sleep(atoi(tmps));
-       value = (double)tmplong;
-       time(&tmplong);
-       value = ((double)tmplong) - value;
-       goto donumset;
-    case O_FLIP:
-       if (str_true(sarg[1])) {
-           str_numset(str,0.0);
-           anum = 2;
-           arg->arg_type = optype = O_FLOP;
-           maxarg = 0;
-           arg[2].arg_flags &= ~AF_SPECIAL;
-           arg[1].arg_flags |= AF_SPECIAL;
-           argflags = arg[anum].arg_flags;
-           goto re_eval;
-       }
-       str_set(str,"");
-       break;
-    case O_FLOP:
-       str_inc(str);
-       if (str_true(sarg[2])) {
-           arg->arg_type = O_FLIP;
-           arg[1].arg_flags &= ~AF_SPECIAL;
-           arg[2].arg_flags |= AF_SPECIAL;
-           str_cat(str,"E0");
-       }
-       break;
-    case O_FORK:
-       value = (double)fork();
-       goto donumset;
-    case O_SYSTEM:
-       if (anum = vfork()) {
-           ihand = signal(SIGINT, SIG_IGN);
-           qhand = signal(SIGQUIT, SIG_IGN);
-           while ((maxarg = wait(&argflags)) != anum && maxarg != -1)
-               ;
-           if (maxarg == -1)
-               argflags = -1;
-           signal(SIGINT, ihand);
-           signal(SIGQUIT, qhand);
-           value = (double)argflags;
-           goto donumset;
-       }
-       /* FALL THROUGH */
-    case O_EXEC:
-       if (arg[1].arg_flags & AF_SPECIAL)
-           value = (double)do_aexec(arg);
-       else {
-           value = (double)do_exec(str_get(sarg[1]));
-       }
-       goto donumset;
-    case O_HEX:
-       maxarg = 4;
-       goto snarfnum;
-
-    case O_OCT:
-       maxarg = 3;
-
-      snarfnum:
-       anum = 0;
-       tmps = str_get(sarg[1]);
-       for (;;) {
-           switch (*tmps) {
-           default:
-               goto out;
-           case '8': case '9':
-               if (maxarg != 4)
-                   goto out;
-               /* FALL THROUGH */
-           case '0': case '1': case '2': case '3': case '4':
-           case '5': case '6': case '7':
-               anum <<= maxarg;
-               anum += *tmps++ & 15;
-               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 (maxarg != 4)
-                   goto out;
-               anum <<= 4;
-               anum += (*tmps++ & 7) + 9;
-               break;
-           case 'x':
-               maxarg = 4;
-               tmps++;
-               break;
-           }
-       }
-      out:
-       value = (double)anum;
-       goto donumset;
-    case O_CHMOD:
-    case O_CHOWN:
-    case O_KILL:
-    case O_UNLINK:
-       if (arg[1].arg_flags & AF_SPECIAL)
-           value = (double)apply(optype,arg,Null(STR**));
-       else {
-           sarg[2] = Nullstr;
-           value = (double)apply(optype,arg,sarg);
-       }
-       goto donumset;
-    case O_UMASK:
-       value = (double)umask((int)str_gnum(sarg[1]));
-       goto donumset;
-    case O_RENAME:
-       tmps = str_get(sarg[1]);
-#ifdef RENAME
-       value = (double)(rename(tmps,str_get(sarg[2])) >= 0);
-#else
-       tmps2 = str_get(sarg[2]);
-       UNLINK(tmps2);
-       if (!(anum = link(tmps,tmps2)))
-           anum = UNLINK(tmps);
-       value = (double)(anum >= 0);
-#endif
-       goto donumset;
-    case O_LINK:
-       tmps = str_get(sarg[1]);
-       value = (double)(link(tmps,str_get(sarg[2])) >= 0);
-       goto donumset;
-    case O_UNSHIFT:
-       ary = arg[2].arg_ptr.arg_stab->stab_array;
-       if (arg[1].arg_flags & AF_SPECIAL)
-           do_unshift(arg,ary);
-       else {
-           str = str_new(0);           /* must copy the STR */
-           str_sset(str,sarg[1]);
-           aunshift(ary,1);
-           astore(ary,0,str);
-       }
-       value = (double)(ary->ary_fill + 1);
-       break;
-    case O_EVAL:
-       str_sset(str,
-           do_eval(arg[1].arg_type != A_NULL ? sarg[1] : defstab->stab_val) );
-       STABSET(str);
-       break;
-    }
-#ifdef DEBUGGING
-    dlevel--;
-    if (debug & 8)
-       deb("%s RETURNS \"%s\"\n",opname[optype],str_get(str));
-#endif
-    goto freeargs;
-
-donumset:
-    str_numset(str,value);
-    STABSET(str);
-#ifdef DEBUGGING
-    dlevel--;
-    if (debug & 8)
-       deb("%s RETURNS \"%f\"\n",opname[optype],value);
-#endif
-
-freeargs:
-    if (sarg != quicksarg) {
-       if (retary) {
-           if (optype == O_LIST)
-               sarg[0] = &str_no;
-           else
-               sarg[0] = Nullstr;
-           sarg[maxarg+1] = Nullstr;
-           *retary = sarg;     /* up to them to free it */
-       }
-       else
-           safefree(sarg);
-    }
-    return str;
-
-nullarray:
-    maxarg = 0;
-#ifdef DEBUGGING
-    dlevel--;
-    if (debug & 8)
-       deb("%s RETURNS ()\n",opname[optype],value);
-#endif
-    goto freeargs;
+    opargs[O_FTEREAD] =                A(1,0,0);
+    opargs[O_FTEWRITE] =       A(1,0,0);
+    opargs[O_FTEEXEC] =                A(1,0,0);
+    opargs[O_FTEOWNED] =       A(1,0,0);
+    opargs[O_FTRREAD] =                A(1,0,0);
+    opargs[O_FTRWRITE] =       A(1,0,0);
+    opargs[O_FTREXEC] =                A(1,0,0);
+    opargs[O_FTROWNED] =       A(1,0,0);
+    opargs[O_FTIS] =           A(1,0,0);
+    opargs[O_FTZERO] =         A(1,0,0);
+    opargs[O_FTSIZE] =         A(1,0,0);
+    opargs[O_FTFILE] =         A(1,0,0);
+    opargs[O_FTDIR] =          A(1,0,0);
+    opargs[O_FTLINK] =         A(1,0,0);
+    opargs[O_SYMLINK] =                A(1,1,0);
+    opargs[O_FTPIPE] =         A(1,0,0);
+    opargs[O_FTSUID] =         A(1,0,0);
+    opargs[O_FTSGID] =         A(1,0,0);
+    opargs[O_FTSVTX] =         A(1,0,0);
+    opargs[O_FTCHR] =          A(1,0,0);
+    opargs[O_FTBLK] =          A(1,0,0);
+    opargs[O_FTSOCK] =         A(1,0,0);
+    opargs[O_FTTTY] =          A(1,0,0);
+    opargs[O_DOFILE] =         A(1,0,0);
+    opargs[O_FTTEXT] =         A(1,0,0);
+    opargs[O_FTBINARY] =       A(1,0,0);
+    opargs[O_UTIME] =          A(1,0,0);
+    opargs[O_WAIT] =           A(0,0,0);
+    opargs[O_SORT] =           A(1,0,0);
+    opargs[O_STUDY] =          A(1,0,0);
+    opargs[O_DELETE] =         A(1,0,0);
 }