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