1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1982-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * David Korn <dgk@research.att.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22 * AT&T Labs
23 *
24 */
25
26 #include "defs.h"
27 #include "variables.h"
28 #include "builtins.h"
29 #include "path.h"
30
nv_compare(Dt_t * dict,Void_t * sp,Void_t * dp,Dtdisc_t * disc)31 int nv_compare(Dt_t* dict, Void_t *sp, Void_t *dp, Dtdisc_t *disc)
32 {
33 if(sp==dp)
34 return(0);
35 return(strcmp((char*)sp,(char*)dp));
36 }
37
38 /*
39 * call the next getval function in the chain
40 */
nv_getv(Namval_t * np,register Namfun_t * nfp)41 char *nv_getv(Namval_t *np, register Namfun_t *nfp)
42 {
43 register Namfun_t *fp;
44 register char *cp;
45 if((fp = nfp) != NIL(Namfun_t*) && !nv_local)
46 fp = nfp = nfp->next;
47 nv_local=0;
48 for(; fp; fp=fp->next)
49 {
50 if(!fp->disc || (!fp->disc->getnum && !fp->disc->getval))
51 continue;
52 if(!nv_isattr(np,NV_NODISC) || fp==(Namfun_t*)nv_arrayptr(np))
53 break;
54 }
55 if(fp && fp->disc->getval)
56 cp = (*fp->disc->getval)(np,fp);
57 else if(fp && fp->disc->getnum)
58 {
59 sfprintf(sh.strbuf,"%.*Lg",12,(*fp->disc->getnum)(np,fp));
60 cp = sfstruse(sh.strbuf);
61 }
62 else
63 {
64 nv_local=1;
65 cp = nv_getval(np);
66 }
67 return(cp);
68 }
69
70 /*
71 * call the next getnum function in the chain
72 */
nv_getn(Namval_t * np,register Namfun_t * nfp)73 Sfdouble_t nv_getn(Namval_t *np, register Namfun_t *nfp)
74 {
75 register Namfun_t *fp;
76 register Sfdouble_t d=0;
77 char *str;
78 if((fp = nfp) != NIL(Namfun_t*) && !nv_local)
79 fp = nfp = nfp->next;
80 nv_local=0;
81 for(; fp; fp=fp->next)
82 {
83 if(!fp->disc || (!fp->disc->getnum && !fp->disc->getval))
84 continue;
85 if(!fp->disc->getnum && nv_isattr(np,NV_INTEGER))
86 continue;
87 if(!nv_isattr(np,NV_NODISC) || fp==(Namfun_t*)nv_arrayptr(np))
88 break;
89 }
90 if(fp && fp->disc && fp->disc->getnum)
91 d = (*fp->disc->getnum)(np,fp);
92 else if(nv_isattr(np,NV_INTEGER))
93 {
94 nv_local = 1;
95 d = nv_getnum(np);
96 }
97 else
98 {
99 if(fp && fp->disc && fp->disc->getval)
100 str = (*fp->disc->getval)(np,fp);
101 else
102 str = nv_getv(np,fp?fp:nfp);
103 if(str && *str)
104 {
105 while(*str=='0')
106 str++;
107 d = sh_arith(str);
108 }
109 }
110 return(d);
111 }
112
113 /*
114 * call the next assign function in the chain
115 */
nv_putv(Namval_t * np,const char * value,int flags,register Namfun_t * nfp)116 void nv_putv(Namval_t *np, const char *value, int flags, register Namfun_t *nfp)
117 {
118 register Namfun_t *fp, *fpnext;
119 if((fp=nfp) != NIL(Namfun_t*) && !nv_local)
120 fp = nfp = nfp->next;
121 nv_local=0;
122 if(flags&NV_NODISC)
123 fp = 0;
124 for(; fp; fp=fpnext)
125 {
126 fpnext = fp->next;
127 if(!fp->disc || !fp->disc->putval)
128 {
129 if(!value)
130 {
131 if(fp->disc || !(fp->nofree&1))
132 nv_disc(np,fp,NV_POP);
133 if(!(fp->nofree&1))
134 free((void*)fp);
135 }
136 continue;
137 }
138 if(!nv_isattr(np,NV_NODISC) || fp==(Namfun_t*)nv_arrayptr(np))
139 break;
140 }
141 if(fp && fp->disc->putval)
142 (*fp->disc->putval)(np,value, flags, fp);
143 else
144 {
145 nv_local=1;
146 if(value)
147 nv_putval(np, value, flags);
148 else
149 _nv_unset(np, flags&(NV_RDONLY|NV_EXPORT));
150 }
151 }
152
153 #define LOOKUPS 0
154 #define ASSIGN 1
155 #define APPEND 2
156 #define UNASSIGN 3
157 #define LOOKUPN 4
158 #define BLOCKED ((Namval_t*)&nv_local)
159
160 struct vardisc
161 {
162 Namfun_t fun;
163 Namval_t *disc[5];
164 };
165
166 struct blocked
167 {
168 struct blocked *next;
169 Namval_t *np;
170 int flags;
171 void *sub;
172 int isub;
173 };
174
175 static struct blocked *blist;
176
177 #define isblocked(bp,type) ((bp)->flags & (1<<(type)))
178 #define block(bp,type) ((bp)->flags |= (1<<(type)))
179 #define unblock(bp,type) ((bp)->flags &= ~(1<<(type)))
180
181 /*
182 * returns pointer to blocking structure
183 */
block_info(Namval_t * np,struct blocked * pp)184 static struct blocked *block_info(Namval_t *np, struct blocked *pp)
185 {
186 register struct blocked *bp;
187 void *sub=0;
188 int isub=0;
189 if(nv_isarray(np) && (isub=nv_aindex(np)) < 0)
190 sub = nv_associative(np,(const char*)0,NV_ACURRENT);
191 for(bp=blist ; bp; bp=bp->next)
192 {
193 if(bp->np==np && bp->sub==sub && bp->isub==isub)
194 return(bp);
195 }
196 if(pp)
197 {
198 pp->np = np;
199 pp->flags = 0;
200 pp->isub = isub;
201 pp->sub = sub;
202 pp->next = blist;
203 blist = pp;
204 }
205 return(pp);
206 }
207
block_done(struct blocked * bp)208 static void block_done(struct blocked *bp)
209 {
210 blist = bp = bp->next;
211 if(bp && (bp->isub>=0 || bp->sub))
212 nv_putsub(bp->np, bp->sub,(bp->isub<0?0:bp->isub)|ARRAY_SETSUB);
213 }
214
215 /*
216 * free discipline if no more discipline functions
217 */
chktfree(register Namval_t * np,register struct vardisc * vp)218 static void chktfree(register Namval_t *np, register struct vardisc *vp)
219 {
220 register int n;
221 for(n=0; n< sizeof(vp->disc)/sizeof(*vp->disc); n++)
222 {
223 if(vp->disc[n])
224 break;
225 }
226 if(n>=sizeof(vp->disc)/sizeof(*vp->disc))
227 {
228 /* no disc left so pop */
229 Namfun_t *fp;
230 if((fp=nv_stack(np, NIL(Namfun_t*))) && !(fp->nofree&1))
231 free((void*)fp);
232 }
233 }
234
235 /*
236 * This function performs an assignment disc on the given node <np>
237 */
assign(Namval_t * np,const char * val,int flags,Namfun_t * handle)238 static void assign(Namval_t *np,const char* val,int flags,Namfun_t *handle)
239 {
240 int type = (flags&NV_APPEND)?APPEND:ASSIGN;
241 register struct vardisc *vp = (struct vardisc*)handle;
242 register Namval_t *nq = vp->disc[type];
243 struct blocked block, *bp = block_info(np, &block);
244 Namval_t node;
245 union Value *up = np->nvalue.up;
246 #if SHOPT_TYPEDEF
247 Namval_t *tp, *nr;
248 if(val && (tp=nv_type(np)) && (nr=nv_open(val,sh.var_tree,NV_VARNAME|NV_ARRAY|NV_NOADD|NV_NOFAIL)) && tp==nv_type(nr))
249 {
250 char *sub = nv_getsub(np);
251 nv_unset(np);
252 if(sub)
253 {
254 nv_putsub(np, sub, ARRAY_ADD);
255 nv_putval(np,nv_getval(nr), 0);
256 }
257 else
258 nv_clone(nr,np,0);
259 goto done;
260 }
261 #endif /* SHOPT_TYPEDEF */
262 if(val || isblocked(bp,type))
263 {
264 if(!nq || isblocked(bp,type))
265 {
266 nv_putv(np,val,flags,handle);
267 goto done;
268 }
269 node = *SH_VALNOD;
270 if(!nv_isnull(SH_VALNOD))
271 {
272 nv_onattr(SH_VALNOD,NV_NOFREE);
273 nv_unset(SH_VALNOD);
274 }
275 if(flags&NV_INTEGER)
276 nv_onattr(SH_VALNOD,(flags&(NV_LONG|NV_DOUBLE|NV_EXPNOTE|NV_HEXFLOAT|NV_SHORT)));
277 nv_putval(SH_VALNOD, val, (flags&NV_INTEGER)?flags:NV_NOFREE);
278 }
279 else
280 nq = vp->disc[type=UNASSIGN];
281 if(nq && !isblocked(bp,type))
282 {
283 int bflag;
284 block(bp,type);
285 if (type==APPEND && (bflag= !isblocked(bp,LOOKUPS)))
286 block(bp,LOOKUPS);
287 sh_fun(nq,np,(char**)0);
288 unblock(bp,type);
289 if(bflag)
290 unblock(bp,LOOKUPS);
291 if(!vp->disc[type])
292 chktfree(np,vp);
293 }
294 if(nv_isarray(np))
295 np->nvalue.up = up;
296 if(val)
297 {
298 register char *cp;
299 Sfdouble_t d;
300 if(nv_isnull(SH_VALNOD))
301 cp=0;
302 else if(flags&NV_INTEGER)
303 {
304 d = nv_getnum(SH_VALNOD);
305 cp = (char*)(&d);
306 flags |= (NV_LONG|NV_DOUBLE);
307 flags &= ~NV_SHORT;
308 }
309 else
310 cp = nv_getval(SH_VALNOD);
311 if(cp)
312 nv_putv(np,cp,flags|NV_RDONLY,handle);
313 nv_unset(SH_VALNOD);
314 /* restore everything but the nvlink field */
315 memcpy(&SH_VALNOD->nvname, &node.nvname, sizeof(node)-sizeof(node.nvlink));
316 }
317 else if(sh_isstate(SH_INIT))
318 {
319 /* don't free functions during reinitialization */
320 nv_putv(np,val,flags,handle);
321 }
322 else if(!nq || !isblocked(bp,type))
323 {
324 Dt_t *root = sh_subfuntree(1);
325 int n;
326 Namarr_t *ap;
327 block(bp,type);
328 nv_putv(np, val, flags, handle);
329 if(sh.subshell)
330 goto done;
331 if(nv_isarray(np) && (ap=nv_arrayptr(np)) && ap->nelem>0)
332 goto done;
333 for(n=0; n < sizeof(vp->disc)/sizeof(*vp->disc); n++)
334 {
335 if((nq=vp->disc[n]) && !nv_isattr(nq,NV_NOFREE))
336 {
337 nv_unset(nq);
338 dtdelete(root,nq);
339 }
340 }
341 unblock(bp,type);
342 nv_disc(np,handle,NV_POP);
343 if(!(handle->nofree&1))
344 free(handle);
345 }
346 done:
347 if(bp== &block)
348 block_done(bp);
349 }
350
351 /*
352 * This function executes a lookup disc and then performs
353 * the lookup on the given node <np>
354 */
lookup(Namval_t * np,int type,Sfdouble_t * dp,Namfun_t * handle)355 static char* lookup(Namval_t *np, int type, Sfdouble_t *dp,Namfun_t *handle)
356 {
357 register struct vardisc *vp = (struct vardisc*)handle;
358 struct blocked block, *bp = block_info(np, &block);
359 register Namval_t *nq = vp->disc[type];
360 register char *cp=0;
361 Namval_t node;
362 union Value *up = np->nvalue.up;
363 if(nq && !isblocked(bp,type))
364 {
365 node = *SH_VALNOD;
366 if(!nv_isnull(SH_VALNOD))
367 {
368 nv_onattr(SH_VALNOD,NV_NOFREE);
369 nv_unset(SH_VALNOD);
370 }
371 if(type==LOOKUPN)
372 {
373 nv_onattr(SH_VALNOD,NV_DOUBLE|NV_INTEGER);
374 nv_setsize(SH_VALNOD,10);
375 }
376 block(bp,type);
377 sh_fun(nq,np,(char**)0);
378 unblock(bp,type);
379 if(!vp->disc[type])
380 chktfree(np,vp);
381 if(type==LOOKUPN)
382 {
383 cp = (char*)(SH_VALNOD->nvalue.cp);
384 *dp = nv_getnum(SH_VALNOD);
385 }
386 else if(cp = nv_getval(SH_VALNOD))
387 cp = stkcopy(stkstd,cp);
388 _nv_unset(SH_VALNOD,NV_RDONLY);
389 if(!nv_isnull(&node))
390 {
391 /* restore everything but the nvlink field */
392 memcpy(&SH_VALNOD->nvname, &node.nvname, sizeof(node)-sizeof(node.nvlink));
393 }
394 }
395 if(nv_isarray(np))
396 np->nvalue.up = up;
397 if(!cp)
398 {
399 if(type==LOOKUPS)
400 cp = nv_getv(np,handle);
401 else
402 *dp = nv_getn(np,handle);
403 }
404 if(bp== &block)
405 block_done(bp);
406 return(cp);
407 }
408
lookups(Namval_t * np,Namfun_t * handle)409 static char* lookups(Namval_t *np, Namfun_t *handle)
410 {
411 return(lookup(np,LOOKUPS,(Sfdouble_t*)0,handle));
412 }
413
lookupn(Namval_t * np,Namfun_t * handle)414 static Sfdouble_t lookupn(Namval_t *np, Namfun_t *handle)
415 {
416 Sfdouble_t d;
417 lookup(np,LOOKUPN, &d ,handle);
418 return(d);
419 }
420
421
422 /*
423 * Set disc on given <event> to <action>
424 * If action==np, the current disc is returned
425 * A null return value indicates that no <event> is known for <np>
426 * If <event> is NULL, then return the event name after <action>
427 * If <event> is NULL, and <action> is NULL, return the first event
428 */
nv_setdisc(register Namval_t * np,register const char * event,Namval_t * action,register Namfun_t * fp)429 char *nv_setdisc(register Namval_t* np,register const char *event,Namval_t *action,register Namfun_t *fp)
430 {
431 register struct vardisc *vp = (struct vardisc*)np->nvfun;
432 register int type;
433 char *empty = "";
434 while(vp)
435 {
436 if(vp->fun.disc && (vp->fun.disc->setdisc || vp->fun.disc->putval == assign))
437 break;
438 vp = (struct vardisc*)vp->fun.next;
439 }
440 if(vp && !vp->fun.disc)
441 vp = 0;
442 if(np == (Namval_t*)fp)
443 {
444 register const char *name;
445 register int getname=0;
446 /* top level call, check for get/set */
447 if(!event)
448 {
449 if(!action)
450 return((char*)nv_discnames[0]);
451 getname=1;
452 event = (char*)action;
453 }
454 for(type=0; name=nv_discnames[type]; type++)
455 {
456 if(strcmp(event,name)==0)
457 break;
458 }
459 if(getname)
460 {
461 event = 0;
462 if(name && !(name = nv_discnames[++type]))
463 action = 0;
464 }
465 if(!name)
466 {
467 for(fp=(Namfun_t*)vp; fp; fp=fp->next)
468 {
469 if(fp->disc && fp->disc->setdisc)
470 return((*fp->disc->setdisc)(np,event,action,fp));
471 }
472 }
473 else if(getname)
474 return((char*)name);
475 }
476 if(!fp)
477 return(NIL(char*));
478 if(np != (Namval_t*)fp)
479 {
480 /* not the top level */
481 while(fp = fp->next)
482 {
483 if(fp->disc && fp->disc->setdisc)
484 return((*fp->disc->setdisc)(np,event,action,fp));
485 }
486 return(NIL(char*));
487 }
488 /* Handle GET/SET/APPEND/UNSET disc */
489 if(vp && vp->fun.disc->putval!=assign)
490 vp = 0;
491 if(!vp)
492 {
493 Namdisc_t *dp;
494 if(action==np)
495 return((char*)action);
496 if(!(vp = newof(NIL(struct vardisc*),struct vardisc,1,sizeof(Namdisc_t))))
497 return(0);
498 dp = (Namdisc_t*)(vp+1);
499 vp->fun.disc = dp;
500 memset(dp,0,sizeof(*dp));
501 dp->dsize = sizeof(struct vardisc);
502 dp->putval = assign;
503 if(nv_isarray(np) && !nv_arrayptr(np))
504 nv_putsub(np,(char*)0, 1);
505 nv_stack(np, (Namfun_t*)vp);
506 }
507 if(action==np)
508 {
509 action = vp->disc[type];
510 empty = 0;
511 }
512 else if(action)
513 {
514 Namdisc_t *dp = (Namdisc_t*)vp->fun.disc;
515 if(type==LOOKUPS)
516 dp->getval = lookups;
517 else if(type==LOOKUPN)
518 dp->getnum = lookupn;
519 vp->disc[type] = action;
520 }
521 else
522 {
523 struct blocked *bp;
524 action = vp->disc[type];
525 vp->disc[type] = 0;
526 if(!(bp=block_info(np,(struct blocked*)0)) || !isblocked(bp,UNASSIGN))
527 chktfree(np,vp);
528 }
529 return(action?(char*)action:empty);
530 }
531
532 /*
533 * Set disc on given <event> to <action>
534 * If action==np, the current disc is returned
535 * A null return value indicates that no <event> is known for <np>
536 * If <event> is NULL, then return the event name after <action>
537 * If <event> is NULL, and <action> is NULL, return the first event
538 */
setdisc(register Namval_t * np,register const char * event,Namval_t * action,register Namfun_t * fp)539 static char *setdisc(register Namval_t* np,register const char *event,Namval_t *action,register Namfun_t *fp)
540 {
541 register Nambfun_t *vp = (Nambfun_t*)fp;
542 register int type,getname=0;
543 register const char *name;
544 const char **discnames = vp->bnames;
545 /* top level call, check for discipline match */
546 if(!event)
547 {
548 if(!action)
549 return((char*)discnames[0]);
550 getname=1;
551 event = (char*)action;
552 }
553 for(type=0; name=discnames[type]; type++)
554 {
555 if(strcmp(event,name)==0)
556 break;
557 }
558 if(getname)
559 {
560 event = 0;
561 if(name && !(name = discnames[++type]))
562 action = 0;
563 }
564 if(!name)
565 return(nv_setdisc(np,event,action,fp));
566 else if(getname)
567 return((char*)name);
568 /* Handle the disciplines */
569 if(action==np)
570 action = vp->bltins[type];
571 else if(action)
572 vp->bltins[type] = action;
573 else
574 {
575 action = vp->bltins[type];
576 vp->bltins[type] = 0;
577 }
578 return(action?(char*)action:"");
579 }
580
putdisc(Namval_t * np,const char * val,int flag,Namfun_t * fp)581 static void putdisc(Namval_t* np, const char* val, int flag, Namfun_t* fp)
582 {
583 nv_putv(np,val,flag,fp);
584 if(!val && !(flag&NV_NOFREE))
585 {
586 register Nambfun_t *vp = (Nambfun_t*)fp;
587 register int i;
588 for(i=0; vp->bnames[i]; i++)
589 {
590 register Namval_t *mp;
591 if((mp=vp->bltins[i]) && !nv_isattr(mp,NV_NOFREE))
592 {
593 if(is_abuiltin(mp))
594 {
595 if(mp->nvfun && !nv_isattr(mp,NV_NOFREE))
596 free((void*)mp->nvfun);
597 dtdelete(sh.bltin_tree,mp);
598 free((void*)mp);
599 }
600 }
601 }
602 nv_disc(np,fp,NV_POP);
603 if(!(fp->nofree&1))
604 free((void*)fp);
605
606 }
607 }
608
609 static const Namdisc_t Nv_bdisc = { 0, putdisc, 0, 0, setdisc };
610
nv_clone_disc(register Namfun_t * fp,int flags)611 Namfun_t *nv_clone_disc(register Namfun_t *fp, int flags)
612 {
613 register Namfun_t *nfp;
614 register int size;
615 if(!fp->disc && !fp->next && (fp->nofree&1))
616 return(fp);
617 if(!(size=fp->dsize) && (!fp->disc || !(size=fp->disc->dsize)))
618 size = sizeof(Namfun_t);
619 if(!(nfp=newof(NIL(Namfun_t*),Namfun_t,1,size-sizeof(Namfun_t))))
620 return(0);
621 memcpy(nfp,fp,size);
622 nfp->nofree &= ~1;
623 nfp->nofree |= (flags&NV_RDONLY)?1:0;
624 return(nfp);
625 }
626
nv_adddisc(Namval_t * np,const char ** names,Namval_t ** funs)627 int nv_adddisc(Namval_t *np, const char **names, Namval_t **funs)
628 {
629 register Nambfun_t *vp;
630 register int n=0;
631 register const char **av=names;
632 if(av)
633 {
634 while(*av++)
635 n++;
636 }
637 if(!(vp = newof(NIL(Nambfun_t*),Nambfun_t,1,n*sizeof(Namval_t*))))
638 return(0);
639 vp->fun.dsize = sizeof(Nambfun_t)+n*sizeof(Namval_t*);
640 vp->fun.nofree |= 2;
641 vp->num = n;
642 if(funs)
643 memcpy((void*)vp->bltins, (void*)funs,n*sizeof(Namval_t*));
644 else while(n>=0)
645 vp->bltins[n--] = 0;
646 vp->fun.disc = &Nv_bdisc;
647 vp->bnames = names;
648 nv_stack(np,&vp->fun);
649 return(1);
650 }
651
652 /*
653 * push, pop, clne, or reorder disciplines onto node <np>
654 * mode can be one of
655 * NV_FIRST: Move or push <fp> to top of the stack or delete top
656 * NV_LAST: Move or push <fp> to bottom of stack or delete last
657 * NV_POP: Delete <fp> from top of the stack
658 * NV_CLONE: Replace fp with a copy created my malloc() and return it
659 */
nv_disc(register Namval_t * np,register Namfun_t * fp,int mode)660 Namfun_t *nv_disc(register Namval_t *np, register Namfun_t* fp, int mode)
661 {
662 Namfun_t *lp, **lpp;
663 if(nv_isref(np))
664 return(0);
665 if(mode==NV_CLONE && !fp)
666 return(0);
667 if(fp)
668 {
669 fp->subshell = sh.subshell;
670 if((lp=np->nvfun)==fp)
671 {
672 if(mode==NV_CLONE)
673 {
674 lp = nv_clone_disc(fp,0);
675 return(np->nvfun=lp);
676 }
677 if(mode==NV_FIRST || mode==0)
678 return(fp);
679 np->nvfun = lp->next;
680 if(mode==NV_POP)
681 return(fp);
682 if(mode==NV_LAST && (lp->next==0 || lp->next->disc==0))
683 return(fp);
684 }
685 /* see if <fp> is on the list already */
686 lpp = &np->nvfun;
687 if(lp)
688 {
689 while(lp->next && lp->next->disc)
690 {
691 if(lp->next==fp)
692 {
693 if(mode==NV_LAST && fp->next==0)
694 return(fp);
695 if(mode==NV_CLONE)
696 {
697 fp = nv_clone_disc(fp,0);
698 lp->next = fp;
699 return(fp);
700 }
701 lp->next = fp->next;
702 if(mode==NV_POP)
703 return(fp);
704 if(mode!=NV_LAST)
705 break;
706 }
707 lp = lp->next;
708 }
709 if(mode==NV_LAST)
710 lpp = &lp->next;
711 }
712 if(mode==NV_POP)
713 return(0);
714 /* push */
715 nv_offattr(np,NV_NODISC);
716 if(mode==NV_LAST)
717 fp->next = 0;
718 else
719 {
720 if((fp->nofree&1) && *lpp)
721 fp = nv_clone_disc(fp,0);
722 fp->next = *lpp;
723 }
724 *lpp = fp;
725 }
726 else
727 {
728 if(mode==NV_FIRST)
729 return(np->nvfun);
730 else if(mode==NV_LAST)
731 for(lp=np->nvfun; lp; fp=lp,lp=lp->next);
732 else if(fp = np->nvfun)
733 np->nvfun = fp->next;
734 }
735 return(fp);
736 }
737
738 /*
739 * returns discipline pointer if discipline with specified functions
740 * is on the discipline stack
741 */
nv_hasdisc(Namval_t * np,const Namdisc_t * dp)742 Namfun_t *nv_hasdisc(Namval_t *np, const Namdisc_t *dp)
743 {
744 register Namfun_t *fp;
745 for(fp=np->nvfun; fp; fp = fp->next)
746 {
747 if(fp->disc== dp)
748 return(fp);
749 }
750 return(0);
751 }
752
753 struct notify
754 {
755 Namfun_t hdr;
756 char **ptr;
757 };
758
put_notify(Namval_t * np,const char * val,int flags,Namfun_t * fp)759 static void put_notify(Namval_t* np,const char *val,int flags,Namfun_t *fp)
760 {
761 struct notify *pp = (struct notify*)fp;
762 nv_putv(np,val,flags,fp);
763 nv_stack(np,fp);
764 nv_stack(np,(Namfun_t*)0);
765 *pp->ptr = 0;
766 if(!(fp->nofree&1))
767 free((void*)fp);
768 }
769
770 static const Namdisc_t notify_disc = { 0, put_notify };
771
nv_unsetnotify(Namval_t * np,char ** addr)772 int nv_unsetnotify(Namval_t *np, char **addr)
773 {
774 register Namfun_t *fp;
775 for(fp=np->nvfun;fp;fp=fp->next)
776 {
777 if(fp->disc->putval==put_notify && ((struct notify*)fp)->ptr==addr)
778 {
779 nv_stack(np,fp);
780 nv_stack(np,(Namfun_t*)0);
781 if(!(fp->nofree&1))
782 free((void*)fp);
783 return(1);
784 }
785 }
786 return(0);
787 }
788
nv_setnotify(Namval_t * np,char ** addr)789 int nv_setnotify(Namval_t *np, char **addr)
790 {
791 struct notify *pp = newof(0,struct notify, 1,0);
792 if(!pp)
793 return(0);
794 pp->ptr = addr;
795 pp->hdr.disc = ¬ify_disc;
796 nv_stack(np,&pp->hdr);
797 return(1);
798 }
799
newnode(const char * name)800 static void *newnode(const char *name)
801 {
802 register int s;
803 register Namval_t *np = newof(0,Namval_t,1,s=strlen(name)+1);
804 if(np)
805 {
806 np->nvname = (char*)np+sizeof(Namval_t);
807 memcpy(np->nvname,name,s);
808 }
809 return((void*)np);
810 }
811
812 #if SHOPT_NAMESPACE
813 /*
814 * clone a numeric value
815 */
num_clone(register Namval_t * np,void * val)816 static void *num_clone(register Namval_t *np, void *val)
817 {
818 register int size;
819 void *nval;
820 if(!val)
821 return(0);
822 if(nv_isattr(np,NV_DOUBLE)==NV_DOUBLE)
823 {
824 if(nv_isattr(np,NV_LONG))
825 size = sizeof(Sfdouble_t);
826 else if(nv_isattr(np,NV_SHORT))
827 size = sizeof(float);
828 else
829 size = sizeof(double);
830 }
831 else
832 {
833 if(nv_isattr(np,NV_LONG))
834 size = sizeof(Sflong_t);
835 else if(nv_isattr(np,NV_SHORT))
836 {
837 if(nv_isattr(np,NV_INT16P)==NV_INT16P)
838 size = sizeof(short);
839 else
840 return((void*)np->nvalue.ip);
841 }
842 else
843 size = sizeof(int32_t);
844 }
845 if(!(nval = malloc(size)))
846 return(0);
847 memcpy(nval,val,size);
848 return(nval);
849 }
850
clone_all_disc(Namval_t * np,Namval_t * mp,int flags)851 void clone_all_disc( Namval_t *np, Namval_t *mp, int flags)
852 {
853 register Namfun_t *fp, **mfp = &mp->nvfun, *nfp, *fpnext;
854 for(fp=np->nvfun; fp;fp=fpnext)
855 {
856 fpnext = fp->next;
857 if(!fpnext && (flags&NV_COMVAR) && fp->disc && fp->disc->namef)
858 return;
859 if((fp->nofree&2) && (flags&NV_NODISC))
860 nfp = 0;
861 if(fp->disc && fp->disc->clonef)
862 nfp = (*fp->disc->clonef)(np,mp,flags,fp);
863 else if(flags&NV_MOVE)
864 nfp = fp;
865 else
866 nfp = nv_clone_disc(fp,flags);
867 if(!nfp)
868 continue;
869 nfp->next = 0;
870 *mfp = nfp;
871 mfp = &nfp->next;
872 }
873 }
874
875 /*
876 * clone <mp> from <np> flags can be one of the following
877 * NV_APPEND - append <np> onto <mp>
878 * NV_MOVE - move <np> to <mp>
879 * NV_NOFREE - mark the new node as nofree
880 * NV_NODISC - discplines with funs non-zero will not be copied
881 * NV_COMVAR - cloning a compound variable
882 */
nv_clone(Namval_t * np,Namval_t * mp,int flags)883 int nv_clone(Namval_t *np, Namval_t *mp, int flags)
884 {
885 Namfun_t *fp, *fpnext;
886 const char *val = mp->nvalue.cp;
887 unsigned short flag = mp->nvflag;
888 unsigned short size = mp->nvsize;
889 for(fp=mp->nvfun; fp; fp=fpnext)
890 {
891 fpnext = fp->next;
892 if(!fpnext && (flags&NV_COMVAR) && fp->disc && fp->disc->namef)
893 break;
894 if(!(fp->nofree&1))
895 free((void*)fp);
896 }
897 mp->nvfun = fp;
898 if(fp=np->nvfun)
899 {
900 if(nv_isattr(mp,NV_EXPORT|NV_MINIMAL) == (NV_EXPORT|NV_MINIMAL))
901 {
902 mp->nvenv = 0;
903 nv_offattr(mp,NV_MINIMAL);
904 }
905 if(!(flags&NV_COMVAR) && !nv_isattr(np,NV_MINIMAL) && np->nvenv && !(nv_isattr(mp,NV_MINIMAL)))
906 mp->nvenv = np->nvenv;
907 mp->nvflag &= NV_MINIMAL;
908 mp->nvflag |= np->nvflag&~(NV_ARRAY|NV_MINIMAL|NV_NOFREE);
909 flag = mp->nvflag;
910 clone_all_disc(np, mp, flags);
911 }
912 if(flags&NV_APPEND)
913 return(1);
914 if(mp->nvsize == size)
915 nv_setsize(mp,nv_size(np));
916 if(mp->nvflag == flag)
917 mp->nvflag = (np->nvflag&~(NV_MINIMAL))|(mp->nvflag&NV_MINIMAL);
918 if(nv_isattr(np,NV_EXPORT))
919 mp->nvflag |= (np->nvflag&NV_MINIMAL);
920 if(mp->nvalue.cp==val && !nv_isattr(np,NV_INTEGER))
921 {
922 if(np->nvalue.cp && np->nvalue.cp!=Empty && (flags&NV_COMVAR) && !(flags&NV_MOVE))
923 {
924 if(size)
925 mp->nvalue.cp = (char*)memdup(np->nvalue.cp,size);
926 else
927 mp->nvalue.cp = strdup(np->nvalue.cp);
928 nv_offattr(mp,NV_NOFREE);
929 }
930 else if(!(mp->nvalue.cp = np->nvalue.cp))
931 nv_offattr(mp,NV_NOFREE);
932 }
933 if(flags&NV_MOVE)
934 {
935 if(nv_isattr(np,NV_INTEGER))
936 mp->nvalue.ip = np->nvalue.ip;
937 np->nvfun = 0;
938 np->nvalue.cp = 0;
939 if(!nv_isattr(np,NV_MINIMAL) || nv_isattr(mp,NV_EXPORT))
940 {
941 mp->nvenv = np->nvenv;
942 np->nvenv = 0;
943 np->nvflag = 0;
944 }
945 else
946 np->nvflag &= NV_MINIMAL;
947 nv_setsize(np,0);
948 return(1);
949 }
950 if(nv_isattr(np,NV_INTEGER) && mp->nvalue.ip!=np->nvalue.ip)
951 {
952 mp->nvalue.ip = (int*)num_clone(np,(void*)np->nvalue.ip);
953 nv_offattr(mp,NV_NOFREE);
954 }
955 else if(flags&NV_NOFREE)
956 nv_onattr(np,NV_NOFREE);
957 return(1);
958 }
959
960 /*
961 * The following discipline is for copy-on-write semantics
962 */
clone_getv(Namval_t * np,Namfun_t * handle)963 static char* clone_getv(Namval_t *np, Namfun_t *handle)
964 {
965 return(np->nvalue.np?nv_getval(np->nvalue.np):0);
966 }
967
clone_getn(Namval_t * np,Namfun_t * handle)968 static Sfdouble_t clone_getn(Namval_t *np, Namfun_t *handle)
969 {
970 return(np->nvalue.np?nv_getnum(np->nvalue.np):0);
971 }
972
clone_putv(Namval_t * np,const char * val,int flags,Namfun_t * handle)973 static void clone_putv(Namval_t *np,const char* val,int flags,Namfun_t *handle)
974 {
975 Namfun_t *dp = nv_stack(np,(Namfun_t*)0);
976 Namval_t *mp = np->nvalue.np;
977 if(!sh.subshell)
978 free((void*)dp);
979 if(val)
980 nv_clone(mp,np,NV_NOFREE);
981 np->nvalue.cp = 0;
982 nv_putval(np,val,flags);
983 }
984
985 static const Namdisc_t clone_disc =
986 {
987 0,
988 clone_putv,
989 clone_getv,
990 clone_getn
991 };
992
nv_mkclone(Namval_t * mp)993 Namval_t *nv_mkclone(Namval_t *mp)
994 {
995 Namval_t *np;
996 Namfun_t *dp;
997 np = newof(0,Namval_t,1,0);
998 np->nvflag = mp->nvflag;
999 np->nvsize = mp->nvsize;
1000 np->nvname = mp->nvname;
1001 np->nvalue.np = mp;
1002 np->nvflag = mp->nvflag;
1003 dp = newof(0,Namfun_t,1,0);
1004 dp->disc = &clone_disc;
1005 nv_stack(np,dp);
1006 dtinsert(nv_dict(sh.namespace),np);
1007 return(np);
1008 }
1009 #endif /* SHOPT_NAMESPACE */
1010
nv_search(const char * name,Dt_t * root,int mode)1011 Namval_t *nv_search(const char *name, Dt_t *root, int mode)
1012 {
1013 register Namval_t *np;
1014 register Dt_t *dp = 0;
1015 if(mode&HASH_NOSCOPE)
1016 dp = dtview(root,0);
1017 if(mode&HASH_BUCKET)
1018 {
1019 Namval_t *mp = (void*)name;
1020 if(!(np = dtsearch(root,mp)) && (mode&NV_ADD))
1021 name = nv_name(mp);
1022 }
1023 else
1024 {
1025 if(*name=='.' && root==sh.var_tree && !dp)
1026 root = sh.var_base;
1027 np = dtmatch(root,(void*)name);
1028 }
1029 if(!np && (mode&NV_ADD))
1030 {
1031 if(sh.namespace && !(mode&HASH_NOSCOPE) && root==sh.var_tree)
1032 root = nv_dict(sh.namespace);
1033 else if(!dp && !(mode&HASH_NOSCOPE))
1034 {
1035 register Dt_t *next;
1036 while(next=dtvnext(root))
1037 root = next;
1038 }
1039 np = (Namval_t*)dtinsert(root,newnode(name));
1040 }
1041 if(dp)
1042 dtview(root,dp);
1043 return(np);
1044 }
1045
1046 /*
1047 * finds function or builtin for given name and the discipline variable
1048 * if var!=0 the variable pointer is returned and the built-in name
1049 * is put onto the stack at the current offset.
1050 * otherwise, a pointer to the builtin (variable or type) is returned
1051 * and var contains the poiner to the variable
1052 * if last==0 and first component of name is a reference, nv_bfsearch()
1053 will return 0.
1054 */
nv_bfsearch(const char * name,Dt_t * root,Namval_t ** var,char ** last)1055 Namval_t *nv_bfsearch(const char *name, Dt_t *root, Namval_t **var, char **last)
1056 {
1057 int c,offset = staktell();
1058 register char *sp, *cp=0;
1059 Namval_t *np, *nq;
1060 char *dname=0;
1061 if(var)
1062 *var = 0;
1063 /* check for . in the name before = */
1064 for(sp=(char*)name+1; *sp; sp++)
1065 {
1066 if(*sp=='=')
1067 return(0);
1068 if(*sp=='[')
1069 {
1070 if(sp[-1]!='.')
1071 dname = sp;
1072 while(*sp=='[')
1073 {
1074 sp = nv_endsubscript((Namval_t*)0,(char*)sp,0);
1075 if(sp[-1]!=']')
1076 return(0);
1077 }
1078 if(*sp==0)
1079 break;
1080 if(*sp!='.')
1081 return(0);
1082 if(dname)
1083 {
1084 cp = dname;
1085 dname = sp+1;
1086 }
1087 }
1088 else if(*sp=='.')
1089 cp = sp;
1090 }
1091 if(!cp)
1092 return(var?nv_search(name,root,0):0);
1093 stakputs(name);
1094 stakputc(0);
1095 if(!dname)
1096 dname = cp+1;
1097 cp = stakptr(offset) + (cp-name);
1098 if(last)
1099 *last = cp;
1100 c = *cp;
1101 *cp = 0;
1102 nq=nv_open(stakptr(offset),0,NV_VARNAME|NV_ARRAY|NV_NOASSIGN|NV_NOADD|NV_NOFAIL);
1103 *cp = c;
1104 if(!nq)
1105 {
1106 np = 0;
1107 goto done;
1108 }
1109 if(!var)
1110 {
1111 np = nq;
1112 goto done;
1113 }
1114 *var = nq;
1115 if(c=='[')
1116 nv_endsubscript(nq, cp,NV_NOADD);
1117 return((Namval_t*)nv_setdisc(nq,dname,nq,(Namfun_t*)nq));
1118 done:
1119 stakseek(offset);
1120 return(np);
1121 }
1122
1123 /*
1124 * add or replace built-in version of command corresponding to <path>
1125 * The <bltin> argument is a pointer to the built-in
1126 * if <extra>==1, the built-in will be deleted
1127 * Special builtins cannot be added or deleted return failure
1128 * The return value for adding builtins is a pointer to the node or NULL on
1129 * failure. For delete NULL means success and the node that cannot be
1130 * deleted is returned on failure.
1131 */
sh_addbuiltin(const char * path,int (* bltin)(int,char * [],void *),void * extra)1132 Namval_t *sh_addbuiltin(const char *path, int (*bltin)(int, char*[],void*),void *extra)
1133 {
1134 register const char *name = path_basename(path);
1135 char *cp;
1136 register Namval_t *np, *nq=0;
1137 int offset=staktell();
1138 if(name==path && (nq=nv_bfsearch(name,sh.bltin_tree,(Namval_t**)0,&cp)))
1139 path = name = stakptr(offset);
1140 if(np = nv_search(path,sh.bltin_tree,0))
1141 {
1142 /* exists without a path */
1143 if(extra == (void*)1)
1144 {
1145 if(np->nvfun && !nv_isattr(np,NV_NOFREE))
1146 free((void*)np->nvfun);
1147 dtdelete(sh.bltin_tree,np);
1148 return(0);
1149 }
1150 if(!bltin)
1151 return(np);
1152 }
1153 else for(np=(Namval_t*)dtfirst(sh.bltin_tree);np;np=(Namval_t*)dtnext(sh.bltin_tree,np))
1154 {
1155 if(strcmp(name,path_basename(nv_name(np))))
1156 continue;
1157 /* exists probably with different path so delete it */
1158 if(strcmp(path,nv_name(np)))
1159 {
1160 if(nv_isattr(np,BLT_SPC))
1161 return(np);
1162 if(!bltin)
1163 bltin = np->nvalue.bfp;
1164 if(np->nvenv)
1165 dtdelete(sh.bltin_tree,np);
1166 if(extra == (void*)1)
1167 return(0);
1168 np = 0;
1169 }
1170 break;
1171 }
1172 if(!np && !(np = nv_search(path,sh.bltin_tree,bltin?NV_ADD:0)))
1173 return(0);
1174 if(nv_isattr(np,BLT_SPC))
1175 {
1176 if(extra)
1177 np->nvfun = (Namfun_t*)extra;
1178 return(np);
1179 }
1180 np->nvenv = 0;
1181 np->nvfun = 0;
1182 if(bltin)
1183 {
1184 np->nvalue.bfp = bltin;
1185 nv_onattr(np,NV_BLTIN|NV_NOFREE);
1186 np->nvfun = (Namfun_t*)extra;
1187 }
1188 if(nq)
1189 {
1190 cp=nv_setdisc(nq,cp+1,np,(Namfun_t*)nq);
1191 nv_close(nq);
1192 if(!cp)
1193 errormsg(SH_DICT,ERROR_exit(1),e_baddisc,name);
1194 }
1195 if(extra == (void*)1)
1196 return(0);
1197 return(np);
1198 }
1199
1200 #undef nv_stack
nv_stack(register Namval_t * np,register Namfun_t * fp)1201 extern Namfun_t *nv_stack(register Namval_t *np, register Namfun_t* fp)
1202 {
1203 return(nv_disc(np,fp,0));
1204 }
1205
1206 struct table
1207 {
1208 Namfun_t fun;
1209 Namval_t *parent;
1210 Shell_t *shp;
1211 Dt_t *dict;
1212 };
1213
next_table(register Namval_t * np,Dt_t * root,Namfun_t * fp)1214 static Namval_t *next_table(register Namval_t* np, Dt_t *root,Namfun_t *fp)
1215 {
1216 struct table *tp = (struct table *)fp;
1217 if(root)
1218 return((Namval_t*)dtnext(root,np));
1219 else
1220 return((Namval_t*)dtfirst(tp->dict));
1221 }
1222
create_table(Namval_t * np,const char * name,int flags,Namfun_t * fp)1223 static Namval_t *create_table(Namval_t *np,const char *name,int flags,Namfun_t *fp)
1224 {
1225 struct table *tp = (struct table *)fp;
1226 tp->shp->last_table = np;
1227 return(nv_create(name, tp->dict, flags, fp));
1228 }
1229
clone_table(Namval_t * np,Namval_t * mp,int flags,Namfun_t * fp)1230 static Namfun_t *clone_table(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp)
1231 {
1232 struct table *tp = (struct table*)fp;
1233 struct table *ntp = (struct table*)nv_clone_disc(fp,0);
1234 Dt_t *oroot=tp->dict,*nroot=dtopen(&_Nvdisc,Dtoset);
1235 if(!nroot)
1236 return(0);
1237 memcpy((void*)ntp,(void*)fp,sizeof(struct table));
1238 ntp->dict = nroot;
1239 ntp->parent = nv_lastdict();
1240 for(np=(Namval_t*)dtfirst(oroot);np;np=(Namval_t*)dtnext(oroot,np))
1241 {
1242 mp = (Namval_t*)dtinsert(nroot,newnode(np->nvname));
1243 nv_clone(np,mp,flags);
1244 }
1245 return(&ntp->fun);
1246 }
1247
put_table(register Namval_t * np,const char * val,int flags,Namfun_t * fp)1248 static void put_table(register Namval_t* np, const char* val, int flags, Namfun_t* fp)
1249 {
1250 register Dt_t *root = ((struct table*)fp)->dict;
1251 register Namval_t *nq, *mp;
1252 Namarr_t *ap;
1253 nv_putv(np,val,flags,fp);
1254 if(val)
1255 return;
1256 if(nv_isarray(np) && (ap=nv_arrayptr(np)) && array_elem(ap))
1257 return;
1258 for(mp=(Namval_t*)dtfirst(root);mp;mp=nq)
1259 {
1260 _nv_unset(mp,flags);
1261 nq = (Namval_t*)dtnext(root,mp);
1262 dtdelete(root,mp);
1263 free((void*)mp);
1264 }
1265 dtclose(root);
1266 if(!(fp->nofree&1))
1267 free((void*)fp);
1268 }
1269
1270 /*
1271 * return space separated list of names of variables in given tree
1272 */
get_table(Namval_t * np,Namfun_t * fp)1273 static char *get_table(Namval_t *np, Namfun_t *fp)
1274 {
1275 register Dt_t *root = ((struct table*)fp)->dict;
1276 static Sfio_t *out;
1277 register int first=1;
1278 register Dt_t *base = dtview(root,0);
1279 if(out)
1280 sfseek(out,(Sfoff_t)0,SEEK_SET);
1281 else
1282 out = sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING);
1283 for(np=(Namval_t*)dtfirst(root);np;np=(Namval_t*)dtnext(root,np))
1284 {
1285 if(!nv_isnull(np) || np->nvfun || nv_isattr(np,~NV_NOFREE))
1286 {
1287 if(!first)
1288 sfputc(out,' ');
1289 else
1290 first = 0;
1291 sfputr(out,np->nvname,-1);
1292 }
1293 }
1294 sfputc(out,0);
1295 if(base)
1296 dtview(root,base);
1297 return((char*)out->_data);
1298 }
1299
1300 static const Namdisc_t table_disc =
1301 {
1302 sizeof(struct table),
1303 put_table,
1304 get_table,
1305 0,
1306 0,
1307 create_table,
1308 clone_table,
1309 0,
1310 next_table,
1311 };
1312
nv_parent(Namval_t * np)1313 Namval_t *nv_parent(Namval_t *np)
1314 {
1315 struct table *tp = (struct table *)nv_hasdisc(np,&table_disc);
1316 if(tp)
1317 return(tp->parent);
1318 return(0);
1319 }
1320
nv_dict(Namval_t * np)1321 Dt_t *nv_dict(Namval_t* np)
1322 {
1323 struct table *tp = (struct table*)nv_hasdisc(np,&table_disc);
1324 if(tp)
1325 return(tp->dict);
1326 np = sh.last_table;
1327 while(np)
1328 {
1329 if(tp = (struct table*)nv_hasdisc(np,&table_disc))
1330 return(tp->dict);
1331 #if 0
1332 np = nv_create(np,(const char*)0, NV_FIRST, (Namfun_t*)0);
1333 #else
1334 break;
1335 #endif
1336 }
1337 return(sh.var_tree);
1338 }
1339
1340 /*
1341 * create a mountable name-value pair tree
1342 */
nv_mount(Namval_t * np,const char * name,Dt_t * dict)1343 Namval_t *nv_mount(Namval_t *np, const char *name, Dt_t *dict)
1344 {
1345 Namval_t *mp, *pp=0;
1346 struct table *tp = newof((struct table*)0, struct table,1,0);
1347 if(name)
1348 {
1349 if(nv_istable(np))
1350 pp = np;
1351 else
1352 pp = nv_lastdict();
1353 }
1354 if(!(tp = newof((struct table*)0, struct table,1,0)))
1355 return(0);
1356 if(name)
1357 {
1358 Namfun_t *fp = pp->nvfun;
1359 mp = (*fp->disc->createf)(pp,name,0,fp);
1360 }
1361 else
1362 mp = np;
1363 if(!nv_isnull(mp))
1364 nv_unset(mp);
1365 tp->shp = sh_getinterp();
1366 tp->dict = dict;
1367 tp->parent = pp;
1368 tp->fun.disc = &table_disc;
1369 nv_onattr(mp,NV_TABLE);
1370 nv_disc(mp, &tp->fun, NV_FIRST);
1371 return(mp);
1372 }
1373
nv_discfun(int which)1374 const Namdisc_t *nv_discfun(int which)
1375 {
1376 switch(which)
1377 {
1378 case NV_DCADD:
1379 return(&Nv_bdisc);
1380 case NV_DCRESTRICT:
1381 return(&RESTRICTED_disc);
1382 }
1383 return(0);
1384 }
1385
nv_hasget(Namval_t * np)1386 int nv_hasget(Namval_t *np)
1387 {
1388 register Namfun_t *fp;
1389 for(fp=np->nvfun; fp; fp=fp->next)
1390 {
1391 if(!fp->disc || (!fp->disc->getnum && !fp->disc->getval))
1392 continue;
1393 return(1);
1394 }
1395 return(0);
1396 }
1397