xref: /titanic_50/usr/src/lib/libshell/common/sh/nvtree.c (revision c7158ae983f5a04c4a998f468ecefba6d23ba721)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *           Copyright (c) 1982-2007 AT&T Knowledge Ventures            *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                      by AT&T Knowledge Ventures                      *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                  David Korn <dgk@research.att.com>                   *
18 *                                                                      *
19 ***********************************************************************/
20 #pragma prototyped
21 
22 /*
23  * code for tree nodes and name walking
24  *
25  *   David Korn
26  *   AT&T Labs
27  *
28  */
29 
30 #include	"defs.h"
31 #include	"name.h"
32 #include	"argnod.h"
33 
34 struct nvdir
35 {
36 	Dt_t		*root;
37 	Namval_t	*hp;
38 	Namval_t	*table;
39 	Namval_t	*(*nextnode)(Namval_t*,Dt_t*,Namfun_t*);
40 	Namfun_t	*fun;
41 	struct nvdir	*prev;
42 	int		len;
43 	int		offset;
44 	char		data[1];
45 };
46 
47 char *nv_getvtree(Namval_t*, Namfun_t *);
48 static void put_tree(Namval_t*, const char*, int,Namfun_t*);
49 
50 static Namval_t *create_tree(Namval_t *np,const char *name,int flag,Namfun_t *dp)
51 {
52 	register Namfun_t *fp=dp;
53 	while(fp=fp->next)
54 	{
55 		if(fp->disc && fp->disc->createf)
56 		{
57 			if(np=(*fp->disc->createf)(np,name,flag,fp))
58 				dp->last = fp->last;
59 			return(np);
60 		}
61 	}
62 	return((flag&NV_NOADD)?0:np);
63 }
64 
65 static const Namdisc_t treedisc =
66 {
67 	0,
68 	put_tree,
69 	nv_getvtree,
70 	0,
71 	0,
72 	create_tree
73 };
74 
75 static char *nextdot(const char *str)
76 {
77 	register char *cp;
78 	if(*str=='.')
79 		str++;
80 	if(*str =='[')
81 	{
82 		cp = nv_endsubscript((Namval_t*)0,(char*)str,0);
83 		return(*cp=='.'?cp:0);
84 	}
85 	else
86 		return(strchr(str,'.'));
87 }
88 
89 static  Namfun_t *nextdisc(Namval_t *np)
90 {
91 	register Namfun_t *fp;
92 	if(nv_isref(np))
93 		return(0);
94         for(fp=np->nvfun;fp;fp=fp->next)
95 	{
96 		if(fp && fp->disc && fp->disc->nextf)
97 			return(fp);
98 	}
99 	return(0);
100 }
101 
102 void *nv_diropen(const char *name)
103 {
104 	char *next,*last;
105 	int c,len=strlen(name);
106 	struct nvdir *save, *dp = new_of(struct nvdir,len);
107 	Namval_t *np, fake;
108 	Namfun_t *nfp;
109 	if(!dp)
110 		return(0);
111 	memset((void*)dp, 0, sizeof(*dp));
112 	last=dp->data;
113 	if(name[len-1]=='*' || name[len-1]=='@')
114 		len -= 1;
115 	name = memcpy(last,name,len);
116 	last[len] = 0;
117 	dp->len = len;
118 	dp->root = sh.var_tree;
119 	dp->table = sh.last_table;
120 	if(*name)
121 	{
122 		fake.nvname = (char*)name;
123 		dp->hp = (Namval_t*)dtprev(dp->root,&fake);
124 		dp->hp = (Namval_t*)dtnext(dp->root,dp->hp);
125 	}
126 	else
127 		dp->hp = (Namval_t*)dtfirst(dp->root);
128 	while(next= nextdot(last))
129 	{
130 		c = *next;
131 		*next = 0;
132 		np = nv_search(last,dp->root,0);
133 		*next = c;
134 		if(np && ((nfp=nextdisc(np)) || nv_istable(np)))
135 		{
136 			if(!(save = new_of(struct nvdir,0)))
137 				return(0);
138 			*save = *dp;
139 			dp->prev = save;
140 			if(nv_istable(np))
141 				dp->root = nv_dict(np);
142 			else
143 				dp->root = (Dt_t*)dp;
144 			dp->offset = last-(char*)name;
145 			if(dp->offset<len)
146 				dp->len = len-dp->offset;
147 			else
148 				dp->len = 0;
149 			if(nfp)
150 			{
151 				dp->nextnode = nfp->disc->nextf;
152 				dp->table = np;
153 				dp->fun = nfp;
154 				dp->hp = (*dp->nextnode)(np,(Dt_t*)0,nfp);
155 			}
156 			else
157 				dp->nextnode = 0;
158 		}
159 		else
160 			break;
161 		last = next+1;
162 	}
163 	return((void*)dp);
164 }
165 
166 
167 static Namval_t *nextnode(struct nvdir *dp)
168 {
169 	if(dp->nextnode)
170 		return((*dp->nextnode)(dp->hp,dp->root,dp->fun));
171 	if(dp->len && memcmp(dp->data, dp->hp->nvname, dp->len))
172 		return(0);
173 	return((Namval_t*)dtnext(dp->root,dp->hp));
174 }
175 
176 char *nv_dirnext(void *dir)
177 {
178 	register struct nvdir *save, *dp = (struct nvdir*)dir;
179 	register Namval_t *np, *last_table;
180 	register char *cp;
181 	Namfun_t *nfp;
182 	while(1)
183 	{
184 		while(np=dp->hp)
185 		{
186 			dp->hp = nextnode(dp);
187 			if(nv_isnull(np))
188 				continue;
189 			last_table = sh.last_table;
190 			sh.last_table = dp->table;
191 			cp = nv_name(np);
192 			sh.last_table = last_table;
193 			if(!dp->len || memcmp(cp+dp->offset,dp->data,dp->len)==0)
194 			{
195 				if((nfp=nextdisc(np)) || nv_istable(np))
196 				{
197 					Dt_t *root;
198 					if(nv_istable(np))
199 						root = nv_dict(np);
200 					else
201 						root = (Dt_t*)dp;
202 					/* check for recursive walk */
203 					for(save=dp; save;  save=save->prev)
204 					{
205 						if(save->root==root)
206 							break;
207 					}
208 					if(save)
209 						continue;
210 					if(!(save = new_of(struct nvdir,0)))
211 						return(0);
212 					*save = *dp;
213 					dp->prev = save;
214 					dp->root = root;
215 					dp->len = 0;
216 					if(nfp && np->nvfun)
217 					{
218 						dp->nextnode = nfp->disc->nextf;
219 						dp->table = np;
220 						dp->fun = nfp;
221 						dp->hp = (*dp->nextnode)(np,(Dt_t*)0,nfp);
222 					}
223 					else
224 						dp->nextnode = 0;
225 				}
226 				return(cp);
227 			}
228 		}
229 		if(!(save=dp->prev))
230 			break;
231 #if 0
232 		sh.last_table = dp->table;
233 #endif
234 		*dp = *save;
235 		free((void*)save);
236 	}
237 	return(0);
238 }
239 
240 void nv_dirclose(void *dir)
241 {
242 	struct nvdir *dp = (struct nvdir*)dir;
243 	if(dp->prev)
244 		nv_dirclose((void*)dp->prev);
245 	free(dir);
246 }
247 
248 static void outtype(Namval_t *np, Namfun_t *fp, Sfio_t* out, const char *prefix)
249 {
250 	char *type=0;
251 	Namval_t *tp = fp->type;
252 	if(!tp && fp->disc && fp->disc->typef)
253 		tp = (*fp->disc->typef)(np,fp);
254 	for(fp=fp->next;fp;fp=fp->next)
255 	{
256 		if(fp->type || (fp->disc && fp->disc->typef &&(*fp->disc->typef)(np,fp)))
257 		{
258 			outtype(np,fp,out,prefix);
259 			break;
260 		}
261 	}
262 	if(prefix && *prefix=='t')
263 		type = "-T";
264 	else if(!prefix)
265 		type = "type";
266 	if(type)
267 		sfprintf(out,"%s %s ",type,tp->nvname);
268 }
269 
270 /*
271  * print the attributes of name value pair give by <np>
272  */
273 void nv_attribute(register Namval_t *np,Sfio_t *out,char *prefix,int noname)
274 {
275 	register const Shtable_t *tp;
276 	register char *cp;
277 	register unsigned val;
278 	register unsigned mask;
279 	register unsigned attr;
280 	Namfun_t *fp=0;
281 	for(fp=np->nvfun;fp;fp=fp->next)
282 	{
283 		if(fp->type || (fp->disc && fp->disc->typef &&(*fp->disc->typef)(np,fp)))
284 			break;
285 	}
286 #if 0
287 	if(!fp  && !nv_isattr(np,~NV_ARRAY))
288 	{
289 		if(!nv_isattr(np,NV_ARRAY)  || nv_aindex(np)>=0)
290 			return;
291 	}
292 #else
293 	if(!fp  && !nv_isattr(np,~NV_MINIMAL))
294 		return;
295 #endif
296 
297 	if ((attr=nv_isattr(np,~NV_NOFREE)) || fp)
298 	{
299 		if((attr&NV_NOPRINT)==NV_NOPRINT)
300 			attr &= ~NV_NOPRINT;
301 		if(!attr && !fp)
302 			return;
303 		if(prefix)
304 			sfputr(out,prefix,' ');
305 		for(tp = shtab_attributes; *tp->sh_name;tp++)
306 		{
307 			val = tp->sh_number;
308 			mask = val;
309 			if(fp && (val&NV_INTEGER))
310 				break;
311 			/*
312 			 * the following test is needed to prevent variables
313 			 * with E attribute from being given the F
314 			 * attribute as well
315 			*/
316 			if(val==(NV_INTEGER|NV_DOUBLE) && (attr&NV_EXPNOTE))
317 				continue;
318 			if(val&NV_INTEGER)
319 				mask |= NV_DOUBLE;
320 			else if(val&NV_HOST)
321 				mask = NV_HOST;
322 			if((attr&mask)==val)
323 			{
324 				if(val==NV_ARRAY)
325 				{
326 					Namarr_t *ap = nv_arrayptr(np);
327 					if(array_assoc(ap))
328 					{
329 						if(tp->sh_name[1]!='A')
330 							continue;
331 					}
332 					else if(tp->sh_name[1]=='A')
333 						continue;
334 #if 0
335 						cp = "associative";
336 					else
337 						cp = "indexed";
338 					if(!prefix)
339 						sfputr(out,cp,' ');
340 					else if(*cp=='i')
341 						tp++;
342 #endif
343 				}
344 				if(prefix)
345 				{
346 					if(*tp->sh_name=='-')
347 						sfprintf(out,"%.2s ",tp->sh_name);
348 				}
349 				else
350 					sfputr(out,tp->sh_name+2,' ');
351 		                if ((val&(NV_LJUST|NV_RJUST|NV_ZFILL)) && !(val&NV_INTEGER) && val!=NV_HOST)
352 					sfprintf(out,"%d ",nv_size(np));
353 			}
354 		        if(val==NV_INTEGER && nv_isattr(np,NV_INTEGER))
355 			{
356 				if(nv_size(np) != 10)
357 				{
358 					if(nv_isattr(np, NV_DOUBLE))
359 						cp = "precision";
360 					else
361 						cp = "base";
362 					if(!prefix)
363 						sfputr(out,cp,' ');
364 					sfprintf(out,"%d ",nv_size(np));
365 				}
366 				break;
367 			}
368 		}
369 		if(fp)
370 			outtype(np,fp,out,prefix);
371 		if(noname)
372 			return;
373 		sfputr(out,nv_name(np),'\n');
374 	}
375 }
376 
377 struct Walk
378 {
379 	Sfio_t	*out;
380 	Dt_t	*root;
381 	int	noscope;
382 	int	indent;
383 };
384 
385 static void outval(char *name, const char *vname, struct Walk *wp)
386 {
387 	register Namval_t *np, *nq;
388         register Namfun_t *fp;
389 	int isarray=0, associative=0, special=0;
390 	if(!(np=nv_open(vname,wp->root,NV_ARRAY|NV_VARNAME|NV_NOADD|NV_NOASSIGN|wp->noscope)))
391 		return;
392 	if(nv_isarray(np) && *name=='.')
393 		special = 1;
394 	if(!special && (fp=nv_hasdisc(np,&treedisc)))
395 	{
396 		if(!wp->out)
397 		{
398 			fp = nv_stack(np,fp);
399 			if(fp = nv_stack(np,NIL(Namfun_t*)))
400 				free((void*)fp);
401 			np->nvfun = 0;
402 		}
403 		return;
404 	}
405 	if(nv_isnull(np))
406 		return;
407 	if(special || nv_isarray(np))
408 	{
409 		isarray=1;
410 		associative= nv_aindex(np)<0;
411 		if(array_elem(nv_arrayptr(np))==0)
412 			isarray=2;
413 		else
414 			nq = nv_putsub(np,NIL(char*),ARRAY_SCAN|(wp->out?ARRAY_NOCHILD:0));
415 	}
416 	if(!wp->out)
417 	{
418 		_nv_unset(np,NV_RDONLY);
419 		nv_close(np);
420 		return;
421 	}
422 	if(isarray==1 && !nq)
423 		return;
424 	if(special)
425 	{
426 		associative = 1;
427 		sfnputc(wp->out,'\t',wp->indent);
428 	}
429 	else
430 	{
431 		sfnputc(wp->out,'\t',wp->indent);
432 		nv_attribute(np,wp->out,"typeset",'=');
433 		nv_outname(wp->out,name,-1);
434 		sfputc(wp->out,(isarray==2?'\n':'='));
435 		if(isarray)
436 		{
437 			if(isarray==2)
438 				return;
439 			sfwrite(wp->out,"(\n",2);
440 			sfnputc(wp->out,'\t',++wp->indent);
441 		}
442 	}
443 	while(1)
444 	{
445 		char *fmtq,*ep;
446 		if(isarray && associative)
447 		{
448 			if(!(fmtq = nv_getsub(np)))
449 				break;
450 			sfprintf(wp->out,"[%s]",sh_fmtq(fmtq));
451 			sfputc(wp->out,'=');
452 		}
453 		if(!(fmtq = sh_fmtq(nv_getval(np))))
454 			fmtq = "";
455 		else if(!associative && (ep=strchr(fmtq,'=')))
456 		{
457 			char *qp = strchr(fmtq,'\'');
458 			if(!qp || qp>ep)
459 			{
460 				sfwrite(wp->out,fmtq,ep-fmtq);
461 				sfputc(wp->out,'\\');
462 				fmtq = ep;
463 			}
464 		}
465 		if(*name=='[' && !isarray)
466 			sfprintf(wp->out,"(%s)\n",fmtq);
467 		else
468 			sfputr(wp->out,fmtq,'\n');
469 		if(!nv_nextsub(np))
470 			break;
471 		sfnputc(wp->out,'\t',wp->indent);
472 	}
473 	if(isarray && !special)
474 	{
475 		sfnputc(wp->out,'\t',--wp->indent);
476 		sfwrite(wp->out,")\n",2);
477 	}
478 }
479 
480 /*
481  * format initialization list given a list of assignments <argp>
482  */
483 static char **genvalue(char **argv, const char *prefix, int n, struct Walk *wp)
484 {
485 	register char *cp,*nextcp,*arg;
486 	register int m,r;
487 	register Sfio_t *outfile = wp->out;
488 	if(n==0)
489 		m = strlen(prefix);
490 	else if(cp=nextdot(prefix))
491 		m = cp-prefix;
492 	else
493 		m = strlen(prefix)-1;
494 	m++;
495 	if(outfile)
496 	{
497 		sfwrite(outfile,"(\n",2);
498 		wp->indent++;
499 	}
500 	for(; arg= *argv; argv++)
501 	{
502 		cp = arg + n;
503 		if(n==0 && cp[m-1]!='.')
504 			continue;
505 		if(n && cp[m-1]==0)
506 			break;
507 		if(n==0 || strncmp(arg,prefix-n,m+n)==0)
508 		{
509 			cp +=m;
510 			r = 0;
511 			if(*cp=='.')
512 				cp++,r++;
513 			if(nextcp=nextdot(cp))
514 			{
515 				if(outfile)
516 				{
517 					sfnputc(outfile,'\t',wp->indent);
518 					nv_outname(outfile,cp,nextcp-cp);
519 					sfputc(outfile,'=');
520 				}
521 				argv = genvalue(argv,cp,n+m+r,wp);
522 				if(outfile)
523 					sfputc(outfile,'\n');
524 				if(*argv)
525 					continue;
526 				break;
527 			}
528 			else if(outfile && argv[1] && memcmp(arg,argv[1],r=strlen(arg))==0 && argv[1][r]=='[')
529 			{
530 				Namval_t *np = nv_open(arg,wp->root,NV_VARNAME|NV_NOADD|NV_NOASSIGN|wp->noscope);
531 				if(!np)
532 					continue;
533 				sfnputc(outfile,'\t',wp->indent);
534 				nv_attribute(np,outfile,"typeset",1);
535 				nv_close(np);
536 				sfputr(outfile,arg+m+(n?n+1:0),'=');
537 				argv = genvalue(++argv,cp,cp-arg ,wp);
538 				sfputc(outfile,'\n');
539 			}
540 			else if(outfile && *cp=='[')
541 			{
542 				sfnputc(outfile,'\t',wp->indent);
543 				sfputr(outfile,cp,'=');
544 				argv = genvalue(++argv,cp,cp-arg ,wp);
545 				sfputc(outfile,'\n');
546 			}
547 			else
548 				outval(cp,arg,wp);
549 		}
550 		else
551 			break;
552 	}
553 	if(outfile)
554 	{
555 		int c = prefix[m-1];
556 		cp = (char*)prefix;
557 		if(c=='.')
558 			cp[m-1] = 0;
559 		outval(".",prefix-n,wp);
560 		if(c=='.')
561 			cp[m-1] = c;
562 		sfnputc(outfile,'\t',wp->indent-1);
563 		sfputc(outfile,')');
564 	}
565 	return(--argv);
566 }
567 
568 /*
569  * walk the virtual tree and print or delete name-value pairs
570  */
571 static char *walk_tree(register Namval_t *np, int dlete)
572 {
573 	static Sfio_t *out;
574 	struct Walk walk;
575 	Sfio_t *outfile;
576 	int savtop = staktell();
577 	char *savptr = stakfreeze(0);
578 	register struct argnod *ap=0;
579 	struct argnod *arglist=0;
580 	char *name,*cp, **argv;
581 	char *subscript=0;
582 	void *dir;
583 	int n=0, noscope=(dlete&NV_NOSCOPE);
584 	stakputs(nv_name(np));
585 	if(subscript = nv_getsub(np))
586 	{
587 		stakputc('[');
588 		stakputs(subscript);
589 		stakputc(']');
590 		stakputc('.');
591 	}
592 	name = stakfreeze(1);
593 	dir = nv_diropen(name);
594 	if(subscript)
595 		name[strlen(name)-1] = 0;
596 	while(cp = nv_dirnext(dir))
597 	{
598 		stakseek(ARGVAL);
599 		stakputs(cp);
600 		ap = (struct argnod*)stakfreeze(1);
601 		ap->argflag = ARG_RAW;
602 		ap->argchn.ap = arglist;
603 		n++;
604 		arglist = ap;
605 	}
606 	argv = (char**)stakalloc((n+1)*sizeof(char*));
607 	argv += n;
608 	*argv = 0;
609 	for(; ap; ap=ap->argchn.ap)
610 		*--argv = ap->argval;
611 	nv_dirclose(dir);
612 	if(dlete&1)
613 		outfile = 0;
614 	else if(!(outfile=out))
615 		outfile = out =  sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING);
616 	else
617 		sfseek(outfile,0L,SEEK_SET);
618 	walk.out = outfile;
619 	walk.root = sh.last_root;
620 	walk.indent = 0;
621 	walk.noscope = noscope;
622 	genvalue(argv,name,0,&walk);
623 	stakset(savptr,savtop);
624 	if(!outfile)
625 		return((char*)0);
626 	sfputc(out,0);
627 	return((char*)out->_data);
628 }
629 
630 /*
631  * get discipline for compound initializations
632  */
633 char *nv_getvtree(register Namval_t *np, Namfun_t *fp)
634 {
635 	NOT_USED(fp);
636 	if(nv_isattr(np,NV_BINARY) &&  nv_isattr(np,NV_RAW))
637 		return(nv_getv(np,fp));
638 	if(nv_isattr(np,NV_ARRAY) && nv_arraychild(np,(Namval_t*)0,0)==np)
639 		return(nv_getv(np,fp));
640 	return(walk_tree(np,0));
641 }
642 
643 /*
644  * put discipline for compound initializations
645  */
646 static void put_tree(register Namval_t *np, const char *val, int flags,Namfun_t *fp)
647 {
648 	struct Namarray *ap;
649 	int nleft = 0;
650 	if(!nv_isattr(np,NV_INTEGER))
651 		walk_tree(np,(flags&NV_NOSCOPE)|1);
652 	nv_putv(np, val, flags,fp);
653 	if(nv_isattr(np,NV_INTEGER))
654 		return;
655 	if(ap= nv_arrayptr(np))
656 		nleft = array_elem(ap);
657 	if(nleft==0)
658 	{
659 		fp = nv_stack(np,fp);
660 		if(fp = nv_stack(np,NIL(Namfun_t*)))
661 		{
662 			free((void*)fp);
663 		}
664 	}
665 }
666 
667 /*
668  * Insert discipline to cause $x to print current tree
669  */
670 void nv_setvtree(register Namval_t *np)
671 {
672 	register Namfun_t *nfp;
673 	if(nv_hasdisc(np, &treedisc))
674 		return;
675 	nfp = newof(NIL(void*),Namfun_t,1,0);
676 	nfp->disc = &treedisc;
677 	nv_stack(np, nfp);
678 }
679 
680