perl 3.0 patch #14 patch #13, continued
[p5sagit/p5-mst-13.2.git] / stab.c
diff --git a/stab.c b/stab.c
index 845cec3..9d252bb 100644 (file)
--- a/stab.c
+++ b/stab.c
-/* $Header: stab.c,v 1.0.1.2 88/02/02 11:25:53 root Exp $
+/* $Header: stab.c,v 3.0.1.5 90/03/12 17:00:11 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:       stab.c,v $
- * Revision 1.0.1.2  88/02/02  11:25:53  root
- * patch13: moved extern int out of function for a poor Xenix machine.
+ * Revision 3.0.1.5  90/03/12  17:00:11  lwall
+ * patch13: undef $/ didn't work as advertised
+ * 
+ * Revision 3.0.1.4  90/02/28  18:19:14  lwall
+ * patch9: $0 is now always the command name
+ * patch9: you may now undef $/ to have no input record separator
+ * patch9: local($.) didn't work
+ * patch9: sometimes perl thought ordinary data was a symbol table entry
+ * patch9: stab_array() and stab_hash() weren't defined on MICROPORT
+ * 
+ * Revision 3.0.1.3  89/12/21  20:18:40  lwall
+ * patch7: ANSI strerror() is now supported
+ * patch7: errno may now be a macro with an lvalue
+ * patch7: in stab.c, sighandler() may now return either void or int
+ * 
+ * Revision 3.0.1.2  89/11/17  15:35:37  lwall
+ * patch5: sighandler() needed to be static
  * 
- * Revision 1.0.1.1  88/01/28  10:35:17  root
- * patch8: changed some stabents to support eval operator.
+ * Revision 3.0.1.1  89/11/11  04:55:07  lwall
+ * patch2: sys_errlist[sys_nerr] is illegal
  * 
- * Revision 1.0  87/12/18  13:06:14  root
- * Initial revision
+ * Revision 3.0  89/10/18  15:23:23  lwall
+ * 3.0 baseline
  * 
  */
 
-#include <signal.h>
-#include "handy.h"
 #include "EXTERN.h"
-#include "search.h"
-#include "util.h"
 #include "perl.h"
 
+#include <signal.h>
+
 static char *sig_name[] = {
-    "",
-    "HUP",
-    "INT",
-    "QUIT",
-    "ILL",
-    "TRAP",
-    "IOT",
-    "EMT",
-    "FPE",
-    "KILL",
-    "BUS",
-    "SEGV",
-    "SYS",
-    "PIPE",
-    "ALRM",
-    "TERM",
-    "???"
-#ifdef SIGTSTP
-    ,"STOP",
-    "TSTP",
-    "CONT",
-    "CHLD",
-    "TTIN",
-    "TTOU",
-    "TINT",
-    "XCPU",
-    "XFSZ"
-#ifdef SIGPROF
-    ,"VTALARM",
-    "PROF"
-#ifdef SIGWINCH
-    ,"WINCH"
-#ifdef SIGLOST
-    ,"LOST"
-#ifdef SIGUSR1
-    ,"USR1"
+    SIG_NAME,0
+};
+
+#ifdef VOIDSIG
+#define handlertype void
+#else
+#define handlertype int
 #endif
-#ifdef SIGUSR2
-    ,"USR2"
-#endif /* SIGUSR2 */
-#endif /* SIGLOST */
-#endif /* SIGWINCH */
-#endif /* SIGPROF */
-#endif /* SIGTSTP */
-    ,0
-    };
-
-extern int errno;
 
 STR *
