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