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