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