1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1982-2010 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                  Common Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*            http://www.opensource.org/licenses/cpl1.0.txt             *
11*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                  David Korn <dgk@research.att.com>                   *
18*                                                                      *
19***********************************************************************/
20#pragma prototyped
21
22/*
23 * code for tree nodes and name walking
24 *
25 *   David Korn
26 *   AT&T Labs
27 *
28 */
29
30#include	"defs.h"
31#include	"name.h"
32#include	"argnod.h"
33#include	"lexstates.h"
34
35struct nvdir
36{
37	Dt_t		*root;
38	Namval_t	*hp;
39	Namval_t	*table;
40	Namval_t	*otable;
41	Namval_t	*(*nextnode)(Namval_t*,Dt_t*,Namfun_t*);
42	Namfun_t	*fun;
43	struct nvdir	*prev;
44	int		len;
45	char		data[1];
46};
47
48char *nv_getvtree(Namval_t*, Namfun_t *);
49static void put_tree(Namval_t*, const char*, int,Namfun_t*);
50static char *walk_tree(Namval_t*, Namval_t*, int);
51
52static int read_tree(Namval_t* np, Sfio_t *iop, int n, Namfun_t *dp)
53{
54	Sfio_t	*sp;
55	char	*cp;
56	int	c;
57	if(n>=0)
58		return(-1);
59	while((c = sfgetc(iop)) &&  isblank(c));
60	sfungetc(iop,c);
61	sfprintf(sh.strbuf,"%s=%c",nv_name(np),0);
62	cp = sfstruse(sh.strbuf);
63	sp = sfopen((Sfio_t*)0,cp,"s");
64	sfstack(iop,sp);
65	c=sh_eval(iop,SH_READEVAL);
66	return(c);
67}
68
69static Namval_t *create_tree(Namval_t *np,const char *name,int flag,Namfun_t *dp)
70{
71	register Namfun_t *fp=dp;
72	fp->dsize = 0;
73	while(fp=fp->next)
74	{
75		if(fp->disc && fp->disc->createf)
76		{
77			if(np=(*fp->disc->createf)(np,name,flag,fp))
78				dp->last = fp->last;
79			return(np);
80		}
81	}
82	return((flag&NV_NOADD)?0:np);
83}
84
85static Namfun_t *clone_tree(Namval_t *np, Namval_t *mp, int flags, Namfun_t *fp){
86	Namfun_t	*dp;
87	if ((flags&NV_MOVE) && nv_type(np))
88		return(fp);
89	dp = nv_clone_disc(fp,flags);
90	if((flags&NV_COMVAR) && !(flags&NV_RAW))
91	{
92		walk_tree(np,mp,flags);
93		if((flags&NV_MOVE) && !(fp->nofree&1))
94			free((void*)fp);
95	}
96	return(dp);
97}
98
99static const Namdisc_t treedisc =
100{
101	0,
102	put_tree,
103	nv_getvtree,
104	0,
105	0,
106	create_tree,
107	clone_tree
108	,0,0,0,
109	read_tree
110};
111
112static char *nextdot(const char *str)
113{
114	register char *cp;
115	register int c;
116	if(*str=='.')
117		str++;
118	for(cp=(char*)str;c= *cp; cp++)
119	{
120		if(c=='[')
121		{
122			cp = nv_endsubscript((Namval_t*)0,(char*)cp,0);
123			return(*cp=='.'?cp:0);
124		}
125		if(c=='.')
126			return(cp);
127	}
128	return(0);
129}
130
131static  Namfun_t *nextdisc(Namval_t *np)
132{
133	register Namfun_t *fp;
134	if(nv_isref(np))
135		return(0);
136        for(fp=np->nvfun;fp;fp=fp->next)
137	{
138		if(fp && fp->disc && fp->disc->nextf)
139			return(fp);
140	}
141	return(0);
142}
143
144void *nv_diropen(Namval_t *np,const char *name)
145{
146	char *next,*last;
147	int c,len=strlen(name);
148	struct nvdir *save, *dp = new_of(struct nvdir,len);
149	Namval_t *nq=0,fake;
150	Namfun_t *nfp=0;
151	if(!dp)
152		return(0);
153	memset((void*)dp, 0, sizeof(*dp));
154	if(name[len-1]=='*' || name[len-1]=='@')
155		len -= 1;
156	name = memcpy(dp->data,name,len);
157	dp->data[len] = 0;
158	dp->len = len;
159	dp->root = sh.last_root?sh.last_root:sh.var_tree;
160#if 1
161	while(1)
162	{
163		dp->table = sh.last_table;
164		sh.last_table = 0;
165		if(*(last=(char*)name)==0)
166			break;
167		if(!(next=nextdot(last)))
168			break;
169		*next = 0;
170		np = nv_open(name, dp->root, NV_NOFAIL);
171		*next = '.';
172		if(!np || !nv_istable(np))
173			break;
174		dp->root = nv_dict(np);
175		name = next+1;
176	}
177#else
178	dp->table = sh.last_table;
179	sh.last_table = 0;
180	last = dp->data;
181#endif
182	if(*name)
183	{
184		fake.nvname = (char*)name;
185		if(dp->hp = (Namval_t*)dtprev(dp->root,&fake))
186		{
187			char *cp = nv_name(dp->hp);
188			c = strlen(cp);
189			if(memcmp(name,cp,c) || name[c]!='[')
190				dp->hp = (Namval_t*)dtnext(dp->root,dp->hp);
191			else
192			{
193				np = dp->hp;
194				last = 0;
195			}
196		}
197		else
198			dp->hp = (Namval_t*)dtfirst(dp->root);
199	}
200	else
201		dp->hp = (Namval_t*)dtfirst(dp->root);
202	while(1)
203	{
204		if(!last)
205			next = 0;
206		else if(next= nextdot(last))
207		{
208			c = *next;
209			*next = 0;
210		}
211		if(!np)
212		{
213			if(nfp && nfp->disc && nfp->disc->createf)
214			{
215				np =  (*nfp->disc->createf)(nq,last,0,nfp);
216				if(*nfp->last == '[')
217				{
218					nv_endsubscript(np,nfp->last,NV_NOADD);
219					if(nq = nv_opensub(np))
220						np = nq;
221				}
222			}
223			else
224				np = nv_search(last,dp->root,0);
225		}
226		if(next)
227			*next = c;
228		if(np==dp->hp && !next)
229			dp->hp = (Namval_t*)dtnext(dp->root,dp->hp);
230		if(np && ((nfp=nextdisc(np)) || nv_istable(np)))
231		{
232			if(!(save = new_of(struct nvdir,0)))
233				return(0);
234			*save = *dp;
235			dp->prev = save;
236			if(nv_istable(np))
237				dp->root = nv_dict(np);
238			else
239				dp->root = (Dt_t*)np;
240			if(nfp)
241			{
242				dp->nextnode = nfp->disc->nextf;
243				dp->table = np;
244				dp->otable = sh.last_table;
245				dp->fun = nfp;
246				dp->hp = (*dp->nextnode)(np,(Dt_t*)0,nfp);
247			}
248			else
249				dp->nextnode = 0;
250		}
251		else
252			break;
253		if(!next || next[1]==0)
254			break;
255		last = next+1;
256		nq = np;
257		np = 0;
258	}
259	return((void*)dp);
260}
261
262
263static Namval_t *nextnode(struct nvdir *dp)
264{
265	if(dp->nextnode)
266		return((*dp->nextnode)(dp->hp,dp->root,dp->fun));
267	if(dp->len && memcmp(dp->data, dp->hp->nvname, dp->len))
268		return(0);
269	return((Namval_t*)dtnext(dp->root,dp->hp));
270}
271
272char *nv_dirnext(void *dir)
273{
274	register struct nvdir *save, *dp = (struct nvdir*)dir;
275	register Namval_t *np, *last_table;
276	register char *cp;
277	Namfun_t *nfp;
278	Namval_t *nq;
279	while(1)
280	{
281		while(np=dp->hp)
282		{
283#if 0
284			char *sptr;
285#endif
286			if(nv_isarray(np))
287				nv_putsub(np,(char*)0, ARRAY_UNDEF);
288			dp->hp = nextnode(dp);
289			if(nv_isnull(np) && !nv_isarray(np) && !nv_isattr(np,NV_INTEGER))
290				continue;
291			last_table = sh.last_table;
292#if 0
293			if(dp->table && dp->otable && !nv_isattr(dp->table,NV_MINIMAL))
294			{
295				sptr = dp->table->nvenv;
296				dp->table->nvenv = (char*)dp->otable;
297			}
298#endif
299			sh.last_table = dp->table;
300			cp = nv_name(np);
301#if 0
302			if(dp->table && dp->otable && !nv_isattr(dp->table,NV_MINIMAL))
303				dp->table->nvenv = sptr;
304#endif
305			if(dp->nextnode && !dp->hp && (nq = (Namval_t*)dp->table))
306			{
307				Namarr_t  *ap = nv_arrayptr(nq);
308				if(ap && (ap->nelem&ARRAY_SCAN) && nv_nextsub(nq))
309					dp->hp = (*dp->nextnode)(np,(Dt_t*)0,dp->fun);
310			}
311			sh.last_table = last_table;
312			if(!dp->len || memcmp(cp,dp->data,dp->len)==0)
313			{
314				if((nfp=nextdisc(np)) && (nfp->disc->getval||nfp->disc->getnum) && nv_isvtree(np) && strcmp(cp,dp->data))
315					nfp = 0;
316				if(nfp || nv_istable(np))
317				{
318					Dt_t *root;
319					if(nv_istable(np))
320						root = nv_dict(np);
321					else
322						root = (Dt_t*)np;
323					/* check for recursive walk */
324					for(save=dp; save;  save=save->prev)
325					{
326						if(save->root==root)
327							break;
328					}
329					if(save)
330						return(cp);
331					if(!(save = new_of(struct nvdir,0)))
332						return(0);
333					*save = *dp;
334					dp->prev = save;
335					dp->root = root;
336					dp->len = 0;
337					if(nfp && np->nvfun)
338					{
339#if 0
340				                Namarr_t *ap = nv_arrayptr(np);
341				                if(ap && (ap->nelem&ARRAY_UNDEF))
342				                        nv_putsub(np,(char*)0,ARRAY_SCAN);
343#endif
344						dp->nextnode = nfp->disc->nextf;
345						dp->otable = dp->table;
346						dp->table = np;
347						dp->fun = nfp;
348						dp->hp = (*dp->nextnode)(np,(Dt_t*)0,nfp);
349					}
350					else
351						dp->nextnode = 0;
352				}
353				return(cp);
354			}
355		}
356		if(!(save=dp->prev))
357			break;
358		*dp = *save;
359		free((void*)save);
360	}
361	return(0);
362}
363
364void nv_dirclose(void *dir)
365{
366	struct nvdir *dp = (struct nvdir*)dir;
367	if(dp->prev)
368		nv_dirclose((void*)dp->prev);
369	free(dir);
370}
371
372static void outtype(Namval_t *np, Namfun_t *fp, Sfio_t* out, const char *prefix)
373{
374	char *type=0;
375	Namval_t *tp = fp->type;
376	if(!tp && fp->disc && fp->disc->typef)
377		tp = (*fp->disc->typef)(np,fp);
378	for(fp=fp->next;fp;fp=fp->next)
379	{
380		if(fp->type || (fp->disc && fp->disc->typef &&(*fp->disc->typef)(np,fp)))
381		{
382			outtype(np,fp,out,prefix);
383			break;
384		}
385	}
386	if(prefix && *prefix=='t')
387		type = "-T";
388	else if(!prefix)
389		type = "type";
390	if(type)
391	{
392		char *cp=tp->nvname;
393		if(cp=strrchr(cp,'.'))
394			cp++;
395		else
396			cp = tp->nvname;
397		sfprintf(out,"%s %s ",type,cp);
398	}
399}
400
401/*
402 * print the attributes of name value pair give by <np>
403 */
404void nv_attribute(register Namval_t *np,Sfio_t *out,char *prefix,int noname)
405{
406	register const Shtable_t *tp;
407	register char *cp;
408	register unsigned val,mask,attr;
409	char *ip=0;
410	Namfun_t *fp=0;
411	Namval_t *typep=0;
412	for(fp=np->nvfun;fp;fp=fp->next)
413	{
414		if((typep=fp->type) || (fp->disc && fp->disc->typef && (typep=(*fp->disc->typef)(np,fp))))
415			break;
416	}
417	if(!fp  && !nv_isattr(np,~(NV_MINIMAL|NV_NOFREE)))
418	{
419		if(prefix && *prefix)
420		{
421			if(nv_isvtree(np))
422				sfprintf(out,"%s -C ",prefix);
423			else if((!np->nvalue.cp||np->nvalue.cp==Empty) && nv_isattr(np,~NV_NOFREE)==NV_MINIMAL && strcmp(np->nvname,"_"))
424				sfputr(out,prefix,' ');
425		}
426		return;
427	}
428
429	if ((attr=nv_isattr(np,~NV_NOFREE)) || fp)
430	{
431		if((attr&(NV_NOPRINT|NV_INTEGER))==NV_NOPRINT)
432			attr &= ~NV_NOPRINT;
433		if(!attr && !fp)
434			return;
435		if(fp)
436		{
437			prefix = Empty;
438			attr &= NV_RDONLY|NV_ARRAY;
439			if(nv_isattr(np,NV_REF|NV_TAGGED)==(NV_REF|NV_TAGGED))
440				attr |= (NV_REF|NV_TAGGED);
441			if(typep)
442			{
443				char *cp = typep->nvname;
444				if(cp = strrchr(cp,'.'))
445					cp++;
446				else
447					cp = typep->nvname;
448				sfputr(out,cp,' ');
449				fp = 0;
450			}
451		}
452		else if(prefix && *prefix)
453			sfputr(out,prefix,' ');
454		for(tp = shtab_attributes; *tp->sh_name;tp++)
455		{
456			val = tp->sh_number;
457			mask = val;
458			if(fp && (val&NV_INTEGER))
459				break;
460			/*
461			 * the following test is needed to prevent variables
462			 * with E attribute from being given the F
463			 * attribute as well
464			*/
465			if(val==NV_DOUBLE && (attr&(NV_EXPNOTE|NV_HEXFLOAT)))
466				continue;
467			if(val&NV_INTEGER)
468				mask |= NV_DOUBLE;
469			else if(val&NV_HOST)
470				mask = NV_HOST;
471			if((attr&mask)==val)
472			{
473				if(val==NV_ARRAY)
474				{
475					Namarr_t *ap = nv_arrayptr(np);
476					char **xp=0;
477					if(ap && array_assoc(ap))
478					{
479						if(tp->sh_name[1]!='A')
480							continue;
481					}
482					else if(tp->sh_name[1]=='A')
483						continue;
484					if((ap && (ap->nelem&ARRAY_TREE)) || (!ap && nv_isattr(np,NV_NOFREE)))
485					{
486						if(prefix && *prefix)
487							sfwrite(out,"-C ",3);
488					}
489					if(ap && !array_assoc(ap) && (xp=(char**)(ap+1)) && *xp)
490						ip = nv_namptr(*xp,0)->nvname;
491				}
492				if(prefix)
493				{
494					if(*tp->sh_name=='-')
495						sfprintf(out,"%.2s ",tp->sh_name);
496					if(ip)
497					{
498						sfprintf(out,"[%s] ",ip);
499						ip = 0;
500					}
501				}
502				else
503					sfputr(out,tp->sh_name+2,' ');
504		                if ((val&(NV_LJUST|NV_RJUST|NV_ZFILL)) && !(val&NV_INTEGER) && val!=NV_HOST)
505					sfprintf(out,"%d ",nv_size(np));
506				if(val==(NV_REF|NV_TAGGED))
507					attr &= ~(NV_REF|NV_TAGGED);
508			}
509		        if(val==NV_INTEGER && nv_isattr(np,NV_INTEGER))
510			{
511				if(nv_size(np) != 10)
512				{
513					if(nv_isattr(np, NV_DOUBLE)== NV_DOUBLE)
514						cp = "precision";
515					else
516						cp = "base";
517					if(!prefix)
518						sfputr(out,cp,' ');
519					sfprintf(out,"%d ",nv_size(np));
520				}
521				break;
522			}
523		}
524		if(fp)
525			outtype(np,fp,out,prefix);
526		if(noname)
527			return;
528		sfputr(out,nv_name(np),'\n');
529	}
530}
531
532struct Walk
533{
534	Sfio_t	*out;
535	Dt_t	*root;
536	int	noscope;
537	int	indent;
538	int	nofollow;
539	int	array;
540	int	flags;
541};
542
543void nv_outnode(Namval_t *np, Sfio_t* out, int indent, int special)
544{
545	char		*fmtq,*ep,*xp;
546	Namval_t	*mp;
547	Namarr_t	*ap = nv_arrayptr(np);
548	int		tabs=0,c,more,associative = 0;
549	if(ap)
550	{
551		if(!(ap->nelem&ARRAY_SCAN))
552			nv_putsub(np,NIL(char*),ARRAY_SCAN);
553		sfputc(out,'(');
554		if(indent>=0)
555		{
556			sfputc(out,'\n');
557			tabs=1;
558		}
559		if(!(associative =(array_assoc(ap)!=0)))
560		{
561			if(array_elem(ap) < nv_aimax(np)+1)
562				associative=1;
563		}
564	}
565	mp = nv_opensub(np);
566	while(1)
567	{
568		if(mp && special && nv_isvtree(mp))
569		{
570			if(!nv_nextsub(np))
571				break;
572			mp = nv_opensub(np);
573			continue;
574		}
575		if(tabs)
576			sfnputc(out,'\t',++indent);
577		tabs=0;
578		if(associative||special)
579		{
580			if(!(fmtq = nv_getsub(np)))
581				break;
582			sfprintf(out,"[%s]",sh_fmtq(fmtq));
583			sfputc(out,'=');
584		}
585		if(mp && nv_isarray(mp))
586		{
587			nv_outnode(mp, out, indent+(indent>=0),0);
588			if(indent>0)
589				sfnputc(out,'\t',indent);
590			sfputc(out,')');
591			sfputc(out,indent>=0?'\n':' ');
592			more = nv_nextsub(np);
593			goto skip;
594		}
595		if(mp && nv_isvtree(mp))
596			nv_onattr(mp,NV_EXPORT);
597		ep = nv_getval(mp?mp:np);
598		if(ep==Empty)
599			ep = 0;
600		xp = 0;
601		if(!ap && nv_isattr(np,NV_INTEGER|NV_LJUST)==NV_LJUST)
602		{
603			xp = ep+nv_size(np);
604			while(--xp>ep && *xp==' ');
605			if(xp>ep || *xp!=' ')
606				xp++;
607			if(xp < (ep+nv_size(np)))
608				*xp = 0;
609			else
610				xp = 0;
611		}
612		if(mp && nv_isvtree(mp))
613			fmtq = ep;
614		else if(!(fmtq = sh_fmtq(ep)))
615			fmtq = "";
616		else if(!associative && (ep=strchr(fmtq,'=')))
617		{
618			char *qp = strchr(fmtq,'\'');
619			if(!qp || qp>ep)
620			{
621				sfwrite(out,fmtq,ep-fmtq);
622				sfputc(out,'\\');
623				fmtq = ep;
624			}
625		}
626		more = nv_nextsub(np);
627		c = '\n';
628		if(indent<0)
629		{
630			c = ';';
631			if(ap)
632				c = more?' ':-1;
633		}
634		sfputr(out,fmtq,c);
635		if(xp)
636			*xp = ' ';
637	skip:
638		if(!more)
639			return;
640		mp = nv_opensub(np);
641		if(indent>0 && !(mp && special && nv_isvtree(mp)))
642			sfnputc(out,'\t',indent);
643	}
644}
645
646static void outval(char *name, const char *vname, struct Walk *wp)
647{
648	register Namval_t *np, *nq;
649        register Namfun_t *fp;
650	int isarray=0, special=0,mode=0;
651	if(*name!='.' || vname[strlen(vname)-1]==']')
652		mode = NV_ARRAY;
653	if(!(np=nv_open(vname,wp->root,mode|NV_VARNAME|NV_NOADD|NV_NOASSIGN|NV_NOFAIL|wp->noscope)))
654		return;
655	fp = nv_hasdisc(np,&treedisc);
656	if(*name=='.')
657	{
658		if(nv_isattr(np,NV_BINARY))
659			return;
660		if(fp && np->nvalue.cp && np->nvalue.cp!=Empty)
661		{
662			nv_local = 1;
663			fp = 0;
664		}
665		if(fp)
666			return;
667		if(nv_isarray(np))
668			return;
669	}
670	if(!special && fp && !nv_isarray(np))
671	{
672		Namfun_t *xp;
673		if(!wp->out)
674		{
675			fp = nv_stack(np,fp);
676			if(fp = nv_stack(np,NIL(Namfun_t*)))
677				free((void*)fp);
678			np->nvfun = 0;
679			return;
680		}
681		for(xp=fp->next; xp; xp = xp->next)
682		{
683			if(xp->disc && (xp->disc->getval || xp->disc->getnum))
684				break;
685		}
686		if(!xp)
687			return;
688	}
689	if(nv_isnull(np) && !nv_isarray(np) && !nv_isattr(np,NV_INTEGER))
690		return;
691	if(special || (nv_isarray(np) && nv_arrayptr(np)))
692	{
693		isarray=1;
694		if(array_elem(nv_arrayptr(np))==0)
695			isarray=2;
696		else
697			nq = nv_putsub(np,NIL(char*),ARRAY_SCAN|(wp->out?ARRAY_NOCHILD:0));
698	}
699	if(!wp->out)
700	{
701		_nv_unset(np,NV_RDONLY);
702		nv_close(np);
703#if 0
704		if(sh.subshell==0 && !(wp->flags&NV_RDONLY) && !nv_isattr(np,NV_MINIMAL|NV_NOFREE))
705			nv_delete(np,wp->root,0);
706#endif
707		return;
708	}
709	if(isarray==1 && !nq)
710	{
711		sfputc(wp->out,'(');
712		if(wp->indent>=0)
713			sfputc(wp->out,'\n');
714		return;
715	}
716	if(isarray==0 && nv_isarray(np) && nv_isnull(np))  /* empty array */
717		isarray = 2;
718	special |= wp->nofollow;
719	if(!wp->array && wp->indent>0)
720		sfnputc(wp->out,'\t',wp->indent);
721	if(!special)
722	{
723		if(*name!='.')
724			nv_attribute(np,wp->out,"typeset",'=');
725		nv_outname(wp->out,name,-1);
726		if((np->nvalue.cp && np->nvalue.cp!=Empty) || nv_isattr(np,~(NV_MINIMAL|NV_NOFREE)) || nv_isvtree(np))
727		{
728			if(wp->indent>=0 || isarray!=2)
729				sfputc(wp->out,(isarray==2?'\n':'='));
730		}
731		if(isarray==2)
732			return;
733	}
734	fp = np->nvfun;
735	if(*name=='.' && !isarray)
736		np->nvfun = 0;
737	nv_outnode(np, wp->out, wp->indent, special);
738	if(*name=='.' && !isarray)
739		np->nvfun = fp;
740	if(isarray && !special)
741	{
742		if(wp->indent>0)
743		{
744			sfnputc(wp->out,'\t',wp->indent);
745			sfwrite(wp->out,")\n",2);
746		}
747		else
748			sfwrite(wp->out,");",2);
749	}
750}
751
752/*
753 * format initialization list given a list of assignments <argp>
754 */
755static char **genvalue(char **argv, const char *prefix, int n, struct Walk *wp)
756{
757	register char *cp,*nextcp,*arg;
758	register Sfio_t *outfile = wp->out;
759	register int m,r,l;
760	if(n==0)
761		m = strlen(prefix);
762	else if(cp=nextdot(prefix))
763		m = cp-prefix;
764	else
765		m = strlen(prefix)-1;
766	m++;
767	if(outfile && !wp->array)
768	{
769		sfputc(outfile,'(');
770		if(wp->indent>=0)
771		{
772			wp->indent++;
773			sfputc(outfile,'\n');
774		}
775	}
776	for(; arg= *argv; argv++)
777	{
778		cp = arg + n;
779		if(n==0 && cp[m-1]!='.')
780			continue;
781		if(n && cp[m-1]==0)
782			break;
783		if(n==0 || strncmp(arg,prefix-n,m+n)==0)
784		{
785			cp +=m;
786			r = 0;
787			if(*cp=='.')
788				cp++,r++;
789			if(nextcp=nextdot(cp))
790			{
791				if(outfile)
792				{
793					Namval_t *np,*tp;
794					*nextcp = 0;
795					np=nv_open(arg,wp->root,NV_VARNAME|NV_NOADD|NV_NOASSIGN|NV_NOFAIL|wp->noscope);
796					if(!np || (nv_isarray(np) && (!(tp=nv_opensub(np)) || !nv_isvtree(tp))))
797					{
798						*nextcp = '.';
799						continue;
800					}
801					if(wp->indent>=0)
802						sfnputc(outfile,'\t',wp->indent);
803					if(*cp!='[' && (tp = nv_type(np)))
804					{
805						char *sp;
806						if(sp = strrchr(tp->nvname,'.'))
807							sp++;
808						else
809							sp = tp->nvname;
810						sfputr(outfile,sp,' ');
811					}
812					nv_outname(outfile,cp,nextcp-cp);
813					sfputc(outfile,'=');
814					*nextcp = '.';
815				}
816				else
817				{
818					outval(cp,arg,wp);
819					continue;
820				}
821				argv = genvalue(argv,cp,n+m+r,wp);
822				if(wp->indent>=0)
823					sfputc(outfile,'\n');
824				if(*argv)
825					continue;
826				break;
827			}
828			else if(outfile && !wp->nofollow && argv[1] && memcmp(arg,argv[1],l=strlen(arg))==0 && argv[1][l]=='[')
829			{
830				int	k=1;
831				Namarr_t *ap=0;
832				Namval_t *np = nv_open(arg,wp->root,NV_VARNAME|NV_NOADD|NV_NOASSIGN|wp->noscope);
833				if(!np)
834					continue;
835				if((wp->array = nv_isarray(np)) && (ap=nv_arrayptr(np)))
836					k = array_elem(ap);
837
838				if(wp->indent>0)
839					sfnputc(outfile,'\t',wp->indent);
840				nv_attribute(np,outfile,"typeset",1);
841				nv_close(np);
842				sfputr(outfile,arg+m+r+(n?n:0),(k?'=':'\n'));
843				if(!k)
844				{
845					wp->array=0;
846					continue;
847				}
848				wp->nofollow=1;
849				argv = genvalue(argv,cp,cp-arg ,wp);
850				sfputc(outfile,wp->indent<0?';':'\n');
851			}
852			else if(outfile && *cp=='[')
853			{
854				if(wp->indent)
855					sfnputc(outfile,'\t',wp->indent);
856				sfputr(outfile,cp,'=');
857				argv = genvalue(++argv,cp,cp-arg ,wp);
858				sfputc(outfile,'\n');
859			}
860			else
861			{
862				outval(cp,arg,wp);
863				if(wp->array)
864				{
865					if(wp->indent>=0)
866						wp->indent++;
867					else
868						sfputc(outfile,' ');
869					wp->array = 0;
870				}
871			}
872		}
873		else
874			break;
875		wp->nofollow = 0;
876	}
877	wp->array = 0;
878	if(outfile)
879	{
880		int c = prefix[m-1];
881		cp = (char*)prefix;
882		if(c=='.')
883			cp[m-1] = 0;
884		outval(".",prefix-n,wp);
885		if(c=='.')
886			cp[m-1] = c;
887		if(wp->indent>0)
888			sfnputc(outfile,'\t',--wp->indent);
889		sfputc(outfile,')');
890	}
891	return(--argv);
892}
893
894/*
895 * walk the virtual tree and print or delete name-value pairs
896 */
897static char *walk_tree(register Namval_t *np, Namval_t *xp, int flags)
898{
899	static Sfio_t *out;
900	struct Walk walk;
901	Sfio_t *outfile;
902	int len, savtop = staktell();
903	char *savptr = stakfreeze(0);
904	register struct argnod *ap=0;
905	struct argnod *arglist=0;
906	char *name,*cp, **argv;
907	char *subscript=0;
908	void *dir;
909	int n=0, noscope=(flags&NV_NOSCOPE);
910	Namarr_t *arp = nv_arrayptr(np);
911	Dt_t	*save_tree = sh.var_tree;
912	Namval_t	*mp=0;
913	Shell_t		*shp = sh_getinterp();
914	char		*xpname = xp?stakcopy(nv_name(xp)):0;
915	if(xp)
916	{
917		shp->last_root = shp->prev_root;
918		shp->last_table = shp->prev_table;
919	}
920	if(shp->last_table)
921		shp->last_root = nv_dict(shp->last_table);
922	if(shp->last_root)
923		shp->var_tree = shp->last_root;
924	stakputs(nv_name(np));
925	if(arp && !(arp->nelem&ARRAY_SCAN) && (subscript = nv_getsub(np)))
926	{
927		mp = nv_opensub(np);
928		stakputc('[');
929		stakputs(subscript);
930		stakputc(']');
931		stakputc('.');
932	}
933	else if(*stakptr(staktell()-1) == ']')
934		mp = np;
935	name = stakfreeze(1);
936	len = strlen(name);
937	shp->last_root = 0;
938	dir = nv_diropen(mp,name);
939	walk.root = shp->last_root?shp->last_root:shp->var_tree;
940	if(subscript)
941		name[strlen(name)-1] = 0;
942	while(cp = nv_dirnext(dir))
943	{
944		if(cp[len]!='.')
945			continue;
946		if(xp)
947		{
948			Dt_t		*dp = shp->var_tree;
949			Namval_t	*nq, *mq;
950			if(strlen(cp)<=len)
951				continue;
952			nq = nv_open(cp,walk.root,NV_VARNAME|NV_NOADD|NV_NOASSIGN|NV_NOFAIL);
953			if(!nq && (flags&NV_MOVE))
954				nq = nv_search(cp,walk.root,NV_NOADD);
955			stakseek(0);
956			stakputs(xpname);
957			stakputs(cp+len);
958			stakputc(0);
959			shp->var_tree = save_tree;
960			mq = nv_open(stakptr(0),save_tree,NV_VARNAME|NV_NOASSIGN|NV_NOFAIL);
961			shp->var_tree = dp;
962			if(nq && mq)
963			{
964				nv_clone(nq,mq,flags|NV_RAW);
965				if(flags&NV_MOVE)
966					nv_delete(nq,walk.root,0);
967			}
968			continue;
969		}
970		stakseek(ARGVAL);
971		stakputs(cp);
972		ap = (struct argnod*)stakfreeze(1);
973		ap->argflag = ARG_RAW;
974		ap->argchn.ap = arglist;
975		n++;
976		arglist = ap;
977	}
978	nv_dirclose(dir);
979	if(xp)
980	{
981		shp->var_tree = save_tree;
982		return((char*)0);
983	}
984	argv = (char**)stakalloc((n+1)*sizeof(char*));
985	argv += n;
986	*argv = 0;
987	for(; ap; ap=ap->argchn.ap)
988		*--argv = ap->argval;
989	if(flags&1)
990		outfile = 0;
991	else if(!(outfile=out))
992		outfile = out =  sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING);
993	else
994		sfseek(outfile,0L,SEEK_SET);
995	walk.out = outfile;
996	walk.indent = (flags&NV_EXPORT)?-1:0;
997	walk.nofollow = 0;
998	walk.noscope = noscope;
999	walk.array = 0;
1000	walk.flags = flags;
1001	genvalue(argv,name,0,&walk);
1002	stakset(savptr,savtop);
1003	shp->var_tree = save_tree;
1004	if(!outfile)
1005		return((char*)0);
1006	sfputc(out,0);
1007	return((char*)out->_data);
1008}
1009
1010Namfun_t *nv_isvtree(Namval_t *np)
1011{
1012	if(np)
1013		return(nv_hasdisc(np,&treedisc));
1014	return(0);
1015}
1016
1017/*
1018 * get discipline for compound initializations
1019 */
1020char *nv_getvtree(register Namval_t *np, Namfun_t *fp)
1021{
1022	int flags=0, dsize=fp->dsize;
1023	for(; fp && fp->next; fp=fp->next)
1024	{
1025		if(fp->next->disc && (fp->next->disc->getnum || fp->next->disc->getval))
1026			return(nv_getv(np,fp));
1027	}
1028	if(nv_isattr(np,NV_BINARY) &&  !nv_isattr(np,NV_RAW))
1029		return(nv_getv(np,fp));
1030	if(nv_isattr(np,NV_ARRAY) && !nv_type(np) && nv_arraychild(np,(Namval_t*)0,0)==np)
1031		return(nv_getv(np,fp));
1032	if(flags = nv_isattr(np,NV_EXPORT))
1033		nv_offattr(np,NV_EXPORT);
1034	if(dsize && (flags&NV_EXPORT))
1035		return("()");
1036	return(walk_tree(np,(Namval_t*)0,flags));
1037}
1038
1039/*
1040 * put discipline for compound initializations
1041 */
1042static void put_tree(register Namval_t *np, const char *val, int flags,Namfun_t *fp)
1043{
1044	struct Namarray *ap;
1045	int nleft = 0;
1046	if(!val && !fp->next && nv_isattr(np,NV_NOFREE))
1047		return;
1048	if(!nv_isattr(np,(NV_INTEGER|NV_BINARY)))
1049	{
1050		Shell_t		*shp = sh_getinterp();
1051		Namval_t	*last_table = shp->last_table;
1052		Dt_t		*last_root = shp->last_root;
1053		Namval_t 	*mp = val?nv_open(val,shp->var_tree,NV_VARNAME|NV_NOADD|NV_NOASSIGN|NV_ARRAY|NV_NOFAIL):0;
1054		if(mp && nv_isvtree(mp))
1055		{
1056			shp->prev_table = shp->last_table;
1057			shp->prev_root = shp->last_root;
1058			shp->last_table = last_table;
1059			shp->last_root = last_root;
1060			if(!(flags&NV_APPEND))
1061				walk_tree(np,(Namval_t*)0,(flags&NV_NOSCOPE)|1);
1062			nv_clone(mp,np,NV_COMVAR);
1063			return;
1064		}
1065		walk_tree(np,(Namval_t*)0,(flags&NV_NOSCOPE)|1);
1066	}
1067	nv_putv(np, val, flags,fp);
1068	if(val && nv_isattr(np,(NV_INTEGER|NV_BINARY)))
1069		return;
1070	if(ap= nv_arrayptr(np))
1071		nleft = array_elem(ap);
1072	if(nleft==0)
1073	{
1074		fp = nv_stack(np,fp);
1075		if(fp = nv_stack(np,NIL(Namfun_t*)))
1076			free((void*)fp);
1077	}
1078}
1079
1080/*
1081 * Insert discipline to cause $x to print current tree
1082 */
1083void nv_setvtree(register Namval_t *np)
1084{
1085	register Namfun_t *nfp;
1086	if(sh.subshell)
1087		sh_assignok(np,1);
1088	if(nv_hasdisc(np, &treedisc))
1089		return;
1090	nfp = newof(NIL(void*),Namfun_t,1,0);
1091	nfp->disc = &treedisc;
1092	nfp->dsize = sizeof(Namfun_t);
1093	nv_stack(np, nfp);
1094}
1095
1096