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