xref: /titanic_50/usr/src/lib/libshell/common/sh/nvtree.c (revision eab227978ccdaa5a7cc9fd92ace768915dae3a2b)
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 
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 	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 
85 static 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 
99 static 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 
112 static 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 
131 static  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 
144 void *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 
263 static 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 
272 char *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 
364 void 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 
372 static 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  */
404 void 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 
532 struct 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 
543 void 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 
646 static 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  */
755 static 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  */
897 static 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 
1010 Namfun_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  */
1020 char *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  */
1042 static 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  */
1083 void 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