1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1982-2009 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 if(vp && !vp->fun.disc) 435 vp = 0; 436 if(np == (Namval_t*)fp) 437 { 438 register const char *name; 439 register int getname=0; 440 /* top level call, check for get/set */ 441 if(!event) 442 { 443 if(!action) 444 return((char*)nv_discnames[0]); 445 getname=1; 446 event = (char*)action; 447 } 448 for(type=0; name=nv_discnames[type]; type++) 449 { 450 if(strcmp(event,name)==0) 451 break; 452 } 453 if(getname) 454 { 455 event = 0; 456 if(name && !(name = nv_discnames[++type])) 457 action = 0; 458 } 459 if(!name) 460 { 461 for(fp=(Namfun_t*)vp; fp; fp=fp->next) 462 { 463 if(fp->disc && fp->disc->setdisc) 464 return((*fp->disc->setdisc)(np,event,action,fp)); 465 } 466 } 467 else if(getname) 468 return((char*)name); 469 } 470 if(!fp) 471 return(NIL(char*)); 472 if(np != (Namval_t*)fp) 473 { 474 /* not the top level */ 475 while(fp = fp->next) 476 { 477 if(fp->disc && fp->disc->setdisc) 478 return((*fp->disc->setdisc)(np,event,action,fp)); 479 } 480 return(NIL(char*)); 481 } 482 /* Handle GET/SET/APPEND/UNSET disc */ 483 if(vp && vp->fun.disc->putval!=assign) 484 vp = 0; 485 if(!vp) 486 { 487 Namdisc_t *dp; 488 if(action==np) 489 return((char*)action); 490 if(!(vp = newof(NIL(struct vardisc*),struct vardisc,1,sizeof(Namdisc_t)))) 491 return(0); 492 dp = (Namdisc_t*)(vp+1); 493 vp->fun.disc = dp; 494 memset(dp,0,sizeof(*dp)); 495 dp->dsize = sizeof(struct vardisc); 496 dp->putval = assign; 497 nv_stack(np, (Namfun_t*)vp); 498 } 499 if(action==np) 500 { 501 action = vp->disc[type]; 502 empty = 0; 503 } 504 else if(action) 505 { 506 Namdisc_t *dp = (Namdisc_t*)vp->fun.disc; 507 if(type==LOOKUPS) 508 dp->getval = lookups; 509 else if(type==LOOKUPN) 510 dp->getnum = lookupn; 511 vp->disc[type] = action; 512 } 513 else 514 { 515 struct blocked *bp; 516 action = vp->disc[type]; 517 vp->disc[type] = 0; 518 if(!(bp=block_info(np,(struct blocked*)0)) || !isblocked(bp,UNASSIGN)) 519 chktfree(np,vp); 520 } 521 return(action?(char*)action:empty); 522 } 523 524 /* 525 * Set disc on given <event> to <action> 526 * If action==np, the current disc is returned 527 * A null return value indicates that no <event> is known for <np> 528 * If <event> is NULL, then return the event name after <action> 529 * If <event> is NULL, and <action> is NULL, return the first event 530 */ 531 static char *setdisc(register Namval_t* np,register const char *event,Namval_t *action,register Namfun_t *fp) 532 { 533 register Nambfun_t *vp = (Nambfun_t*)fp; 534 register int type,getname=0; 535 register const char *name; 536 const char **discnames = vp->bnames; 537 /* top level call, check for discipline match */ 538 if(!event) 539 { 540 if(!action) 541 return((char*)discnames[0]); 542 getname=1; 543 event = (char*)action; 544 } 545 for(type=0; name=discnames[type]; type++) 546 { 547 if(strcmp(event,name)==0) 548 break; 549 } 550 if(getname) 551 { 552 event = 0; 553 if(name && !(name = discnames[++type])) 554 action = 0; 555 } 556 if(!name) 557 return(nv_setdisc(np,event,action,fp)); 558 else if(getname) 559 return((char*)name); 560 /* Handle the disciplines */ 561 if(action==np) 562 action = vp->bltins[type]; 563 else if(action) 564 vp->bltins[type] = action; 565 else 566 { 567 action = vp->bltins[type]; 568 vp->bltins[type] = 0; 569 } 570 return(action?(char*)action:""); 571 } 572 573 static void putdisc(Namval_t* np, const char* val, int flag, Namfun_t* fp) 574 { 575 nv_putv(np,val,flag,fp); 576 if(!val && !(flag&NV_NOFREE)) 577 { 578 register Nambfun_t *vp = (Nambfun_t*)fp; 579 register int i; 580 for(i=0; vp->bnames[i]; i++) 581 { 582 register Namval_t *mp; 583 if((mp=vp->bltins[i]) && !nv_isattr(mp,NV_NOFREE)) 584 { 585 if(is_abuiltin(mp)) 586 { 587 if(mp->nvfun && !nv_isattr(mp,NV_NOFREE)) 588 free((void*)mp->nvfun); 589 dtdelete(sh.bltin_tree,mp); 590 free((void*)mp); 591 } 592 } 593 } 594 nv_disc(np,fp,NV_POP); 595 if(!(fp->nofree&1)) 596 free((void*)fp); 597 598 } 599 } 600 601 static const Namdisc_t Nv_bdisc = { 0, putdisc, 0, 0, setdisc }; 602 603 Namfun_t *nv_clone_disc(register Namfun_t *fp, int flags) 604 { 605 register Namfun_t *nfp; 606 register int size; 607 if(!fp->disc && !fp->next && (fp->nofree&1)) 608 return(fp); 609 if(!(size=fp->dsize) && (!fp->disc || !(size=fp->disc->dsize))) 610 size = sizeof(Namfun_t); 611 if(!(nfp=newof(NIL(Namfun_t*),Namfun_t,1,size-sizeof(Namfun_t)))) 612 return(0); 613 memcpy(nfp,fp,size); 614 nfp->nofree &= ~1; 615 nfp->nofree |= (flags&NV_RDONLY)?1:0; 616 return(nfp); 617 } 618 619 int nv_adddisc(Namval_t *np, const char **names, Namval_t **funs) 620 { 621 register Nambfun_t *vp; 622 register int n=0; 623 register const char **av=names; 624 if(av) 625 { 626 while(*av++) 627 n++; 628 } 629 if(!(vp = newof(NIL(Nambfun_t*),Nambfun_t,1,n*sizeof(Namval_t*)))) 630 return(0); 631 vp->fun.dsize = sizeof(Nambfun_t)+n*sizeof(Namval_t*); 632 vp->fun.nofree |= 2; 633 vp->num = n; 634 if(funs) 635 memcpy((void*)vp->bltins, (void*)funs,n*sizeof(Namval_t*)); 636 else while(n>=0) 637 vp->bltins[n--] = 0; 638 vp->fun.disc = &Nv_bdisc; 639 vp->bnames = names; 640 nv_stack(np,&vp->fun); 641 return(1); 642 } 643 644 /* 645 * push, pop, clne, or reorder disciplines onto node <np> 646 * mode can be one of 647 * NV_FIRST: Move or push <fp> to top of the stack or delete top 648 * NV_LAST: Move or push <fp> to bottom of stack or delete last 649 * NV_POP: Delete <fp> from top of the stack 650 * NV_CLONE: Replace fp with a copy created my malloc() and return it 651 */ 652 Namfun_t *nv_disc(register Namval_t *np, register Namfun_t* fp, int mode) 653 { 654 Namfun_t *lp, **lpp; 655 if(nv_isref(np)) 656 return(0); 657 if(mode==NV_CLONE && !fp) 658 return(0); 659 if(fp) 660 { 661 fp->subshell = sh.subshell; 662 if((lp=np->nvfun)==fp) 663 { 664 if(mode==NV_CLONE) 665 { 666 lp = nv_clone_disc(fp,0); 667 return(np->nvfun=lp); 668 } 669 if(mode==NV_FIRST || mode==0) 670 return(fp); 671 np->nvfun = lp->next; 672 if(mode==NV_POP) 673 return(fp); 674 if(mode==NV_LAST && (lp->next==0 || lp->next->disc==0)) 675 return(fp); 676 } 677 /* see if <fp> is on the list already */ 678 lpp = &np->nvfun; 679 if(lp) 680 { 681 while(lp->next && lp->next->disc) 682 { 683 if(lp->next==fp) 684 { 685 if(mode==NV_LAST && fp->next==0) 686 return(fp); 687 if(mode==NV_CLONE) 688 { 689 fp = nv_clone_disc(fp,0); 690 lp->next = fp; 691 return(fp); 692 } 693 lp->next = fp->next; 694 if(mode==NV_POP) 695 return(fp); 696 if(mode!=NV_LAST) 697 break; 698 } 699 lp = lp->next; 700 } 701 if(mode==NV_LAST) 702 lpp = &lp->next; 703 } 704 if(mode==NV_POP) 705 return(0); 706 /* push */ 707 nv_offattr(np,NV_NODISC); 708 if(mode==NV_LAST) 709 fp->next = 0; 710 else 711 { 712 if((fp->nofree&1) && *lpp) 713 fp = nv_clone_disc(fp,0); 714 fp->next = *lpp; 715 } 716 *lpp = fp; 717 } 718 else 719 { 720 if(mode==NV_FIRST) 721 return(np->nvfun); 722 else if(mode==NV_LAST) 723 for(lp=np->nvfun; lp; fp=lp,lp=lp->next); 724 else if(fp = np->nvfun) 725 np->nvfun = fp->next; 726 } 727 return(fp); 728 } 729 730 /* 731 * returns discipline pointer if discipline with specified functions 732 * is on the discipline stack 733 */ 734 Namfun_t *nv_hasdisc(Namval_t *np, const Namdisc_t *dp) 735 { 736 register Namfun_t *fp; 737 for(fp=np->nvfun; fp; fp = fp->next) 738 { 739 if(fp->disc== dp) 740 return(fp); 741 } 742 return(0); 743 } 744 745 struct notify 746 { 747 Namfun_t hdr; 748 char **ptr; 749 }; 750 751 static void put_notify(Namval_t* np,const char *val,int flags,Namfun_t *fp) 752 { 753 struct notify *pp = (struct notify*)fp; 754 nv_putv(np,val,flags,fp); 755 nv_stack(np,fp); 756 nv_stack(np,(Namfun_t*)0); 757 *pp->ptr = 0; 758 if(!(fp->nofree&1)) 759 free((void*)fp); 760 } 761 762 static const Namdisc_t notify_disc = { 0, put_notify }; 763 764 int nv_unsetnotify(Namval_t *np, char **addr) 765 { 766 register Namfun_t *fp; 767 for(fp=np->nvfun;fp;fp=fp->next) 768 { 769 if(fp->disc->putval==put_notify && ((struct notify*)fp)->ptr==addr) 770 { 771 nv_stack(np,fp); 772 nv_stack(np,(Namfun_t*)0); 773 if(!(fp->nofree&1)) 774 free((void*)fp); 775 return(1); 776 } 777 } 778 return(0); 779 } 780 781 int nv_setnotify(Namval_t *np, char **addr) 782 { 783 struct notify *pp = newof(0,struct notify, 1,0); 784 if(!pp) 785 return(0); 786 pp->ptr = addr; 787 pp->hdr.disc = ¬ify_disc; 788 nv_stack(np,&pp->hdr); 789 return(1); 790 } 791 792 static void *newnode(const char *name) 793 { 794 register int s; 795 register Namval_t *np = newof(0,Namval_t,1,s=strlen(name)+1); 796 if(np) 797 { 798 np->nvname = (char*)np+sizeof(Namval_t); 799 memcpy(np->nvname,name,s); 800 } 801 return((void*)np); 802 } 803 804 #if SHOPT_NAMESPACE 805 /* 806 * clone a numeric value 807 */ 808 static void *num_clone(register Namval_t *np, void *val) 809 { 810 register int size; 811 void *nval; 812 if(!val) 813 return(0); 814 if(nv_isattr(np,NV_DOUBLE)==NV_DOUBLE) 815 { 816 if(nv_isattr(np,NV_LONG)) 817 size = sizeof(Sfdouble_t); 818 else if(nv_isattr(np,NV_SHORT)) 819 size = sizeof(float); 820 else 821 size = sizeof(double); 822 } 823 else 824 { 825 if(nv_isattr(np,NV_LONG)) 826 size = sizeof(Sflong_t); 827 else if(nv_isattr(np,NV_SHORT)) 828 { 829 if(nv_isattr(np,NV_INT16P)==NV_INT16P) 830 size = sizeof(short); 831 else 832 return((void*)np->nvalue.ip); 833 } 834 else 835 size = sizeof(int32_t); 836 } 837 if(!(nval = malloc(size))) 838 return(0); 839 memcpy(nval,val,size); 840 return(nval); 841 } 842 843 void clone_all_disc( Namval_t *np, Namval_t *mp, int flags) 844 { 845 register Namfun_t *fp, **mfp = &mp->nvfun, *nfp, *fpnext; 846 for(fp=np->nvfun; fp;fp=fpnext) 847 { 848 fpnext = fp->next; 849 if(!fpnext && (flags&NV_COMVAR) && fp->disc && fp->disc->namef) 850 return; 851 if((fp->nofree&2) && (flags&NV_NODISC)) 852 nfp = 0; 853 if(fp->disc && fp->disc->clonef) 854 nfp = (*fp->disc->clonef)(np,mp,flags,fp); 855 else if(flags&NV_MOVE) 856 nfp = fp; 857 else 858 nfp = nv_clone_disc(fp,flags); 859 if(!nfp) 860 continue; 861 nfp->next = 0; 862 *mfp = nfp; 863 mfp = &nfp->next; 864 } 865 } 866 867 /* 868 * clone <mp> from <np> flags can be one of the following 869 * NV_APPEND - append <np> onto <mp> 870 * NV_MOVE - move <np> to <mp> 871 * NV_NOFREE - mark the new node as nofree 872 * NV_NODISC - discplines with funs non-zero will not be copied 873 * NV_COMVAR - cloning a compound variable 874 */ 875 int nv_clone(Namval_t *np, Namval_t *mp, int flags) 876 { 877 Namfun_t *fp, *fpnext; 878 const char *val = mp->nvalue.cp; 879 unsigned short flag = mp->nvflag; 880 unsigned short size = mp->nvsize; 881 for(fp=mp->nvfun; fp; fp=fpnext) 882 { 883 fpnext = fp->next; 884 if(!fpnext && (flags&NV_COMVAR) && fp->disc && fp->disc->namef) 885 break; 886 if(!(fp->nofree&1)) 887 free((void*)fp); 888 } 889 mp->nvfun = fp; 890 if(fp=np->nvfun) 891 { 892 if(nv_isattr(mp,NV_EXPORT|NV_MINIMAL) == (NV_EXPORT|NV_MINIMAL)) 893 { 894 mp->nvenv = 0; 895 nv_offattr(mp,NV_MINIMAL); 896 } 897 if(!(flags&NV_COMVAR) && !nv_isattr(np,NV_MINIMAL) && np->nvenv && !(nv_isattr(mp,NV_MINIMAL))) 898 mp->nvenv = np->nvenv; 899 mp->nvflag &= NV_MINIMAL; 900 mp->nvflag |= np->nvflag&~(NV_ARRAY|NV_MINIMAL|NV_NOFREE); 901 flag = mp->nvflag; 902 clone_all_disc(np, mp, flags); 903 } 904 if(flags&NV_APPEND) 905 return(1); 906 if(mp->nvsize == size) 907 nv_setsize(mp,nv_size(np)); 908 if(mp->nvflag == flag) 909 mp->nvflag = (np->nvflag&~(NV_MINIMAL))|(mp->nvflag&NV_MINIMAL); 910 if(mp->nvalue.cp==val && !nv_isattr(np,NV_INTEGER)) 911 { 912 if(np->nvalue.cp && np->nvalue.cp!=Empty && (flags&NV_COMVAR) && !(flags&NV_MOVE)) 913 { 914 if(size) 915 mp->nvalue.cp = (char*)memdup(np->nvalue.cp,size); 916 else 917 mp->nvalue.cp = strdup(np->nvalue.cp); 918 nv_offattr(mp,NV_NOFREE); 919 } 920 else if(!(mp->nvalue.cp = np->nvalue.cp)) 921 nv_offattr(mp,NV_NOFREE); 922 } 923 if(flags&NV_MOVE) 924 { 925 if(nv_isattr(np,NV_INTEGER)) 926 mp->nvalue.ip = np->nvalue.ip; 927 np->nvfun = 0; 928 np->nvalue.cp = 0; 929 if(!nv_isattr(np,NV_MINIMAL) || nv_isattr(mp,NV_EXPORT)) 930 np->nvenv = 0; 931 np->nvflag &= NV_MINIMAL; 932 nv_setsize(np,0); 933 return(1); 934 } 935 if(nv_isattr(np,NV_INTEGER) && mp->nvalue.ip!=np->nvalue.ip) 936 { 937 mp->nvalue.ip = (int*)num_clone(np,(void*)np->nvalue.ip); 938 nv_offattr(mp,NV_NOFREE); 939 } 940 else if(flags&NV_NOFREE) 941 nv_onattr(np,NV_NOFREE); 942 return(1); 943 } 944 945 /* 946 * The following discipline is for copy-on-write semantics 947 */ 948 static char* clone_getv(Namval_t *np, Namfun_t *handle) 949 { 950 return(np->nvalue.np?nv_getval(np->nvalue.np):0); 951 } 952 953 static Sfdouble_t clone_getn(Namval_t *np, Namfun_t *handle) 954 { 955 return(np->nvalue.np?nv_getnum(np->nvalue.np):0); 956 } 957 958 static void clone_putv(Namval_t *np,const char* val,int flags,Namfun_t *handle) 959 { 960 Namfun_t *dp = nv_stack(np,(Namfun_t*)0); 961 Namval_t *mp = np->nvalue.np; 962 if(!sh.subshell) 963 free((void*)dp); 964 if(val) 965 nv_clone(mp,np,NV_NOFREE); 966 np->nvalue.cp = 0; 967 nv_putval(np,val,flags); 968 } 969 970 static const Namdisc_t clone_disc = 971 { 972 0, 973 clone_putv, 974 clone_getv, 975 clone_getn 976 }; 977 978 Namval_t *nv_mkclone(Namval_t *mp) 979 { 980 Namval_t *np; 981 Namfun_t *dp; 982 np = newof(0,Namval_t,1,0); 983 np->nvflag = mp->nvflag; 984 np->nvsize = mp->nvsize; 985 np->nvname = mp->nvname; 986 np->nvalue.np = mp; 987 np->nvflag = mp->nvflag; 988 dp = newof(0,Namfun_t,1,0); 989 dp->disc = &clone_disc; 990 nv_stack(np,dp); 991 dtinsert(nv_dict(sh.namespace),np); 992 return(np); 993 } 994 #endif /* SHOPT_NAMESPACE */ 995 996 Namval_t *nv_search(const char *name, Dt_t *root, int mode) 997 { 998 register Namval_t *np; 999 register Dt_t *dp = 0; 1000 if(mode&HASH_NOSCOPE) 1001 dp = dtview(root,0); 1002 if(mode&HASH_BUCKET) 1003 { 1004 Namval_t *mp = (void*)name; 1005 if(!(np = dtsearch(root,mp)) && (mode&NV_ADD)) 1006 name = nv_name(mp); 1007 } 1008 else 1009 { 1010 if(*name=='.' && root==sh.var_tree && !dp) 1011 root = sh.var_base; 1012 np = dtmatch(root,(void*)name); 1013 } 1014 if(!np && (mode&NV_ADD)) 1015 { 1016 if(sh.namespace && !(mode&HASH_NOSCOPE) && root==sh.var_tree) 1017 root = nv_dict(sh.namespace); 1018 else if(!dp && !(mode&HASH_NOSCOPE)) 1019 { 1020 register Dt_t *next; 1021 while(next=dtvnext(root)) 1022 root = next; 1023 } 1024 np = (Namval_t*)dtinsert(root,newnode(name)); 1025 } 1026 if(dp) 1027 dtview(root,dp); 1028 return(np); 1029 } 1030 1031 /* 1032 * finds function or builtin for given name and the discipline variable 1033 * if var!=0 the variable pointer is returned and the built-in name 1034 * is put onto the stack at the current offset. 1035 * otherwise, a pointer to the builtin (variable or type) is returned 1036 * and var contains the poiner to the variable 1037 * if last==0 and first component of name is a reference, nv_bfsearch() 1038 will return 0. 1039 */ 1040 Namval_t *nv_bfsearch(const char *name, Dt_t *root, Namval_t **var, char **last) 1041 { 1042 int c,offset = staktell(); 1043 register char *sp, *cp=0; 1044 Namval_t *np, *nq; 1045 char *dname=0; 1046 if(var) 1047 *var = 0; 1048 /* check for . in the name before = */ 1049 for(sp=(char*)name+1; *sp; sp++) 1050 { 1051 if(*sp=='=') 1052 return(0); 1053 if(*sp=='[') 1054 { 1055 if(sp[-1]!='.') 1056 dname = sp; 1057 while(*sp=='[') 1058 { 1059 sp = nv_endsubscript((Namval_t*)0,(char*)sp,0); 1060 if(sp[-1]!=']') 1061 return(0); 1062 } 1063 if(*sp==0) 1064 break; 1065 if(*sp!='.') 1066 return(0); 1067 if(dname) 1068 { 1069 cp = dname; 1070 dname = sp+1; 1071 } 1072 } 1073 else if(*sp=='.') 1074 cp = sp; 1075 } 1076 if(!cp) 1077 return(var?nv_search(name,root,0):0); 1078 stakputs(name); 1079 stakputc(0); 1080 if(!dname) 1081 dname = cp+1; 1082 cp = stakptr(offset) + (cp-name); 1083 if(last) 1084 *last = cp; 1085 c = *cp; 1086 *cp = 0; 1087 nq=nv_open(stakptr(offset),0,NV_VARNAME|NV_ARRAY|NV_NOASSIGN|NV_NOADD|NV_NOFAIL); 1088 *cp = c; 1089 if(!nq) 1090 { 1091 np = 0; 1092 goto done; 1093 } 1094 if(!var) 1095 { 1096 np = nq; 1097 goto done; 1098 } 1099 *var = nq; 1100 if(c=='[') 1101 nv_endsubscript(nq, cp,NV_NOADD); 1102 return((Namval_t*)nv_setdisc(nq,dname,nq,(Namfun_t*)nq)); 1103 done: 1104 stakseek(offset); 1105 return(np); 1106 } 1107 1108 /* 1109 * add or replace built-in version of command corresponding to <path> 1110 * The <bltin> argument is a pointer to the built-in 1111 * if <extra>==1, the built-in will be deleted 1112 * Special builtins cannot be added or deleted return failure 1113 * The return value for adding builtins is a pointer to the node or NULL on 1114 * failure. For delete NULL means success and the node that cannot be 1115 * deleted is returned on failure. 1116 */ 1117 Namval_t *sh_addbuiltin(const char *path, int (*bltin)(int, char*[],void*),void *extra) 1118 { 1119 register const char *name = path_basename(path); 1120 char *cp; 1121 register Namval_t *np, *nq=0; 1122 int offset=staktell(); 1123 if(name==path && (nq=nv_bfsearch(name,sh.bltin_tree,(Namval_t**)0,&cp))) 1124 path = name = stakptr(offset); 1125 if(np = nv_search(path,sh.bltin_tree,0)) 1126 { 1127 /* exists without a path */ 1128 if(extra == (void*)1) 1129 { 1130 if(np->nvfun && !nv_isattr(np,NV_NOFREE)) 1131 free((void*)np->nvfun); 1132 dtdelete(sh.bltin_tree,np); 1133 return(0); 1134 } 1135 if(!bltin) 1136 return(np); 1137 } 1138 else for(np=(Namval_t*)dtfirst(sh.bltin_tree);np;np=(Namval_t*)dtnext(sh.bltin_tree,np)) 1139 { 1140 if(strcmp(name,path_basename(nv_name(np)))) 1141 continue; 1142 /* exists probably with different path so delete it */ 1143 if(strcmp(path,nv_name(np))) 1144 { 1145 if(nv_isattr(np,BLT_SPC)) 1146 return(np); 1147 if(!bltin) 1148 bltin = np->nvalue.bfp; 1149 if(np->nvenv) 1150 dtdelete(sh.bltin_tree,np); 1151 if(extra == (void*)1) 1152 return(0); 1153 np = 0; 1154 } 1155 break; 1156 } 1157 if(!np && !(np = nv_search(path,sh.bltin_tree,bltin?NV_ADD:0))) 1158 return(0); 1159 if(nv_isattr(np,BLT_SPC)) 1160 { 1161 if(extra) 1162 np->nvfun = (Namfun_t*)extra; 1163 return(np); 1164 } 1165 np->nvenv = 0; 1166 np->nvfun = 0; 1167 if(bltin) 1168 { 1169 np->nvalue.bfp = bltin; 1170 nv_onattr(np,NV_BLTIN|NV_NOFREE); 1171 np->nvfun = (Namfun_t*)extra; 1172 } 1173 if(nq) 1174 { 1175 cp=nv_setdisc(nq,cp+1,np,(Namfun_t*)nq); 1176 nv_close(nq); 1177 if(!cp) 1178 errormsg(SH_DICT,ERROR_exit(1),e_baddisc,name); 1179 } 1180 if(extra == (void*)1) 1181 return(0); 1182 return(np); 1183 } 1184 1185 #undef nv_stack 1186 extern Namfun_t *nv_stack(register Namval_t *np, register Namfun_t* fp) 1187 { 1188 return(nv_disc(np,fp,0)); 1189 } 1190 1191 struct table 1192 { 1193 Namfun_t fun; 1194 Namval_t *parent; 1195 Shell_t *shp; 1196 Dt_t *dict; 1197 }; 1198 1199 static Namval_t *next_table(register Namval_t* np, Dt_t *root,Namfun_t *fp) 1200 { 1201 struct table *tp = (struct table *)fp; 1202 if(root) 1203 return((Namval_t*)dtnext(root,np)); 1204 else 1205 return((Namval_t*)dtfirst(tp->dict)); 1206 } 1207 1208 static Namval_t *create_table(Namval_t *np,const char *name,int flags,Namfun_t *fp) 1209 { 1210 struct table *tp = (struct table *)fp; 1211 tp->shp->last_table = np; 1212 return(nv_create(name, tp->dict, flags, fp)); 1213 } 1214 1215 static Namfun_t *clone_table(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp) 1216 { 1217 struct table *tp = (struct table*)fp; 1218 struct table *ntp = (struct table*)nv_clone_disc(fp,0); 1219 Dt_t *oroot=tp->dict,*nroot=dtopen(&_Nvdisc,Dtoset); 1220 if(!nroot) 1221 return(0); 1222 memcpy((void*)ntp,(void*)fp,sizeof(struct table)); 1223 ntp->dict = nroot; 1224 ntp->parent = nv_lastdict(); 1225 for(np=(Namval_t*)dtfirst(oroot);np;np=(Namval_t*)dtnext(oroot,np)) 1226 { 1227 mp = (Namval_t*)dtinsert(nroot,newnode(np->nvname)); 1228 nv_clone(np,mp,flags); 1229 } 1230 return(&ntp->fun); 1231 } 1232 1233 static void put_table(register Namval_t* np, const char* val, int flags, Namfun_t* fp) 1234 { 1235 register Dt_t *root = ((struct table*)fp)->dict; 1236 register Namval_t *nq, *mp; 1237 Namarr_t *ap; 1238 nv_putv(np,val,flags,fp); 1239 if(val) 1240 return; 1241 if(nv_isarray(np) && (ap=nv_arrayptr(np)) && array_elem(ap)) 1242 return; 1243 for(mp=(Namval_t*)dtfirst(root);mp;mp=nq) 1244 { 1245 _nv_unset(mp,flags); 1246 nq = (Namval_t*)dtnext(root,mp); 1247 dtdelete(root,mp); 1248 free((void*)mp); 1249 } 1250 dtclose(root); 1251 if(!(fp->nofree&1)) 1252 free((void*)fp); 1253 } 1254 1255 /* 1256 * return space separated list of names of variables in given tree 1257 */ 1258 static char *get_table(Namval_t *np, Namfun_t *fp) 1259 { 1260 register Dt_t *root = ((struct table*)fp)->dict; 1261 static Sfio_t *out; 1262 register int first=1; 1263 register Dt_t *base = dtview(root,0); 1264 if(out) 1265 sfseek(out,(Sfoff_t)0,SEEK_SET); 1266 else 1267 out = sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING); 1268 for(np=(Namval_t*)dtfirst(root);np;np=(Namval_t*)dtnext(root,np)) 1269 { 1270 if(!nv_isnull(np) || np->nvfun || nv_isattr(np,~NV_NOFREE)) 1271 { 1272 if(!first) 1273 sfputc(out,' '); 1274 else 1275 first = 0; 1276 sfputr(out,np->nvname,-1); 1277 } 1278 } 1279 sfputc(out,0); 1280 if(base) 1281 dtview(root,base); 1282 return((char*)out->_data); 1283 } 1284 1285 static const Namdisc_t table_disc = 1286 { 1287 sizeof(struct table), 1288 put_table, 1289 get_table, 1290 0, 1291 0, 1292 create_table, 1293 clone_table, 1294 0, 1295 next_table, 1296 }; 1297 1298 Namval_t *nv_parent(Namval_t *np) 1299 { 1300 struct table *tp = (struct table *)nv_hasdisc(np,&table_disc); 1301 if(tp) 1302 return(tp->parent); 1303 return(0); 1304 } 1305 1306 Dt_t *nv_dict(Namval_t* np) 1307 { 1308 struct table *tp = (struct table*)nv_hasdisc(np,&table_disc); 1309 if(tp) 1310 return(tp->dict); 1311 np = sh.last_table; 1312 while(np) 1313 { 1314 if(tp = (struct table*)nv_hasdisc(np,&table_disc)) 1315 return(tp->dict); 1316 #if 0 1317 np = nv_create(np,(const char*)0, NV_FIRST, (Namfun_t*)0); 1318 #else 1319 break; 1320 #endif 1321 } 1322 return(sh.var_tree); 1323 } 1324 1325 /* 1326 * create a mountable name-value pair tree 1327 */ 1328 Namval_t *nv_mount(Namval_t *np, const char *name, Dt_t *dict) 1329 { 1330 Namval_t *mp, *pp=0; 1331 struct table *tp = newof((struct table*)0, struct table,1,0); 1332 if(name) 1333 { 1334 if(nv_istable(np)) 1335 pp = np; 1336 else 1337 pp = nv_lastdict(); 1338 } 1339 if(!(tp = newof((struct table*)0, struct table,1,0))) 1340 return(0); 1341 if(name) 1342 { 1343 Namfun_t *fp = pp->nvfun; 1344 mp = (*fp->disc->createf)(pp,name,0,fp); 1345 } 1346 else 1347 mp = np; 1348 if(!nv_isnull(mp)) 1349 nv_unset(mp); 1350 tp->shp = sh_getinterp(); 1351 tp->dict = dict; 1352 tp->parent = pp; 1353 tp->fun.disc = &table_disc; 1354 nv_onattr(mp,NV_TABLE); 1355 nv_disc(mp, &tp->fun, NV_FIRST); 1356 return(mp); 1357 } 1358 1359 const Namdisc_t *nv_discfun(int which) 1360 { 1361 switch(which) 1362 { 1363 case NV_DCADD: 1364 return(&Nv_bdisc); 1365 case NV_DCRESTRICT: 1366 return(&RESTRICTED_disc); 1367 } 1368 return(0); 1369 } 1370 1371