date and time created 88/07/21 17:34:00 by marc
authorMarc Teitelbaum <marc@ucbvax.Berkeley.EDU>
Fri, 22 Jul 1988 08:34:00 +0000 (00:34 -0800)
committerMarc Teitelbaum <marc@ucbvax.Berkeley.EDU>
Fri, 22 Jul 1988 08:34:00 +0000 (00:34 -0800)
SCCS-vsn: local/toolchest/ksh/sh/name.c 1.1

usr/src/local/toolchest/ksh/sh/name.c [new file with mode: 0644]

diff --git a/usr/src/local/toolchest/ksh/sh/name.c b/usr/src/local/toolchest/ksh/sh/name.c
new file mode 100644 (file)
index 0000000..a51efc5
--- /dev/null
@@ -0,0 +1,1025 @@
+/*
+
+ *      Copyright (c) 1984, 1985, 1986 AT&T
+ *      All Rights Reserved
+
+ *      THIS IS UNPUBLISHED PROPRIETARY SOURCE 
+ *      CODE OF AT&T.
+ *      The copyright notice above does not 
+ *      evidence any actual or intended
+ *      publication of such source code.
+
+ */
+/*
+ * AT&T Bell Laboratories
+ *
+ */
+
+#include       "defs.h"
+#include       "io.h"
+#include       "flags.h"
+#include       "name.h"
+#include       "shtype.h"
+#include       "sym.h"
+#include       "stak.h"
+#include       "brkincr.h"
+#include       "builtins.h"
+#include       "history.h"
+#include       "timeout.h"
+
+/* This module defines the following routines */
+NAMPTR checkfor();
+void   do_whence();
+#ifdef ECHO_N
+char   *echo_mode();
+#endif /* ECHO_N */
+#ifdef apollo
+void   ev_$set_var();
+#endif /* apollo */
+int    genenv();
+char   *heap();
+void   meminit();
+void   mem_scope();
+void   mem_unscope();
+char   *movstr();
+void   name_unscope();
+char   *qvalup();
+void   prinscan();
+int    printnam();
+void   printflg();
+int    readvar();
+void   rmlocal();
+char   **setenv();
+void   setlist();
+NAMPTR setname();
+int    syslook();
+
+/* This module references the following external routines */
+extern void    assign();
+extern char    *bracket_match();
+extern unsigned        chkid();
+#ifdef ECHO_N
+extern MSG     echo_bin;
+extern MSG     echo_opt;
+#endif /* ECHO_N */
+extern char    **environ;
+extern void    exitsh();
+extern void    failed();
+extern void    free();
+extern NAMPTR  findnod();
+extern char    *fullname();
+extern char    *getstak();
+extern struct Amemory *gettree();
+extern void    gsort();
+extern long    hist_list();
+extern void    hist_flush();
+extern void    initf();
+extern void    gscan_all();
+extern void    gscan_some();
+extern NAMPTR  lookup();
+extern void    linknod();
+extern char    *mactrim();
+extern char    *malloc();
+extern char    *movstr();
+extern unsigned        rand();
+extern void    p_num();
+extern void    p_str();
+extern void    p_sub();
+extern void    p_setout();
+extern void    scan_all();
+extern char    *simple();
+extern int     srand();
+extern char    *strchr();
+extern char    *strcpy();
+extern void    rmnval();
+extern void    unassign();
+extern long    time();
+extern char    *utos();
+extern char    *valup();
+
+static void    countnam();
+#ifdef ECHO_N
+static char    *echo_arg;
+#endif /* ECHO_N */
+static long    get_rand();
+static long    get_second();
+static struct Amemory *inittree();
+static void    no_export();
+static void    pr_name();
+static void    pushnam();
+static void    pushnid();
+static void    rehash();
+static void    rm_node();
+static int     set_second();
+static int     set_rand();
+static char    *staknam();
+
+
+static rsflag; /* used to see if "SHELL" has been set in the environment */
+static int     namec;
+static char    **argnam;
+static struct Amemory  *namebase;
+
+struct Bfunction seconds = { get_second, set_second};
+struct Bfunction randnum = { get_rand, set_rand};
+
+
+
+/* ========    variable and string handling    ======== */
+
+/*
+ *  Table lookup routine
+ *  The table <syswds> is searched for string <w> and corresponding value is returned
+ */
+syslook(w,syswds)
+register char *w;
+SYSTAB         syswds;
+{
+       register int first;
+       register SYSPTR syscan;
+       register int c;
+       if(w==0 || (first= *w)==0)
+               return(0);
+       syscan=syswds;
+       while((c= *syscan->sysnam) && c <= first)
+       {
+               if(first == c && eq(w,syscan->sysnam))
+                       return(syscan->sysval);
+               syscan++;
+       }
+       return(0);
+}
+
+/*
+ * perform parameter assignment on an argument list
+ */
+
+void setlist(arg,xp)
+register ARGPTR        arg;
+register int xp;
+{
+       if(is_option(ALLEXP))
+               xp |= N_EXPORT;
+       while(arg)
+       {
+               register char *s;
+               if(arg->argflag&A_MAC)
+                       s=mactrim(arg->argval,0);
+               else
+                       s = arg->argval;
+               setname(s, xp);
+               arg=arg->argnxt;
+               if(is_option(EXECPR))
+               {
+                       p_setout(stderr);
+                       p_str(s,arg?SP:NL);
+               }
+       }
+}
+
+/*
+ * Put <arg> into associative memory.
+ * If <xp> & V_FLAG then the alias list is used instead
+ * If <xp> & S_FLAG then use the current scope only
+ */
+
+
+NAMPTR setname(argi, xp)
+char *argi;
+int    xp;
+{
+       register char *argscan=argi;
+       register NAMPTR n;
+       register int sep = *argscan;
+       char *sim;
+       if(isalpha(sep) || ((xp&V_FLAG) && !expchar(sep)))
+       {
+               do
+               {
+                       sep = *++argscan;
+               }
+               while(isalnum(sep));
+               /* check for subscript*/
+               if(sep=='[')
+               {
+                       argscan = bracket_match(argscan)+1;
+               }
+               if((sep = *argscan) && sep!='=')
+                       failed(argi,notid);
+               *argscan = 0;
+               if(xp&V_FLAG)
+               {
+                       n = findnod(argi,alias,1);
+                       if(attest(n,T_FLAG|NO_ALIAS))
+                               pattrib(n,~(NO_ALIAS|T_FLAG|N_EXPORT));
+               }
+               else if(xp&S_FLAG)
+                       /* scoped name must be in first tree */
+                       n = findnod(argi,namep,1);
+               else
+                       n = lookup(argi);
+               *argscan++ = sep;
+               if(sep == '=')
+               {
+                       if(n==PATHNOD || n==ENVNOD || n==SHELLNOD)
+                       {
+                               if(is_option(RSHFLG))
+                                       failed(argi,restricted);
+                               if(n==SHELLNOD)
+                               {
+                                       sim = simple(argscan);
+                                       if(strchr(sim,'r') != NULL)
+                                               rsflag = 0;     /* restricted shell */
+                               }
+                       }
+                       assign (n, argscan);
+                       attrib(n, xp&~(S_FLAG|V_FLAG));
+#ifdef apollo
+                       if(attest(n,N_EXPORT) && attest(n,N_IMPORT)==0 
+                               && (xp&(S_FLAG|V_FLAG))==0)
+                       {
+                               short namlen,vallen;
+                               namlen =strlen(n->namid);
+                               vallen = strlen(argscan);
+                               ev_$set_var(n->namid,&namlen,argscan,&vallen);
+                       }
+#endif /* apollo */
+                       if(n==PATHNOD)
+                       {
+                               gscan_some(rehash,alias,T_FLAG,T_FLAG);
+#ifdef ECHO_N
+                               echo_arg = NIL;
+#endif /* ECHO_N */
+                       }
+                       if(n==VISINOD || ((n==EDITNOD)&&isnull(VISINOD)))
+                       {
+                               /* turn on vi or emacs option if editor name is either*/
+                               argscan = simple(argscan);
+                               if(gmatch(argscan,"*vi"))
+                               {
+                                       off_option(EDITVI|EMACS|GMACS);
+                                       on_option(EDITVI);
+                               }
+                               if(gmatch(argscan,"*macs"))
+                               {
+                                       off_option(EDITVI|EMACS|GMACS);
+                                       if(*argscan=='g')
+                                               on_option(GMACS);
+                                       else
+                                               on_option(EMACS);
+                               }
+                       }
+               }
+               return(n);
+       }
+       failed (argi, notid);
+       /* NOTREACHED */
+}
+
+/*
+ * Mark each node is invalid
+ */
+
+static void rehash(np)
+register NAMPTR np;
+{
+       attrib(np,NO_ALIAS);
+}
+
+/*
+ *  alias each name to full path name
+ *  realias returns the pathname or NULL if not found
+ */
+
+char *realias(np)
+register NAMPTR np;
+{
+       register char *sp;
+       register char *vp = np->value.namval.cp;
+       int flag = namflag(np)&(N_EXPORT|NO_ALIAS|T_FLAG);
+       /* turn of T_FLAG to avoid recursion */
+       pattrib(np,~(NO_ALIAS|T_FLAG));
+       sp = fullname(np->namid);
+       if(sp==NIL)
+       {
+               unassign(np);
+               return(NIL);
+       }
+       else if(*sp!= '/')
+       {
+               sattrib(np,flag);
+               return(NIL);
+       }
+       if(vp==0 || strcmp(sp,vp)!=0)
+               assign(np,sp);
+       /* turn T_FLAG back on */
+       attrib(np,T_FLAG|N_EXPORT);
+       return(sp);
+}
+
+
+int readvar(names,fd,raw)
+register char **names;
+FILE *fd;
+{
+       FILEBLK fb;
+       SHFILE f;
+       register int c;
+       int issep;
+       register NAMPTR n;
+       int checksep = 1;               /* set when looking for separators */
+       STKPTR  rel;
+       char *seps;
+       char is_eol;
+       FILE *savef;
+       states |= RWAIT;
+       /* save in history file if S_FLAG is set */
+       if((raw&S_FLAG) && fc_fix)
+               states |= FIXFLG;
+       raw &= R_FLAG;
+       f = &fb;
+       if(*names)
+       {
+               if(seps=strchr(*names,'?'))
+                       *seps = 0;
+               n = lookup(*names++);
+               if(seps)
+                       *seps = '?';
+       }
+       else
+               n = REPLYNOD;
+       rel=(STKPTR)relstak();
+       seps = qvalup(IFSNOD);
+       if(seps==NULL)
+               seps = sptbnl;
+       savef = input;
+       if(fd==NULL)
+               failed(bread,noquery);
+       if(fd != input)
+       {
+               /* buffer the input stream if possible */
+               if(fnobuff(fd) && (fd!=stdin || !ispipe(stdin)))
+                       setbuf(fd,malloc((unsigned)BUFSIZ));
+               push(f);
+               initf(fd);
+       }
+       while(1)
+       {
+               c = (raw?readc():nextc());
+               issep = (strchr(seps,c)!=0);
+               is_eol = eolchar(c);
+               if(checksep && issep && !is_eol)
+               {
+                       /* visable adjacent separators signify null fields*/
+                       if(strchr(sptbnl,c)!=0)
+                               continue;
+               }
+               checksep = 0;
+               if((issep && *names) || is_eol) /* from non-separator to separator */
+               {
+                       if(*names==NULL && staktop>stakbot)
+                       {
+                               /* remove trailing separators */
+                               while(strchr(seps,*--staktop));
+                               staktop++;
+                       }
+                       zerostak();
+                       assign(n,absstak(rel)); setstak(rel);
+                       if(is_option(ALLEXP))
+                               attrib(n,N_EXPORT);
+                       n = (*names?lookup(*names++):0);
+                       if(is_eol)
+                               break;
+                       checksep = 1;
+               }
+               else            /* not a separator */
+                       pushstak(c);
+       }
+       while(n)
+       {
+               assign(n, nullstr);
+               if(is_option(ALLEXP))
+                       attrib(n,N_EXPORT);
+               n = (*names?lookup(*names++):0);
+       }
+       if(savef != fd)
+               pop(1);
+       if(states&FIXFLG)
+               hist_flush();
+       states &= ~(RWAIT|PROMPT);
+       states |= is_option(INTFLG);
+       return;
+}
+
+/*
+ * put string v onto the heap and return the heap pointer
+ */
+
+char *heap(v)
+register char *v;
+{
+       register char *p;
+       if(v)
+       {
+               movstr(v,p=malloc((unsigned)strlen(v)+1));
+               return(p);
+       }
+       else
+               return(0);
+}
+
+
+/*
+ * print out the name and value of a name-value pair <n>
+ */
+
+int printnam(n,flag)
+register NAMPTR                n;
+register int   flag;
+{
+       register FILE *fd;
+       register char *s;
+       union Namval *up= &n->value.namval;
+       if(trapnote&SIGSET)
+               exitsh(SIGFAIL);
+       fd = output;
+       if(attest(n,NO_ALIAS)==NO_ALIAS)
+       {
+               return(0);
+       }
+       if(is_afunction(n))
+       {
+               fputs(bltfn,fd);
+               fputs(n->namid,fd);
+               if(flag==0 && n->value.namval.rp->hoffset >=0 )
+               {
+                       fputs(fn_hdr,fd);
+                       hist_list(n->value.namval.rp->hoffset,EOF,"\n");
+               }
+               else
+                       putc('\n',fd);
+               return(n->namsz+1);
+       }
+       if(s=valup(n))
+       {
+               char numbuf[30];
+               pr_name(n,0);
+               flag = (flag?NL:'=');
+               if (attest (n, ARRAY))
+               {
+                       if(attest(n,INT_GER))
+                       {
+                               /* copy to a save place */
+                               strcpy(numbuf,s);
+                               s = numbuf;
+                       }
+                       p_sub((int)up->aray->adot,flag);
+               }
+               else
+                       putc(flag,fd);
+               if(flag != NL)
+                       p_str(s,NL);
+               return(1);
+       }
+       return(0);
+}
+
+/*
+ * print the name of a node followed by the character c
+ */
+
+static void pr_name(n,c)
+register NAMPTR                n;
+int c;
+{
+       register char *cp = strchr(n->namid,'=');
+       if(cp)
+               *cp = 0;
+       p_str(n->namid,c);
+       if(cp)
+               *cp = '=';
+}
+
+static void pushnid(np)
+NAMPTR np;
+{
+       *argnam++ = np->namid;
+       namec++;
+       if(attest(np,ARRAY))
+               arayp(np)->adot = arayp(np)->maxi;
+}
+
+/*
+ * print the nodes in tree <root> which have attributes <flag> set
+ */
+
+void prinscan(file,flag,root,option)
+FILE *file;
+struct Amemory *root;
+{
+       register char **argv;
+       register NAMPTR np;
+       p_setout(file);
+       argv = argnam  = (char**)locstak();
+       namec = 0;
+       if(flag)
+               gscan_some(pushnid,root,flag,flag);
+       else
+               gscan_all(pushnid,root);
+       gsort(argv,namec);
+       while(namec--)
+       {
+               {
+                       register char *cp;
+                       if(cp = strchr(*argv,'='))
+                               *cp = 0;
+                       np = checkfor(*argv++,root);
+                       if(cp)
+                               *cp = '=';
+               }
+               if(np)
+               {
+                       if(attest(np,ARRAY))
+                       {
+                               register struct Namaray *ap = arayp (np);
+                               register int i, imax;
+                               i = ap->adot = 0;
+                               imax = ap->maxi;
+                               for (; i <= imax; i++)
+                               {
+                                       ap->adot = i;
+                                       if (ap->val[i])
+                                               printnam(np,option);
+                               }
+                       }
+                       else
+                               printnam(np,option);
+               }
+       }
+}
+
+static char *staknam(n,value)
+char * value;
+register NAMPTR        n;
+{
+       register char *p,*q;
+       q = getstak(strlen(n->namid)+strlen(value)+2);
+       p=movstr(n->namid,q);
+       *p++ = '=';
+       strcpy(p,value);
+       return(q);
+}
+
+
+void   printflg(n)
+register NAMPTR                n;
+{
+       register SYSPTR syscan;
+       register int val;
+       if (namflag(n) != N_DEFAULT)
+       {
+               syscan=attributes;
+               while(*syscan->sysnam)
+               {
+                       val = syscan->sysval;
+                       if(attest(n,val)==val)
+                       {
+                               p_str(syscan->sysnam,SP);
+                               if (attest (n, L_JUST|R_JUST|Z_FILL))
+                                       p_num(n->namsz,SP);
+                               if(val == (BLT_NOD|INT_GER))
+                                       break;
+                       }
+                       if(val == INT_GER && attest(n,INT_GER))
+                       {
+                               if(n->namsz != 10)
+                               {
+                                       p_str(intbase,SP);
+                                       p_num(n->namsz,SP);
+                               }
+                               break;
+                       }
+                       syscan++;
+               }
+               pr_name(n,NL);
+       }
+}
+
+int    genenv()
+{
+       register char **e=environ;
+       register NAMPTR         n;
+       rsflag = 1;
+       if(e)
+       {
+               while(*e)
+               {
+                       n = setname(*e, (N_IMPORT|N_EXPORT)); 
+                       n->namid = *e++;
+               }
+       }
+       return(rsflag);
+}
+
+
+static void    countnam()
+{
+       namec++;
+}
+
+
+static void    pushnam(n)
+register NAMPTR                n;
+{
+       register char *value;
+       if(attest(n,N_IMPORT))
+               *argnam++ = n->namid;
+       else if(value=valup(n))
+               *argnam++ = staknam(n,value);
+}
+
+/*
+ * Generate the environment list for the child.
+ */
+
+
+char **setenv()
+{
+       register char **er;
+       namec = 0;
+       /* L_ARGNOD gets generated automatically as full path name of command */
+       pattrib(L_ARGNOD,~N_EXPORT);
+       gscan_some (countnam,namep, N_EXPORT|N_IMPORT, N_EXPORT);
+       er = (char**)getstak((namec+2)*BYTESPERWORD);
+       argnam = ++er;
+       gscan_some (pushnam, namep, N_EXPORT|N_IMPORT, N_EXPORT);
+       *argnam++ = 0;
+       return(er);
+}
+
+/*
+ * Initialize the shell name and alias table
+ */
+
+void meminit()
+{
+       register NAMPTR np;
+       bltin_nodes = (NAMPTR)malloc((unsigned)(NNODES*sizeof(struct Namnod)));
+       namebase = namep = inittree(node_names,bltin_nodes,0);
+       /* set up random number generator */
+#ifdef apollo
+       (PPIDNOD)->value.namval.cp = (char*)(&ppid);
+       (L_ARGNOD)->value.namval.cp = (char*)(&lastarg);
+       (TMOUTNOD)->value.namval.cp = (char*)(&timeout);
+       (SECONDS)->value.namval.cp = (char*)(&seconds);
+       (MCHKNOD)->value.namval.cp = (char*)(&mailchk);
+       (RANDNOD)->value.namval.cp = (char*)(&randnum);
+#endif /* apollo */
+       namflag(RANDNOD) = N_FREE|INT_GER|BLT_NOD;
+       /* set up the seconds clock */
+       namflag(SECONDS) = N_FREE|INT_GER|BLT_NOD;
+       set_second(0L);
+       namflag(MCHKNOD) = N_FREE|INT_GER;
+       namflag(TMOUTNOD) = INT_GER;
+       namflag(PPIDNOD) = (N_FREE|INT_GER|N_RDONLY);
+       namflag(L_ARGNOD) = N_FREE|IN_DIR;
+       np = (NAMPTR)malloc(NALIAS*sizeof(struct Namnod));
+       alias = inittree(alias_names,np,N_EXPORT);
+       prnames = gettree(MEMSIZE/4);
+}
+
+/*
+ * re-initialize name-value pairs after fork
+ */
+
+
+static struct Amemory *inittree(name_vals,nodes,atflag)
+struct name_value *name_vals;
+NAMPTR nodes;
+{
+       register struct Amemory *treep;
+       register NAMPTR np;
+       register struct name_value *nv;
+       int flag;
+       treep = gettree (MEMSIZE);
+       for(np=nodes,nv=name_vals;*nv->nv_name;nv++,np++)
+       {
+               np->namid = nv->nv_name;
+               np->value.namval.cp = nv->nv_value;
+               flag = 0;
+#ifdef apollo
+               if(*nv->nv_value==0)
+                       np->value.namval.cp = 0;
+               else
+#else
+               if(nv->nv_value)
+#endif /* apollo */
+               {
+                       flag = atflag|N_FREE;
+                       if(atflag && *nv->nv_value=='/')
+                               flag |= T_FLAG;
+               }
+               sattrib(np,flag);
+               np->namsz = 10;
+               linknod (np, treep);
+       }
+       return(treep);
+}
+
+/*
+ * create a new environment scope
+ */
+
+void mem_scope(envlist)
+ARGPTR envlist;
+{
+       register struct Amemory *sav_namep = namep;
+       register struct Amemory *newscope;
+       newscope = gettree(MEMSIZE/8);
+       newscope->nexttree = sav_namep;
+       namep = newscope;
+       setlist(envlist,N_EXPORT|S_FLAG);
+       newscope->nexttree = NULL;
+       namep = sav_namep;
+       scan_all(no_export,newscope);
+       newscope->nexttree = sav_namep;
+       namep = newscope;
+}
+
+/*
+ * Temporarily remove name from export list of previous scopes
+ */
+
+static void no_export(nnod)
+register struct Namnod *nnod;
+{
+       register struct Namnod *np = checkfor(nnod->namid,namep);
+       if(np && attest(np,N_EXPORT))
+       {
+               pattrib(np,~N_EXPORT);
+               attrib(np,E_FLAG);
+       }
+}
+
+/*
+ * free up top environment scope
+ */
+
+void mem_unscope()
+{
+       register struct Amemory *ap = namep;
+       if((namep = ap->nexttree)==NULL)
+               namep = namebase;
+       scan_all(rm_node,ap);
+       free((char*)ap);
+}
+
+/*
+ * free up all environment scopes except the first
+ */
+
+void name_unscope()
+{
+       while(namep->nexttree)
+               mem_unscope();
+}
+
+/*
+ * Remove a node and free up all the space
+ * Restate export attribute for hidden nodes if necessary
+ */
+static void rm_node(nnod)
+register struct Namnod *nnod;
+{
+       register struct Namnod *np = checkfor(nnod->namid,namep);
+       if(np && attest(np,E_FLAG))
+       {
+               pattrib(np,~E_FLAG);
+               attrib(np,N_EXPORT);
+       }
+       pattrib(nnod,~N_EXPORT);
+       rmlocal(nnod);
+       free((char*)nnod);
+}
+
+/* 
+ * Remove freeable local space associated with the namval field
+ * of nnod. This includes any strings representing the value(s) of the
+ * node, as well as its dope vector, if it is an array.
+ */
+
+void   rmlocal (nnod)
+register struct Namnod *nnod;
+{
+       register int i;
+       register struct Nodval *nv;
+       register struct Namaray *ap;
+       register union Namval *up = &nnod->value.namval;
+
+       /* return if the node is global or unfreeable */
+
+       if (attest (nnod, N_EXPORT|N_FREE))
+               return;
+       /*
+        * If the node is a freeable array, then free both the stringspace
+        * associated with it and its dope vector.
+        */
+
+       else if (attest (nnod, ARRAY))
+       {
+               ap = up->aray;
+               i = ap->maxi;
+               while(i >= 0)
+               {
+                       nv = ap->val[i--];
+                       if (nv)
+                       {
+                               if (freeble (nv))
+                                       rmnval (unmark (nv));
+                               else 
+                               {
+                                       up = &nv->namval;
+                                       if ((up->cp) && ((nv->namflg & (N_FREE|N_ALLOC)) == 0))
+                                               free ((nv->namflg & IN_DIR)?up->up->cp:up->cp);
+                               }
+                       }
+               }
+               free ((char*)(arayp(nnod)));
+               nnod->value.namval.cp = NULL;
+       }
+       /* 
+        * otherwise node is a freeable scalar, so free the string 
+        * representing its value.
+        */
+       else
+       {
+               unassign (nnod);
+       }
+       sattrib (nnod, N_DEFAULT);
+}
+
+/*
+ * Get the value of a built-in node
+ * A lookup may not be necessary
+ */
+
+char *qvalup(n)
+register NAMPTR        n;
+{
+       if(namep->nexttree)
+               n = lookup((node_names+(n-bltin_nodes))->nv_name);
+       return(valup(n));
+}
+
+/*
+ * lookup name in trees root and return Namnod pointer with this name.
+ * If none exists, it will not be created.
+ */
+
+NAMPTR checkfor(name,root)
+char *name;
+struct Amemory *root;
+{
+       register struct Namnod *np = NULL;
+       register struct Amemory *app = root;
+       struct Namnod *findnod();
+       while(app && np==NULL)
+       {
+               np = findnod(name,app,0);
+               app = app->nexttree;
+       }
+       return((np==NULL||isnull(np))?NULL:np);
+}
+
+/*
+ *  for the whence command
+ */
+
+void   do_whence(com,flag)
+char **com;
+register int flag;
+{
+       register char *a1;
+       register struct Namnod *np;
+       register char *cp;
+       struct Namnod *fp;
+       while(a1 = *++com)
+       {
+               if(flag)
+                       fputs(a1,output);
+               np = NULL;
+               /* reserved words first */
+               if(syslook(a1,reserved))
+               {
+                       if(flag)
+                               a1 = is_reserved;
+               }
+               /* non-tracked aliases */
+               else if((np=findnod(a1,alias,CHK_FOR)) && !isnull(np)
+                        && attest(np,T_FLAG)==0 && (a1=valup(np))) 
+               {
+                       if(flag)
+                       {
+                               if(attest(np,N_EXPORT))
+                                       cp = is_xalias;
+                               else
+                                       cp = is_alias;
+                               fputs(cp,output);
+                       }
+               }
+               /* built-in commands next */
+               else if(syslook(a1,commands))
+               {
+                       if(flag)
+                               a1 = is_builtin;
+               }
+               /* functions next */
+               else if((fp=findnod(a1,prnames,CHK_FOR))&& !isnull(fp))
+               {
+                       if(flag)
+                               a1=attest(fp,N_EXPORT)?is_xfunction:is_function;
+               }
+               else
+               {
+                       /* find full pathname */
+                       a1 = fullname(a1);
+                       if(a1)
+                       {
+                               if(flag)
+                               {
+                                       /* tracked aliases next */
+                                       if(np && attest(np,T_FLAG) && *a1 == '/')
+                                               fputs(is_talias,output);
+                                       else
+                                               fputs(is_,output);
+                               }
+                       }
+                       else 
+                       {
+                               a1 = (flag?notfound:nullstr);
+                               exitval |= 1;
+                       }
+               }
+               p_str(a1,NL);
+       }
+}
+
+/*
+ * these functions are used to get and set the SECONDS variable
+ */
+
+static long sec_offset;
+
+static int set_second(n)
+long n;
+{
+       sec_offset =  time((long*)0) - n ;
+}
+
+static long get_second()
+{
+       return(time((long*)0)-sec_offset);
+}
+
+/*
+ * These functions are used to get and set the RANDOM variable
+ */
+
+static int set_rand(n)
+long n;
+{
+       srand((int)n);
+}
+
+static long get_rand()
+{
+       return((long)rand());
+}
+
+#ifdef ECHO_N
+char *echo_mode()
+{
+       register char *cp;
+       optflag savopts;
+       if(echo_arg==0)
+       {
+#ifdef apollo
+               register NAMPTR np = checkfor("SYSTYPE",namep);
+               if(np && (cp=valup(np)))
+               {
+                       echo_arg = (*cp=='b'?echo_opt:minus);
+                       return(echo_arg);
+               }
+#endif /* apollo */
+               savopts = flags;
+               off_option(HASHALL);
+               cp = fullname(echo_bin+5);
+               flags = savopts;
+               if(eq(cp,echo_bin))
+                       echo_arg = echo_opt;
+               else
+                       echo_arg = minus;
+       }
+       return(echo_arg);
+}
+#endif /* ECHO_N */