xref: /original-bsd/local/toolchest/ksh/sh/name.c (revision 50dd0bba)
1 /*
2 
3  *      Copyright (c) 1984, 1985, 1986 AT&T
4  *      All Rights Reserved
5 
6  *      THIS IS UNPUBLISHED PROPRIETARY SOURCE
7  *      CODE OF AT&T.
8  *      The copyright notice above does not
9  *      evidence any actual or intended
10  *      publication of such source code.
11 
12  */
13 /*
14  * AT&T Bell Laboratories
15  *
16  */
17 
18 #include	"defs.h"
19 #include	"io.h"
20 #include	"flags.h"
21 #include	"name.h"
22 #include	"shtype.h"
23 #include	"sym.h"
24 #include	"stak.h"
25 #include	"brkincr.h"
26 #include	"builtins.h"
27 #include	"history.h"
28 #include	"timeout.h"
29 
30 /* This module defines the following routines */
31 NAMPTR	checkfor();
32 void	do_whence();
33 #ifdef ECHO_N
34 char	*echo_mode();
35 #endif	/* ECHO_N */
36 #ifdef apollo
37 void	ev_$set_var();
38 #endif	/* apollo */
39 int	genenv();
40 char	*heap();
41 void	meminit();
42 void	mem_scope();
43 void	mem_unscope();
44 char	*movstr();
45 void	name_unscope();
46 char	*qvalup();
47 void	prinscan();
48 int	printnam();
49 void	printflg();
50 int	readvar();
51 void	rmlocal();
52 char	**setenv();
53 void	setlist();
54 NAMPTR	setname();
55 int	syslook();
56 
57 /* This module references the following external routines */
58 extern void	assign();
59 extern char	*bracket_match();
60 extern unsigned	chkid();
61 #ifdef ECHO_N
62 extern MSG	echo_bin;
63 extern MSG	echo_opt;
64 #endif	/* ECHO_N */
65 extern char	**environ;
66 extern void	exitsh();
67 extern void	failed();
68 extern void	free();
69 extern NAMPTR	findnod();
70 extern char	*fullname();
71 extern char	*getstak();
72 extern struct Amemory *gettree();
73 extern void	gsort();
74 extern long	hist_list();
75 extern void	hist_flush();
76 extern void	initf();
77 extern void	gscan_all();
78 extern void	gscan_some();
79 extern NAMPTR	lookup();
80 extern void	linknod();
81 extern char	*mactrim();
82 extern char	*malloc();
83 extern char	*movstr();
84 extern unsigned	rand();
85 extern void	p_num();
86 extern void	p_str();
87 extern void	p_sub();
88 extern void	p_setout();
89 extern void	scan_all();
90 extern char	*simple();
91 extern int	srand();
92 extern char	*strchr();
93 extern char	*strcpy();
94 extern void	rmnval();
95 extern void	unassign();
96 extern long	time();
97 extern char	*utos();
98 extern char	*valup();
99 
100 static void	countnam();
101 #ifdef ECHO_N
102 static char	*echo_arg;
103 #endif	/* ECHO_N */
104 static long	get_rand();
105 static long	get_second();
106 static struct Amemory *inittree();
107 static void	no_export();
108 static void	pr_name();
109 static void	pushnam();
110 static void	pushnid();
111 static void	rehash();
112 static void	rm_node();
113 static int	set_second();
114 static int	set_rand();
115 static char	*staknam();
116 
117 
118 static rsflag;	/* used to see if "SHELL" has been set in the environment */
119 static int	namec;
120 static char	**argnam;
121 static struct Amemory	*namebase;
122 
123 struct Bfunction seconds = { get_second, set_second};
124 struct Bfunction randnum = { get_rand, set_rand};
125 
126 
127 
128 /* ========	variable and string handling	======== */
129 
130 /*
131  *  Table lookup routine
132  *  The table <syswds> is searched for string <w> and corresponding value is returned
133  */
134 syslook(w,syswds)
135 register char *w;
136 SYSTAB		syswds;
137 {
138 	register int first;
139 	register SYSPTR	syscan;
140 	register int c;
141 	if(w==0 || (first= *w)==0)
142 		return(0);
143 	syscan=syswds;
144 	while((c= *syscan->sysnam) && c <= first)
145 	{
146 		if(first == c && eq(w,syscan->sysnam))
147 			return(syscan->sysval);
148 		syscan++;
149 	}
150 	return(0);
151 }
152 
153 /*
154  * perform parameter assignment on an argument list
155  */
156 
157 void setlist(arg,xp)
158 register ARGPTR	arg;
159 register int xp;
160 {
161 	if(is_option(ALLEXP))
162 		xp |= N_EXPORT;
163 	while(arg)
164 	{
165 		register char *s;
166 		if(arg->argflag&A_MAC)
167 			s=mactrim(arg->argval,0);
168 		else
169 			s = arg->argval;
170 		setname(s, xp);
171 		arg=arg->argnxt;
172 		if(is_option(EXECPR))
173 		{
174 			p_setout(stderr);
175 			p_str(s,arg?SP:NL);
176 		}
177 	}
178 }
179 
180 /*
181  * Put <arg> into associative memory.
182  * If <xp> & V_FLAG then the alias list is used instead
183  * If <xp> & S_FLAG then use the current scope only
184  */
185 
186 
187 NAMPTR	setname(argi, xp)
188 char *argi;
189 int 	xp;
190 {
191 	register char *argscan=argi;
192 	register NAMPTR	n;
193 	register int sep = *argscan;
194 	char *sim;
195 	if(isalpha(sep) || ((xp&V_FLAG) && !expchar(sep)))
196 	{
197 		do
198 		{
199 			sep = *++argscan;
200 		}
201 		while(isalnum(sep));
202 		/* check for subscript*/
203 		if(sep=='[')
204 		{
205 			argscan = bracket_match(argscan)+1;
206 		}
207 		if((sep = *argscan) && sep!='=')
208 			failed(argi,notid);
209 		*argscan = 0;
210 		if(xp&V_FLAG)
211 		{
212 			n = findnod(argi,alias,1);
213 			if(attest(n,T_FLAG|NO_ALIAS))
214 				pattrib(n,~(NO_ALIAS|T_FLAG|N_EXPORT));
215 		}
216 		else if(xp&S_FLAG)
217 			/* scoped name must be in first tree */
218 			n = findnod(argi,namep,1);
219 		else
220 			n = lookup(argi);
221 		*argscan++ = sep;
222 		if(sep == '=')
223 		{
224 			if(n==PATHNOD || n==ENVNOD || n==SHELLNOD)
225 			{
226 				if(is_option(RSHFLG))
227 					failed(argi,restricted);
228 				if(n==SHELLNOD)
229 				{
230 					sim = simple(argscan);
231 					if(strchr(sim,'r') != NULL)
232 						rsflag = 0;	/* restricted shell */
233 				}
234 			}
235 			assign (n, argscan);
236 			attrib(n, xp&~(S_FLAG|V_FLAG));
237 #ifdef apollo
238 			if(attest(n,N_EXPORT) && attest(n,N_IMPORT)==0
239 				&& (xp&(S_FLAG|V_FLAG))==0)
240 			{
241 				short namlen,vallen;
242 				namlen =strlen(n->namid);
243 				vallen = strlen(argscan);
244 				ev_$set_var(n->namid,&namlen,argscan,&vallen);
245 			}
246 #endif /* apollo */
247 			if(n==PATHNOD)
248 			{
249 				gscan_some(rehash,alias,T_FLAG,T_FLAG);
250 #ifdef ECHO_N
251 				echo_arg = NIL;
252 #endif	/* ECHO_N */
253 			}
254 			if(n==VISINOD || ((n==EDITNOD)&&isnull(VISINOD)))
255 			{
256 				/* turn on vi or emacs option if editor name is either*/
257 				argscan = simple(argscan);
258 				if(gmatch(argscan,"*vi"))
259 				{
260 					off_option(EDITVI|EMACS|GMACS);
261 					on_option(EDITVI);
262 				}
263 				if(gmatch(argscan,"*macs"))
264 				{
265 					off_option(EDITVI|EMACS|GMACS);
266 					if(*argscan=='g')
267 						on_option(GMACS);
268 					else
269 						on_option(EMACS);
270 				}
271 			}
272 		}
273 		return(n);
274 	}
275 	failed (argi, notid);
276 	/* NOTREACHED */
277 }
278 
279 /*
280  * Mark each node is invalid
281  */
282 
283 static void rehash(np)
284 register NAMPTR np;
285 {
286 	attrib(np,NO_ALIAS);
287 }
288 
289 /*
290  *  alias each name to full path name
291  *  realias returns the pathname or NULL if not found
292  */
293 
294 char *realias(np)
295 register NAMPTR np;
296 {
297 	register char *sp;
298 	register char *vp = np->value.namval.cp;
299 	int flag = namflag(np)&(N_EXPORT|NO_ALIAS|T_FLAG);
300 	/* turn of T_FLAG to avoid recursion */
301 	pattrib(np,~(NO_ALIAS|T_FLAG));
302 	sp = fullname(np->namid);
303 	if(sp==NIL)
304 	{
305 		unassign(np);
306 		return(NIL);
307 	}
308 	else if(*sp!= '/')
309 	{
310 		sattrib(np,flag);
311 		return(NIL);
312 	}
313 	if(vp==0 || strcmp(sp,vp)!=0)
314 		assign(np,sp);
315 	/* turn T_FLAG back on */
316 	attrib(np,T_FLAG|N_EXPORT);
317 	return(sp);
318 }
319 
320 
321 int readvar(names,fd,raw)
322 register char **names;
323 FILE *fd;
324 {
325 	FILEBLK	fb;
326 	SHFILE f;
327 	register int c;
328 	int issep;
329 	register NAMPTR	n;
330 	int checksep = 1;		/* set when looking for separators */
331 	STKPTR	rel;
332 	char *seps;
333 	char is_eol;
334 	FILE *savef;
335 	states |= RWAIT;
336 	/* save in history file if S_FLAG is set */
337 	if((raw&S_FLAG) && fc_fix)
338 		states |= FIXFLG;
339 	raw &= R_FLAG;
340 	f = &fb;
341 	if(*names)
342 	{
343 		if(seps=strchr(*names,'?'))
344 			*seps = 0;
345 		n = lookup(*names++);
346 		if(seps)
347 			*seps = '?';
348 	}
349 	else
350 		n = REPLYNOD;
351 	rel=(STKPTR)relstak();
352 	seps = qvalup(IFSNOD);
353 	if(seps==NULL)
354 		seps = sptbnl;
355 	savef = input;
356 	if(fd==NULL)
357 		failed(bread,noquery);
358 	if(fd != input)
359 	{
360 		/* buffer the input stream if possible */
361 		if(fnobuff(fd) && (fd!=stdin || !ispipe(stdin)))
362 			setbuf(fd,malloc((unsigned)BUFSIZ));
363 		push(f);
364 		initf(fd);
365 	}
366 	while(1)
367 	{
368 		c = (raw?readc():nextc());
369 		issep = (strchr(seps,c)!=0);
370 		is_eol = eolchar(c);
371 		if(checksep && issep && !is_eol)
372 		{
373 			/* visable adjacent separators signify null fields*/
374 			if(strchr(sptbnl,c)!=0)
375 				continue;
376 		}
377 		checksep = 0;
378 		if((issep && *names) || is_eol)	/* from non-separator to separator */
379 		{
380 			if(*names==NULL && staktop>stakbot)
381 			{
382 				/* remove trailing separators */
383 				while(strchr(seps,*--staktop));
384 				staktop++;
385 			}
386 			zerostak();
387 			assign(n,absstak(rel)); setstak(rel);
388 			if(is_option(ALLEXP))
389 				attrib(n,N_EXPORT);
390 			n = (*names?lookup(*names++):0);
391 			if(is_eol)
392 				break;
393 			checksep = 1;
394 		}
395 		else 		/* not a separator */
396 			pushstak(c);
397 	}
398 	while(n)
399 	{
400 		assign(n, nullstr);
401 		if(is_option(ALLEXP))
402 			attrib(n,N_EXPORT);
403 		n = (*names?lookup(*names++):0);
404 	}
405 	if(savef != fd)
406 		pop(1);
407 	if(states&FIXFLG)
408 		hist_flush();
409 	states &= ~(RWAIT|PROMPT);
410 	states |= is_option(INTFLG);
411 	return;
412 }
413 
414 /*
415  * put string v onto the heap and return the heap pointer
416  */
417 
418 char *heap(v)
419 register char *v;
420 {
421 	register char *p;
422 	if(v)
423 	{
424 		movstr(v,p=malloc((unsigned)strlen(v)+1));
425 		return(p);
426 	}
427 	else
428 		return(0);
429 }
430 
431 
432 /*
433  * print out the name and value of a name-value pair <n>
434  */
435 
436 int printnam(n,flag)
437 register NAMPTR		n;
438 register int	flag;
439 {
440 	register FILE *fd;
441 	register char *s;
442 	union Namval *up= &n->value.namval;
443 	if(trapnote&SIGSET)
444 		exitsh(SIGFAIL);
445 	fd = output;
446 	if(attest(n,NO_ALIAS)==NO_ALIAS)
447 	{
448 		return(0);
449 	}
450 	if(is_afunction(n))
451 	{
452 		fputs(bltfn,fd);
453 		fputs(n->namid,fd);
454 		if(flag==0 && n->value.namval.rp->hoffset >=0 )
455 		{
456 			fputs(fn_hdr,fd);
457 			hist_list(n->value.namval.rp->hoffset,EOF,"\n");
458 		}
459 		else
460 			putc('\n',fd);
461 		return(n->namsz+1);
462 	}
463 	if(s=valup(n))
464 	{
465 		char numbuf[30];
466 		pr_name(n,0);
467 		flag = (flag?NL:'=');
468 	        if (attest (n, ARRAY))
469 		{
470 			if(attest(n,INT_GER))
471 			{
472 				/* copy to a save place */
473 				strcpy(numbuf,s);
474 				s = numbuf;
475 			}
476 			p_sub((int)up->aray->adot,flag);
477 		}
478 		else
479 			putc(flag,fd);
480 		if(flag != NL)
481 			p_str(s,NL);
482 		return(1);
483 	}
484 	return(0);
485 }
486 
487 /*
488  * print the name of a node followed by the character c
489  */
490 
491 static void pr_name(n,c)
492 register NAMPTR		n;
493 int c;
494 {
495 	register char *cp = strchr(n->namid,'=');
496 	if(cp)
497 		*cp = 0;
498 	p_str(n->namid,c);
499 	if(cp)
500 		*cp = '=';
501 }
502 
503 static void pushnid(np)
504 NAMPTR np;
505 {
506 	*argnam++ = np->namid;
507 	namec++;
508 	if(attest(np,ARRAY))
509 		arayp(np)->adot = arayp(np)->maxi;
510 }
511 
512 /*
513  * print the nodes in tree <root> which have attributes <flag> set
514  */
515 
516 void prinscan(file,flag,root,option)
517 FILE *file;
518 struct Amemory *root;
519 {
520 	register char **argv;
521 	register NAMPTR np;
522 	p_setout(file);
523 	argv = argnam  = (char**)locstak();
524 	namec = 0;
525 	if(flag)
526 		gscan_some(pushnid,root,flag,flag);
527 	else
528 		gscan_all(pushnid,root);
529 	gsort(argv,namec);
530 	while(namec--)
531 	{
532 		{
533 			register char *cp;
534 			if(cp = strchr(*argv,'='))
535 				*cp = 0;
536 			np = checkfor(*argv++,root);
537 			if(cp)
538 				*cp = '=';
539 		}
540 		if(np)
541 		{
542 			if(attest(np,ARRAY))
543 			{
544 				register struct Namaray *ap = arayp (np);
545 				register int i, imax;
546 				i = ap->adot = 0;
547 				imax = ap->maxi;
548 				for (; i <= imax; i++)
549 				{
550 					ap->adot = i;
551 					if (ap->val[i])
552 						printnam(np,option);
553 				}
554 			}
555 			else
556 				printnam(np,option);
557 		}
558 	}
559 }
560 
561 static char *staknam(n,value)
562 char *	value;
563 register NAMPTR	n;
564 {
565 	register char *p,*q;
566 	q = getstak(strlen(n->namid)+strlen(value)+2);
567 	p=movstr(n->namid,q);
568 	*p++ = '=';
569 	strcpy(p,value);
570 	return(q);
571 }
572 
573 
574 void	printflg(n)
575 register NAMPTR		n;
576 {
577 	register SYSPTR	syscan;
578 	register int val;
579 	if (namflag(n) != N_DEFAULT)
580 	{
581 		syscan=attributes;
582 		while(*syscan->sysnam)
583 		{
584 			val = syscan->sysval;
585 			if(attest(n,val)==val)
586 			{
587 				p_str(syscan->sysnam,SP);
588 		                if (attest (n, L_JUST|R_JUST|Z_FILL))
589 					p_num(n->namsz,SP);
590 				if(val == (BLT_NOD|INT_GER))
591 					break;
592 			}
593 		        if(val == INT_GER && attest(n,INT_GER))
594 			{
595 				if(n->namsz != 10)
596 				{
597 					p_str(intbase,SP);
598 					p_num(n->namsz,SP);
599 				}
600 				break;
601 			}
602 			syscan++;
603 		}
604 		pr_name(n,NL);
605 	}
606 }
607 
608 int	genenv()
609 {
610 	register char **e=environ;
611 	register NAMPTR		n;
612 	rsflag = 1;
613 	if(e)
614 	{
615 		while(*e)
616 		{
617 			n = setname(*e, (N_IMPORT|N_EXPORT));
618 			n->namid = *e++;
619 		}
620 	}
621 	return(rsflag);
622 }
623 
624 
625 static void	countnam()
626 {
627 	namec++;
628 }
629 
630 
631 static void	pushnam(n)
632 register NAMPTR		n;
633 {
634 	register char *value;
635 	if(attest(n,N_IMPORT))
636 		*argnam++ = n->namid;
637 	else if(value=valup(n))
638 		*argnam++ = staknam(n,value);
639 }
640 
641 /*
642  * Generate the environment list for the child.
643  */
644 
645 
646 char **setenv()
647 {
648 	register char **er;
649 	namec = 0;
650 	/* L_ARGNOD gets generated automatically as full path name of command */
651 	pattrib(L_ARGNOD,~N_EXPORT);
652 	gscan_some (countnam,namep, N_EXPORT|N_IMPORT, N_EXPORT);
653 	er = (char**)getstak((namec+2)*BYTESPERWORD);
654 	argnam = ++er;
655 	gscan_some (pushnam, namep, N_EXPORT|N_IMPORT, N_EXPORT);
656 	*argnam++ = 0;
657 	return(er);
658 }
659 
660 /*
661  * Initialize the shell name and alias table
662  */
663 
664 void meminit()
665 {
666 	register NAMPTR np;
667 	bltin_nodes = (NAMPTR)malloc((unsigned)(NNODES*sizeof(struct Namnod)));
668 	namebase = namep = inittree(node_names,bltin_nodes,0);
669 	/* set up random number generator */
670 #ifdef apollo
671 	(PPIDNOD)->value.namval.cp = (char*)(&ppid);
672 	(L_ARGNOD)->value.namval.cp = (char*)(&lastarg);
673 	(TMOUTNOD)->value.namval.cp = (char*)(&timeout);
674 	(SECONDS)->value.namval.cp = (char*)(&seconds);
675 	(MCHKNOD)->value.namval.cp = (char*)(&mailchk);
676 	(RANDNOD)->value.namval.cp = (char*)(&randnum);
677 #endif	/* apollo */
678 	namflag(RANDNOD) = N_FREE|INT_GER|BLT_NOD;
679 	/* set up the seconds clock */
680 	namflag(SECONDS) = N_FREE|INT_GER|BLT_NOD;
681 	set_second(0L);
682 	namflag(MCHKNOD) = N_FREE|INT_GER;
683 	namflag(TMOUTNOD) = INT_GER;
684 	namflag(PPIDNOD) = (N_FREE|INT_GER|N_RDONLY);
685 	namflag(L_ARGNOD) = N_FREE|IN_DIR;
686 	np = (NAMPTR)malloc(NALIAS*sizeof(struct Namnod));
687 	alias = inittree(alias_names,np,N_EXPORT);
688 	prnames = gettree(MEMSIZE/4);
689 }
690 
691 /*
692  * re-initialize name-value pairs after fork
693  */
694 
695 
696 static struct Amemory *inittree(name_vals,nodes,atflag)
697 struct name_value *name_vals;
698 NAMPTR nodes;
699 {
700 	register struct Amemory *treep;
701 	register NAMPTR np;
702 	register struct name_value *nv;
703 	int flag;
704 	treep = gettree (MEMSIZE);
705 	for(np=nodes,nv=name_vals;*nv->nv_name;nv++,np++)
706 	{
707 		np->namid = nv->nv_name;
708 		np->value.namval.cp = nv->nv_value;
709 		flag = 0;
710 #ifdef apollo
711 		if(*nv->nv_value==0)
712 			np->value.namval.cp = 0;
713 		else
714 #else
715 		if(nv->nv_value)
716 #endif	/* apollo */
717 		{
718 			flag = atflag|N_FREE;
719 			if(atflag && *nv->nv_value=='/')
720 				flag |= T_FLAG;
721 		}
722 		sattrib(np,flag);
723 		np->namsz = 10;
724 		linknod (np, treep);
725 	}
726 	return(treep);
727 }
728 
729 /*
730  * create a new environment scope
731  */
732 
733 void mem_scope(envlist)
734 ARGPTR envlist;
735 {
736 	register struct Amemory *sav_namep = namep;
737 	register struct Amemory *newscope;
738 	newscope = gettree(MEMSIZE/8);
739 	newscope->nexttree = sav_namep;
740 	namep = newscope;
741 	setlist(envlist,N_EXPORT|S_FLAG);
742 	newscope->nexttree = NULL;
743 	namep = sav_namep;
744 	scan_all(no_export,newscope);
745 	newscope->nexttree = sav_namep;
746 	namep = newscope;
747 }
748 
749 /*
750  * Temporarily remove name from export list of previous scopes
751  */
752 
753 static void no_export(nnod)
754 register struct Namnod *nnod;
755 {
756 	register struct Namnod *np = checkfor(nnod->namid,namep);
757 	if(np && attest(np,N_EXPORT))
758 	{
759 		pattrib(np,~N_EXPORT);
760 		attrib(np,E_FLAG);
761 	}
762 }
763 
764 /*
765  * free up top environment scope
766  */
767 
768 void mem_unscope()
769 {
770 	register struct Amemory *ap = namep;
771 	if((namep = ap->nexttree)==NULL)
772 		namep = namebase;
773 	scan_all(rm_node,ap);
774 	free((char*)ap);
775 }
776 
777 /*
778  * free up all environment scopes except the first
779  */
780 
781 void name_unscope()
782 {
783 	while(namep->nexttree)
784 		mem_unscope();
785 }
786 
787 /*
788  * Remove a node and free up all the space
789  * Restate export attribute for hidden nodes if necessary
790  */
791 static void rm_node(nnod)
792 register struct Namnod *nnod;
793 {
794 	register struct Namnod *np = checkfor(nnod->namid,namep);
795 	if(np && attest(np,E_FLAG))
796 	{
797 		pattrib(np,~E_FLAG);
798 		attrib(np,N_EXPORT);
799 	}
800 	pattrib(nnod,~N_EXPORT);
801 	rmlocal(nnod);
802 	free((char*)nnod);
803 }
804 
805 /*
806  * Remove freeable local space associated with the namval field
807  * of nnod. This includes any strings representing the value(s) of the
808  * node, as well as its dope vector, if it is an array.
809  */
810 
811 void	rmlocal (nnod)
812 register struct Namnod *nnod;
813 {
814 	register int i;
815 	register struct Nodval *nv;
816 	register struct Namaray *ap;
817 	register union Namval *up = &nnod->value.namval;
818 
819 	/* return if the node is global or unfreeable */
820 
821 	if (attest (nnod, N_EXPORT|N_FREE))
822 		return;
823 	/*
824 	 * If the node is a freeable array, then free both the stringspace
825 	 * associated with it and its dope vector.
826 	 */
827 
828 	else if (attest (nnod, ARRAY))
829 	{
830 	        ap = up->aray;
831 		i = ap->maxi;
832 		while(i >= 0)
833 		{
834 			nv = ap->val[i--];
835 			if (nv)
836 			{
837 	                	if (freeble (nv))
838 					rmnval (unmark (nv));
839 				else
840 				{
841 					up = &nv->namval;
842 					if ((up->cp) && ((nv->namflg & (N_FREE|N_ALLOC)) == 0))
843 						free ((nv->namflg & IN_DIR)?up->up->cp:up->cp);
844 				}
845 			}
846 		}
847 		free ((char*)(arayp(nnod)));
848 		nnod->value.namval.cp = NULL;
849 	}
850 	/*
851 	 * otherwise node is a freeable scalar, so free the string
852 	 * representing its value.
853 	 */
854 	else
855 	{
856 		unassign (nnod);
857 	}
858 	sattrib (nnod, N_DEFAULT);
859 }
860 
861 /*
862  * Get the value of a built-in node
863  * A lookup may not be necessary
864  */
865 
866 char *qvalup(n)
867 register NAMPTR	n;
868 {
869 	if(namep->nexttree)
870 		n = lookup((node_names+(n-bltin_nodes))->nv_name);
871 	return(valup(n));
872 }
873 
874 /*
875  * lookup name in trees root and return Namnod pointer with this name.
876  * If none exists, it will not be created.
877  */
878 
879 NAMPTR checkfor(name,root)
880 char *name;
881 struct Amemory *root;
882 {
883 	register struct Namnod *np = NULL;
884 	register struct Amemory *app = root;
885 	struct Namnod *findnod();
886 	while(app && np==NULL)
887 	{
888 		np = findnod(name,app,0);
889 		app = app->nexttree;
890 	}
891 	return((np==NULL||isnull(np))?NULL:np);
892 }
893 
894 /*
895  *  for the whence command
896  */
897 
898 void	do_whence(com,flag)
899 char **com;
900 register int flag;
901 {
902 	register char *a1;
903 	register struct Namnod *np;
904 	register char *cp;
905 	struct Namnod *fp;
906 	while(a1 = *++com)
907 	{
908 		if(flag)
909 			fputs(a1,output);
910 		np = NULL;
911 		/* reserved words first */
912 		if(syslook(a1,reserved))
913 		{
914 			if(flag)
915 				a1 = is_reserved;
916 		}
917 		/* non-tracked aliases */
918 		else if((np=findnod(a1,alias,CHK_FOR)) && !isnull(np)
919 			 && attest(np,T_FLAG)==0 && (a1=valup(np)))
920 		{
921 			if(flag)
922 			{
923 				if(attest(np,N_EXPORT))
924 					cp = is_xalias;
925 				else
926 					cp = is_alias;
927 				fputs(cp,output);
928 			}
929 		}
930 		/* built-in commands next */
931 		else if(syslook(a1,commands))
932 		{
933 			if(flag)
934 				a1 = is_builtin;
935 		}
936 		/* functions next */
937 		else if((fp=findnod(a1,prnames,CHK_FOR))&& !isnull(fp))
938 		{
939 			if(flag)
940 		 		a1=attest(fp,N_EXPORT)?is_xfunction:is_function;
941 		}
942 		else
943 		{
944 			/* find full pathname */
945 			a1 = fullname(a1);
946 			if(a1)
947 			{
948 				if(flag)
949 				{
950 					/* tracked aliases next */
951 					if(np && attest(np,T_FLAG) && *a1 == '/')
952 						fputs(is_talias,output);
953 					else
954 						fputs(is_,output);
955 				}
956 			}
957 			else
958 			{
959 				a1 = (flag?notfound:nullstr);
960 				exitval |= 1;
961 			}
962 		}
963 		p_str(a1,NL);
964 	}
965 }
966 
967 /*
968  * these functions are used to get and set the SECONDS variable
969  */
970 
971 static long sec_offset;
972 
973 static int set_second(n)
974 long n;
975 {
976 	sec_offset =  time((long*)0) - n ;
977 }
978 
979 static long get_second()
980 {
981 	return(time((long*)0)-sec_offset);
982 }
983 
984 /*
985  * These functions are used to get and set the RANDOM variable
986  */
987 
988 static int set_rand(n)
989 long n;
990 {
991 	srand((int)n);
992 }
993 
994 static long get_rand()
995 {
996 	return((long)rand());
997 }
998 
999 #ifdef ECHO_N
1000 char *echo_mode()
1001 {
1002 	register char *cp;
1003 	optflag savopts;
1004 	if(echo_arg==0)
1005 	{
1006 #ifdef apollo
1007 		register NAMPTR np = checkfor("SYSTYPE",namep);
1008 		if(np && (cp=valup(np)))
1009 		{
1010 			echo_arg = (*cp=='b'?echo_opt:minus);
1011 			return(echo_arg);
1012 		}
1013 #endif /* apollo */
1014 		savopts = flags;
1015 		off_option(HASHALL);
1016 		cp = fullname(echo_bin+5);
1017 		flags = savopts;
1018 		if(eq(cp,echo_bin))
1019 			echo_arg = echo_opt;
1020 		else
1021 			echo_arg = minus;
1022 	}
1023 	return(echo_arg);
1024 }
1025 #endif	/* ECHO_N */
1026