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