xref: /titanic_52/usr/src/contrib/ast/src/cmd/ksh93/sh/nvtree.c (revision 906afcb89d0412cc073b95c2d701a804a8cdb62c)
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 
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;
46 };
47 
48 static int	Indent;
49 char *nv_getvtree(Namval_t*, Namfun_t *);
50 static void put_tree(Namval_t*, const char*, int,Namfun_t*);
51 static char *walk_tree(Namval_t*, Namval_t*, int);
52 
53 static 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 
70 static 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 
86 static 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 
100 static 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 
113 static 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 
132 static  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 
145 void *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 
265 static 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 
274 char *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 
370 void 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 
378 static 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  */
410 void 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 
568 struct 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 
580 void 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 
696 static 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  */
814 static 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  */
965 static 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 
1083 Namfun_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  */
1093 char *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  */
1117 static 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  */
1158 void 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