-stab_str(stab)
-STAB *stab;
+stab_str(str)
+STR *str;
 {
+    STAB *stab = str->str_u.str_stab;
     register int paren;
     register char *s;
+    register int i;
 
-    switch (*stab->stab_name) {
-    case '0': case '1': case '2': case '3': case '4':
+    if (str->str_rare)
+       return stab_val(stab);
+
+    switch (*stab->str_magic->str_ptr) {
+    case '1': case '2': case '3': case '4':
     case '5': case '6': case '7': case '8': case '9': case '&':
        if (curspat) {
-           paren = atoi(stab->stab_name);
-           if (curspat->spat_compex.subend[paren] &&
-             (s = getparen(&curspat->spat_compex,paren))) {
-               curspat->spat_compex.subend[paren] = Nullch;
-               str_set(stab->stab_val,s);
+           paren = atoi(stab_name(stab));
+         getparen:
+           if (curspat->spat_regexp &&
+             paren <= curspat->spat_regexp->nparens &&
+             (s = curspat->spat_regexp->startp[paren]) ) {
+               i = curspat->spat_regexp->endp[paren] - s;
+               if (i >= 0)
+                   str_nset(stab_val(stab),s,i);
+               else
+                   str_sset(stab_val(stab),&str_undef);
            }
+           else
+               str_sset(stab_val(stab),&str_undef);
        }
        break;
     case '+':
        if (curspat) {
-           paren = curspat->spat_compex.lastparen;
-           if (curspat->spat_compex.subend[paren] &&
-             (s = getparen(&curspat->spat_compex,paren))) {
-               curspat->spat_compex.subend[paren] = Nullch;
-               str_set(stab->stab_val,s);
+           paren = curspat->spat_regexp->lastparen;
+           goto getparen;
+       }
+       break;
+    case '`':
+       if (curspat) {
+           if (curspat->spat_regexp &&
+             (s = curspat->spat_regexp->subbase) ) {
+               i = curspat->spat_regexp->startp[0] - s;
+               if (i >= 0)
+                   str_nset(stab_val(stab),s,i);
+               else
+                   str_nset(stab_val(stab),"",0);
+           }
+           else
+               str_nset(stab_val(stab),"",0);
+       }
+       break;
+    case '\'':
+       if (curspat) {
+           if (curspat->spat_regexp &&
+             (s = curspat->spat_regexp->endp[0]) ) {
+               str_set(stab_val(stab),s);
            }
+           else
+               str_nset(stab_val(stab),"",0);
        }
        break;
     case '.':
+#ifndef lint
        if (last_in_stab) {
-           str_numset(stab->stab_val,(double)last_in_stab->stab_io->lines);
+           str_numset(stab_val(stab),(double)stab_io(last_in_stab)->lines);
        }
+#endif
        break;
     case '?':
-       str_numset(stab->stab_val,(double)statusvalue);
+       str_numset(stab_val(stab),(double)statusvalue);
        break;
     case '^':
-       s = curoutstab->stab_io->top_name;
-       str_set(stab->stab_val,s);
+       s = stab_io(curoutstab)->top_name;
+       str_set(stab_val(stab),s);
        break;
     case '~':
-       s = curoutstab->stab_io->fmt_name;
-       str_set(stab->stab_val,s);
+       s = stab_io(curoutstab)->fmt_name;
+       str_set(stab_val(stab),s);
        break;
+#ifndef lint
     case '=':
-       str_numset(stab->stab_val,(double)curoutstab->stab_io->lines);
+       str_numset(stab_val(stab),(double)stab_io(curoutstab)->page_len);
        break;
     case '-':
-       str_numset(stab->stab_val,(double)curoutstab->stab_io->lines_left);
+       str_numset(stab_val(stab),(double)stab_io(curoutstab)->lines_left);
        break;
     case '%':
-       str_numset(stab->stab_val,(double)curoutstab->stab_io->page);
-       break;
-    case '(':
-       if (curspat) {
-           str_numset(stab->stab_val,(double)(curspat->spat_compex.subbeg[0] -
-               curspat->spat_compex.subbase));
-       }
-       break;
-    case ')':
-       if (curspat) {
-           str_numset(stab->stab_val,(double)(curspat->spat_compex.subend[0] -
-               curspat->spat_compex.subbeg[0]));
-       }
+       str_numset(stab_val(stab),(double)stab_io(curoutstab)->page);
        break;
+#endif
     case '/':
-       *tokenbuf = record_separator;
-       tokenbuf[1] = '\0';
-       str_set(stab->stab_val,tokenbuf);
+       if (record_separator != 12345) {
+           *tokenbuf = record_separator;
+           tokenbuf[1] = '\0';
+           str_nset(stab_val(stab),tokenbuf,rslen);
+       }
        break;
     case '[':
-       str_numset(stab->stab_val,(double)arybase);
+       str_numset(stab_val(stab),(double)arybase);
        break;
     case '|':
-       str_numset(stab->stab_val,
-          (double)((curoutstab->stab_io->flags & IOF_FLUSH) != 0) );
+       str_numset(stab_val(stab),
+          (double)((stab_io(curoutstab)->flags & IOF_FLUSH) != 0) );
        break;
     case ',':
-       str_set(stab->stab_val,ofs);
+       str_nset(stab_val(stab),ofs,ofslen);
        break;
     case '\\':
-       str_set(stab->stab_val,ors);
+       str_nset(stab_val(stab),ors,orslen);
        break;
     case '#':
-       str_set(stab->stab_val,ofmt);
+       str_set(stab_val(stab),ofmt);
        break;
     case '!':
-       str_numset(stab->stab_val,(double)errno);
+       str_numset(stab_val(stab), (double)errno);
+       str_set(stab_val(stab), strerror(errno));
+       stab_val(stab)->str_nok = 1;    /* what a wonderful hack! */
+       break;
+    case '<':
+       str_numset(stab_val(stab),(double)uid);
+       break;
+    case '>':
+       str_numset(stab_val(stab),(double)euid);
+       break;
+    case '(':
+       s = buf;
+       (void)sprintf(s,"%d",(int)gid);
+       goto add_groups;
+    case ')':
+       s = buf;
+       (void)sprintf(s,"%d",(int)egid);
+      add_groups:
+       while (*s) s++;
+#ifdef GETGROUPS
+#ifndef NGROUPS
+#define NGROUPS 32
+#endif
+       {
+           GIDTYPE gary[NGROUPS];
+
+           i = getgroups(NGROUPS,gary);
+           while (--i >= 0) {
+               (void)sprintf(s," %ld", (long)gary[i]);
+               while (*s) s++;
+           }
+       }
+#endif
+       str_set(stab_val(stab),buf);
        break;
     }
-    return stab->stab_val;
+    return stab_val(stab);
 }
 
-stabset(stab,str)
-register STAB *stab;
+stabset(mstr,str)
+register STR *mstr;
 STR *str;
 {
+    STAB *stab = mstr->str_u.str_stab;
     char *s;
     int i;
-    int sighandler();
+    static handlertype sighandler();
+
+    switch (mstr->str_rare) {
+    case 'E':
+       setenv(mstr->str_ptr,str_get(str));
+                               /* And you'll never guess what the dog had */
+       break;                  /*   in its mouth... */
+    case 'S':
+       s = str_get(str);
+       i = whichsig(mstr->str_ptr);    /* ...no, a brick */
+       if (strEQ(s,"IGNORE"))
+#ifndef lint
+           (void)signal(i,SIG_IGN);
+#else
+           ;
+#endif
+       else if (strEQ(s,"DEFAULT") || !*s)
+           (void)signal(i,SIG_DFL);
+       else
+           (void)signal(i,sighandler);
+       break;
+#ifdef SOME_DBM
+    case 'D':
+       hdbmstore(stab_hash(stab),mstr->str_ptr,mstr->str_cur,str);
+       break;
+#endif
+    case '#':
+       afill(stab_array(stab), (int)str_gnum(str) - arybase);
+       break;
+    case 'X':  /* merely a copy of a * string */
+       break;
+    case '*':
+       s = str_get(str);
+       if (strNE(s,"StB") || str->str_cur != sizeof(STBP)) {
+           if (!*s) {
+               STBP *stbp;
 
-    if (stab->stab_flags & SF_VMAGIC) {
-       switch (stab->stab_name[0]) {
+               (void)savenostab(stab); /* schedule a free of this stab */
+               if (stab->str_len)
+                   Safefree(stab->str_ptr);
+               Newz(601,stbp, 1, STBP);
+               stab->str_ptr = stbp;
+               stab->str_len = stab->str_cur = sizeof(STBP);
+               stab->str_pok = 1;
+               strcpy(stab_magic(stab),"StB");
+               stab_val(stab) = Str_new(70,0);
+               stab_line(stab) = line;
+           }
+           else
+               stab = stabent(s,TRUE);
+           str_sset(str,stab);
+       }
+       break;
+    case 's': {
+           struct lstring *lstr = (struct lstring*)str;
+
+           mstr->str_rare = 0;
+           str->str_magic = Nullstr;
+           str_insert(mstr,lstr->lstr_offset,lstr->lstr_len,
+             str->str_ptr,str->str_cur);
+       }
+       break;
+
+    case 'v':
+       do_vecset(mstr,str);
+       break;
+
+    case 0:
+       switch (*stab->str_magic->str_ptr) {
+       case '.':
+           if (localizing)
+               savesptr((STR**)&last_in_stab);
+           break;
        case '^':
-           safefree(curoutstab->stab_io->top_name);
-           curoutstab->stab_io->top_name = str_get(str);
-           curoutstab->stab_io->top_stab = stabent(str_get(str),TRUE);
+           Safefree(stab_io(curoutstab)->top_name);
+           stab_io(curoutstab)->top_name = s = savestr(str_get(str));
+           stab_io(curoutstab)->top_stab = stabent(s,TRUE);
            break;
        case '~':
-           safefree(curoutstab->stab_io->fmt_name);
-           curoutstab->stab_io->fmt_name = str_get(str);
-           curoutstab->stab_io->fmt_stab = stabent(str_get(str),TRUE);
+           Safefree(stab_io(curoutstab)->fmt_name);
+           stab_io(curoutstab)->fmt_name = s = savestr(str_get(str));
+           stab_io(curoutstab)->fmt_stab = stabent(s,TRUE);
            break;
        case '=':
-           curoutstab->stab_io->page_len = (long)str_gnum(str);
+           stab_io(curoutstab)->page_len = (long)str_gnum(str);
            break;
        case '-':
-           curoutstab->stab_io->lines_left = (long)str_gnum(str);
+           stab_io(curoutstab)->lines_left = (long)str_gnum(str);
+           if (stab_io(curoutstab)->lines_left < 0L)
+               stab_io(curoutstab)->lines_left = 0L;
            break;
        case '%':
-           curoutstab->stab_io->page = (long)str_gnum(str);
+           stab_io(curoutstab)->page = (long)str_gnum(str);
            break;
        case '|':
-           curoutstab->stab_io->flags &= ~IOF_FLUSH;
+           stab_io(curoutstab)->flags &= ~IOF_FLUSH;
            if (str_gnum(str) != 0.0) {
-               curoutstab->stab_io->flags |= IOF_FLUSH;
+               stab_io(curoutstab)->flags |= IOF_FLUSH;
            }
            break;
        case '*':
-           multiline = (int)str_gnum(str) != 0;
+           i = (int)str_gnum(str);
+           multiline = (i != 0);
            break;
        case '/':
-           record_separator = *str_get(str);
+           if (str->str_pok) {
+               record_separator = *str_get(str);
+               rslen = str->str_cur;
+           }
+           else {
+               record_separator = 12345;       /* fake a non-existent char */
+               rslen = 1;
+           }
            break;
        case '\\':
            if (ors)
-               safefree(ors);
+               Safefree(ors);
            ors = savestr(str_get(str));
+           orslen = str->str_cur;
            break;
        case ',':
            if (ofs)
-               safefree(ofs);
+               Safefree(ofs);
            ofs = savestr(str_get(str));
+           ofslen = str->str_cur;
            break;
        case '#':
            if (ofmt)
-               safefree(ofmt);
+               Safefree(ofmt);
            ofmt = savestr(str_get(str));
            break;
        case '[':
            arybase = (int)str_gnum(str);
            break;
+       case '?':
+           statusvalue = (unsigned short)str_gnum(str);
+           break;
        case '!':
            errno = (int)str_gnum(str);         /* will anyone ever use this? */
            break;
-       case '.':
-       case '+':
-       case '&':
-       case '0':
-       case '1':
-       case '2':
-       case '3':
-       case '4':
-       case '5':
-       case '6':
-       case '7':
-       case '8':
-       case '9':
+       case '<':
+           uid = (int)str_gnum(str);
+#ifdef SETREUID
+           if (delaymagic) {
+               delaymagic |= DM_REUID;
+               break;                          /* don't do magic till later */
+           }
+#endif /* SETREUID */
+#ifdef SETRUID
+           if (setruid((UIDTYPE)uid) < 0)
+               uid = (int)getuid();
+#else
+#ifdef SETREUID
+           if (setreuid((UIDTYPE)uid, (UIDTYPE)-1) < 0)
+               uid = (int)getuid();
+#else
+           fatal("setruid() not implemented");
+#endif
+#endif
+           break;
+       case '>':
+           euid = (int)str_gnum(str);
+#ifdef SETREUID
+           if (delaymagic) {
+               delaymagic |= DM_REUID;
+               break;                          /* don't do magic till later */
+           }
+#endif /* SETREUID */
+#ifdef SETEUID
+           if (seteuid((UIDTYPE)euid) < 0)
+               euid = (int)geteuid();
+#else
+#ifdef SETREUID
+           if (setreuid((UIDTYPE)-1, (UIDTYPE)euid) < 0)
+               euid = (int)geteuid();
+#else
+           fatal("seteuid() not implemented");
+#endif
+#endif
+           break;
        case '(':
+           gid = (int)str_gnum(str);
+#ifdef SETREGID
+           if (delaymagic) {
+               delaymagic |= DM_REGID;
+               break;                          /* don't do magic till later */
+           }
+#endif /* SETREGID */
+#ifdef SETRGID
+           (void)setrgid((GIDTYPE)gid);
+#else
+#ifdef SETREGID
+           (void)setregid((GIDTYPE)gid, (GIDTYPE)-1);
+#else
+           fatal("setrgid() not implemented");
+#endif
+#endif
+           break;
        case ')':
-           break;              /* "read-only" registers */
+           egid = (int)str_gnum(str);
+#ifdef SETREGID
+           if (delaymagic) {
+               delaymagic |= DM_REGID;
+               break;                          /* don't do magic till later */
+           }
+#endif /* SETREGID */
+#ifdef SETEGID
+           (void)setegid((GIDTYPE)egid);
+#else
+#ifdef SETREGID
+           (void)setregid((GIDTYPE)-1, (GIDTYPE)egid);
+#else
+           fatal("setegid() not implemented");
+#endif
+#endif
+           break;
+       case ':':
+           chopset = str_get(str);
+           break;
        }
-    }
-    else if (stab == envstab && envname) {
-       setenv(envname,str_get(str));
-                               /* And you'll never guess what the dog had */
-       safefree(envname);      /*   in its mouth... */
-       envname = Nullch;
-    }
-    else if (stab == sigstab && signame) {
-       s = str_get(str);
-       i = whichsig(signame);  /* ...no, a brick */
-       if (strEQ(s,"IGNORE"))
-           signal(i,SIG_IGN);
-       else if (strEQ(s,"DEFAULT") || !*s)
-           signal(i,SIG_DFL);
-       else
-           signal(i,sighandler);
-       safefree(signame);
-       signame = Nullch;
+       break;
     }
 }
 
-whichsig(signame)
-char *signame;
+whichsig(sig)
+char *sig;
 {
     register char **sigv;
 
     for (sigv = sig_name+1; *sigv; sigv++)
-       if (strEQ(signame,*sigv))
+       if (strEQ(sig,*sigv))
            return sigv - sig_name;
+#ifdef SIGCLD
+    if (strEQ(sig,"CHLD"))
+       return SIGCLD;
+#endif
+#ifdef SIGCHLD
+    if (strEQ(sig,"CLD"))
+       return SIGCHLD;
+#endif
     return 0;
 }
 
+static handlertype
 sighandler(sig)
 int sig;
 {
     STAB *stab;
     ARRAY *savearray;
     STR *str;
+    char *oldfile = filename;
+    int oldsave = savestack->ary_fill;
+    ARRAY *oldstack = stack;
+    SUBR *sub;
 
-    stab = stabent(str_get(hfetch(sigstab->stab_hash,sig_name[sig])),TRUE);
-    savearray = defstab->stab_array;
-    defstab->stab_array = anew();
-    str = str_new(0);
+    stab = stabent(
+       str_get(hfetch(stab_hash(sigstab),sig_name[sig],strlen(sig_name[sig]),
+         TRUE)), TRUE);
+    sub = stab_sub(stab);
+    if (!sub && *sig_name[sig] == 'C' && instr(sig_name[sig],"LD")) {
+       if (sig_name[sig][1] == 'H')
+           stab = stabent(str_get(hfetch(stab_hash(sigstab),"CLD",3,TRUE)),
+             TRUE);
+       else
+           stab = stabent(str_get(hfetch(stab_hash(sigstab),"CHLD",4,TRUE)),
+             TRUE);
+       sub = stab_sub(stab);   /* gag */
+    }
+    if (!sub) {
+       if (dowarn)
+           warn("SIG%s handler \"%s\" not defined.\n",
+               sig_name[sig], stab_name(stab) );
+       return;
+    }
+    savearray = stab_xarray(defstab);
+    stab_xarray(defstab) = stack = anew(defstab);
+    stack->ary_flags = 0;
+    str = Str_new(71,0);
     str_set(str,sig_name[sig]);
-    apush(defstab->stab_array,str);
-    str = cmd_exec(stab->stab_sub);
-    afree(defstab->stab_array);  /* put back old $_[] */
-    defstab->stab_array = savearray;
-}
+    (void)apush(stab_xarray(defstab),str);
+    sub->depth++;
+    if (sub->depth >= 2) {     /* save temporaries on recursion? */
+       if (sub->depth == 100 && dowarn)
+           warn("Deep recursion on subroutine \"%s\"",stab_name(stab));
+       savelist(sub->tosave->ary_array,sub->tosave->ary_fill);
+    }
+    filename = sub->filename;
 
-char *
-reg_get(name)
-char *name;
-{
-    return STAB_GET(stabent(name,TRUE));
-}
+    (void)cmd_exec(sub->cmd,G_SCALAR,1);               /* so do it already */
 
-#ifdef NOTUSED
-reg_set(name,value)
-char *name;
-char *value;
-{
-    str_set(STAB_STR(stabent(name,TRUE)),value);
+    sub->depth--;      /* assuming no longjumps out of here */
+    str_free(stack->ary_array[0]);     /* free the one real string */
+    afree(stab_xarray(defstab));  /* put back old $_[] */
+    stab_xarray(defstab) = savearray;
+    stack = oldstack;
+    filename = oldfile;
+    if (savestack->ary_fill > oldsave)
+       restorelist(oldsave);
 }
-#endif
 
 STAB *
 aadd(stab)
 register STAB *stab;
 {
-    if (!stab->stab_array)
-       stab->stab_array = anew();
+    if (!stab_xarray(stab))
+       stab_xarray(stab) = anew(stab);
     return stab;
 }
 
@@ -321,7 +520,189 @@ STAB *
 hadd(stab)
 register STAB *stab;
 {
-    if (!stab->stab_hash)
-       stab->stab_hash = hnew();
+    if (!stab_xhash(stab))
+       stab_xhash(stab) = hnew(COEFFSIZE);
     return stab;
 }
+
+STAB *
+stabent(name,add)
+register char *name;
+int add;
+{
+    register STAB *stab;
+    register STBP *stbp;
+    int len;
+    register char *namend;
+    HASH *stash;
+    char *sawquote = Nullch;
+    char *prevquote = Nullch;
+    bool global = FALSE;
+
+    if (isascii(*name) && isupper(*name)) {
+       if (*name > 'I') {
+           if (*name == 'S' && (
+             strEQ(name, "SIG") ||
+             strEQ(name, "STDIN") ||
+             strEQ(name, "STDOUT") ||
+             strEQ(name, "STDERR") ))
+               global = TRUE;
+       }
+       else if (*name > 'E') {
+           if (*name == 'I' && strEQ(name, "INC"))
+               global = TRUE;
+       }
+       else if (*name >= 'A') {
+           if (*name == 'E' && strEQ(name, "ENV"))
+               global = TRUE;
+       }
+       else if (*name == 'A' && (
+         strEQ(name, "ARGV") ||
+         strEQ(name, "ARGVOUT") ))
+           global = TRUE;
+    }
+    for (namend = name; *namend; namend++) {
+       if (*namend == '\'' && namend[1])
+           prevquote = sawquote, sawquote = namend;
+    }
+    if (sawquote == name && name[1]) {
+       stash = defstash;
+       sawquote = Nullch;
+       name++;
+    }
+    else if (!isalpha(*name) || global)
+       stash = defstash;
+    else
+       stash = curstash;
+    if (sawquote) {
+       char tmpbuf[256];
+       char *s, *d;
+
+       *sawquote = '\0';
+       if (s = prevquote) {
+           strncpy(tmpbuf,name,s-name+1);
+           d = tmpbuf+(s-name+1);
+           *d++ = '_';
+           strcpy(d,s+1);
+       }
+       else {
+           *tmpbuf = '_';
+           strcpy(tmpbuf+1,name);
+       }
+       stab = stabent(tmpbuf,TRUE);
+       if (!(stash = stab_xhash(stab)))
+           stash = stab_xhash(stab) = hnew(0);
+       name = sawquote+1;
+       *sawquote = '\'';
+    }
+    len = namend - name;
+    stab = (STAB*)hfetch(stash,name,len,add);
+    if (!stab)
+       return Nullstab;
+    if (stab->str_pok) {
+       stab->str_pok |= SP_MULTI;
+       return stab;
+    }
+    else {
+       if (stab->str_len)
+           Safefree(stab->str_ptr);
+       Newz(602,stbp, 1, STBP);
+       stab->str_ptr = stbp;
+       stab->str_len = stab->str_cur = sizeof(STBP);
+       stab->str_pok = 1;
+       strcpy(stab_magic(stab),"StB");
+       stab_val(stab) = Str_new(72,0);
+       stab_line(stab) = line;
+       str_magic(stab,stab,'*',name,len);
+       return stab;
+    }
+}
+
+STIO *
+stio_new()
+{
+    STIO *stio;
+
+    Newz(603,stio,1,STIO);
+    stio->page_len = 60;
+    return stio;
+}
+
+stab_check(min,max)
+int min;
+register int max;
+{
+    register HENT *entry;
+    register int i;
+    register STAB *stab;
+
+    for (i = min; i <= max; i++) {
+       for (entry = defstash->tbl_array[i]; entry; entry = entry->hent_next) {
+           stab = (STAB*)entry->hent_val;
+           if (stab->str_pok & SP_MULTI)
+               continue;
+           line = stab_line(stab);
+           warn("Possible typo: \"%s\"", stab_name(stab));
+       }
+    }
+}
+
+static int gensym = 0;
+
+STAB *
+genstab()
+{
+    (void)sprintf(tokenbuf,"_GEN_%d",gensym++);
+    return stabent(tokenbuf,TRUE);
+}
+
+/* hopefully this is only called on local symbol table entries */
+
+void
+stab_clear(stab)
+register STAB *stab;
+{
+    STIO *stio;
+    SUBR *sub;
+
+    afree(stab_xarray(stab));
+    (void)hfree(stab_xhash(stab));
+    str_free(stab_val(stab));
+    if (stio = stab_io(stab)) {
+       do_close(stab,FALSE);
+       Safefree(stio->top_name);
+       Safefree(stio->fmt_name);
+    }
+    if (sub = stab_sub(stab)) {
+       afree(sub->tosave);
+       cmd_free(sub->cmd);
+    }
+    Safefree(stab->str_ptr);
+    stab->str_ptr = Null(STBP*);
+    stab->str_len = 0;
+    stab->str_cur = 0;
+}
+
+#if defined(CRIPPLED_CC) && (defined(iAPX286) || defined(M_I286) || defined(I80286))
+#define MICROPORT
+#endif
+
+#ifdef MICROPORT       /* Microport 2.4 hack */
+ARRAY *stab_array(stab)
+register STAB *stab;
+{
+    if (((STBP*)(stab->str_ptr))->stbp_array) 
+       return ((STBP*)(stab->str_ptr))->stbp_array;
+    else
+       return ((STBP*)(aadd(stab)->str_ptr))->stbp_array;
+}
+
+HASH *stab_hash(stab)
+register STAB *stab;
+{
+    if (((STBP*)(stab->str_ptr))->stbp_hash)
+       return ((STBP*)(stab->str_ptr))->stbp_hash;
+    else
+       return ((STBP*)(hadd(stab)->str_ptr))->stbp_hash;
+}
+#endif                 /* Microport 2.4 hack */