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
read_tree(Namval_t * np,Sfio_t * iop,int n,Namfun_t * dp)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
create_tree(Namval_t * np,const char * name,int flag,Namfun_t * dp)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
clone_tree(Namval_t * np,Namval_t * mp,int flags,Namfun_t * fp)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
nextdot(const char * str)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
nextdisc(Namval_t * np)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
nv_diropen(Namval_t * np,const char * name)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
nextnode(struct nvdir * dp)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
nv_dirnext(void * dir)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
nv_dirclose(void * dir)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
outtype(Namval_t * np,Namfun_t * fp,Sfio_t * out,const char * prefix)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 */
nv_attribute(register Namval_t * np,Sfio_t * out,char * prefix,int noname)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
nv_outnode(Namval_t * np,Sfio_t * out,int indent,int special)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
outval(char * name,const char * vname,struct Walk * wp)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 */
genvalue(char ** argv,const char * prefix,int n,struct Walk * wp)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 */
walk_tree(register Namval_t * np,Namval_t * xp,int flags)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
nv_isvtree(Namval_t * np)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 */
nv_getvtree(register Namval_t * np,Namfun_t * fp)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 */
put_tree(register Namval_t * np,const char * val,int flags,Namfun_t * fp)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 */
nv_setvtree(register Namval_t * np)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