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 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 409 static char* lookups(Namval_t *np, Namfun_t *handle) 410 { 411 return(lookup(np,LOOKUPS,(Sfdouble_t*)0,handle)); 412 } 413 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 */ 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 */ 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 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 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 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 */ 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 */ 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 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 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 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 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 */ 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 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 */ 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 */ 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 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 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 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 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 */ 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 */ 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 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 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 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 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 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 */ 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 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 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 */ 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 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 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