1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1982-2012 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Eclipse Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.eclipse.org/org/documents/epl-v10.html * 11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) * 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 * Shell arithmetic - uses streval library 23 * David Korn 24 * AT&T Labs 25 */ 26 27 #include "defs.h" 28 #include "lexstates.h" 29 #include "name.h" 30 #include "streval.h" 31 #include "variables.h" 32 #include "builtins.h" 33 34 #ifndef LLONG_MAX 35 #define LLONG_MAX LONG_MAX 36 #endif 37 38 typedef Sfdouble_t (*Math_f)(Sfdouble_t, ...); 39 40 extern const Namdisc_t ENUM_disc; 41 static Sfdouble_t NaN, Inf, Fun; 42 static Namval_t Infnod = 43 { 44 { 0 }, 45 "Inf", 46 }; 47 48 static Namval_t NaNnod = 49 { 50 { 0 }, 51 "NaN", 52 }; 53 54 static Namval_t FunNode = 55 { 56 { 0 }, 57 "?", 58 }; 59 60 static Namval_t *scope(register Namval_t *np,register struct lval *lvalue,int assign) 61 { 62 register int flag = lvalue->flag; 63 register char *sub=0, *cp=(char*)np; 64 register Namval_t *mp; 65 Shell_t *shp = lvalue->shp; 66 int flags = HASH_NOSCOPE|HASH_SCOPE|HASH_BUCKET; 67 int c=0,nosub = lvalue->nosub; 68 Dt_t *sdict = (shp->st.real_fun? shp->st.real_fun->sdict:0); 69 Dt_t *nsdict = (shp->namespace?nv_dict(shp->namespace):0); 70 Dt_t *root = shp->var_tree; 71 assign = assign?NV_ASSIGN:NV_NOASSIGN; 72 lvalue->nosub = 0; 73 if(nosub<0 && lvalue->ovalue) 74 return((Namval_t*)lvalue->ovalue); 75 lvalue->ovalue = 0; 76 if(cp>=lvalue->expr && cp < lvalue->expr+lvalue->elen) 77 { 78 int offset; 79 /* do binding to node now */ 80 int c = cp[flag]; 81 cp[flag] = 0; 82 if((!(np = nv_open(cp,shp->var_tree,assign|NV_VARNAME|NV_NOADD|NV_NOFAIL)) || nv_isnull(np)) && sh_macfun(shp,cp, offset = staktell())) 83 { 84 Fun = sh_arith(shp,sub=stakptr(offset)); 85 FunNode.nvalue.ldp = &Fun; 86 nv_onattr(&FunNode,NV_NOFREE|NV_LDOUBLE|NV_RDONLY); 87 cp[flag] = c; 88 return(&FunNode); 89 } 90 if(!np && assign) 91 np = nv_open(cp,shp->var_tree,assign|NV_VARNAME); 92 cp[flag] = c; 93 if(!np) 94 return(0); 95 root = shp->last_root; 96 if(cp[flag+1]=='[') 97 flag++; 98 else 99 flag = 0; 100 cp = (char*)np; 101 } 102 else if(assign==NV_ASSIGN && nv_isnull(np) && !nv_isattr(np, ~(NV_MINIMAL|NV_NOFREE))) 103 flags |= NV_ADD; 104 if((lvalue->emode&ARITH_COMP) && dtvnext(root) && ((sdict && (mp=nv_search(cp,sdict,flags&~NV_ADD))) || (mp=nv_search(cp,root,flags&~(NV_ADD))) || (nsdict && (mp=nv_search(cp,nsdict,flags&~(NV_ADD|HASH_NOSCOPE)))) )) 105 np = mp; 106 while(nv_isref(np)) 107 { 108 #if SHOPT_FIXEDARRAY 109 int n,dim; 110 dim = nv_refdimen(np); 111 n = nv_refindex(np); 112 #endif /* SHOPT_FIXEDARRAY */ 113 sub = nv_refsub(np); 114 np = nv_refnode(np); 115 #if SHOPT_FIXEDARRAY 116 if(n) 117 { 118 Namarr_t *ap = nv_arrayptr(np); 119 ap->nelem = dim; 120 nv_putsub(np,(char*)0,n); 121 } 122 else 123 #endif /* SHOPT_FIXEDARRAY */ 124 if(sub) 125 nv_putsub(np,sub,assign==NV_ASSIGN?ARRAY_ADD:0); 126 } 127 if(!nosub && flag) 128 { 129 int hasdot = 0; 130 cp = (char*)&lvalue->expr[flag]; 131 if(sub) 132 { 133 goto skip; 134 } 135 sub = cp; 136 while(1) 137 { 138 Namarr_t *ap; 139 Namval_t *nq; 140 cp = nv_endsubscript(np,cp,0); 141 if(c || *cp=='.') 142 { 143 c = '.'; 144 while(*cp=='.') 145 { 146 hasdot=1; 147 cp++; 148 while(c=mbchar(cp),isaname(c)); 149 } 150 if(c=='[') 151 continue; 152 } 153 flag = *cp; 154 *cp = 0; 155 if(c || hasdot) 156 { 157 sfprintf(shp->strbuf,"%s%s%c",nv_name(np),sub,0); 158 sub = sfstruse(shp->strbuf); 159 } 160 if(strchr(sub,'$')) 161 sub = sh_mactrim(shp,sub,0); 162 *cp = flag; 163 if(c || hasdot) 164 { 165 np = nv_open(sub,shp->var_tree,NV_VARNAME|assign); 166 return(np); 167 } 168 #if SHOPT_FIXEDARRAY 169 ap = nv_arrayptr(np); 170 cp = nv_endsubscript(np,sub,NV_ADD|NV_SUBQUOTE|(ap&&ap->fixed?NV_FARRAY:0)); 171 #else 172 cp = nv_endsubscript(np,sub,NV_ADD|NV_SUBQUOTE); 173 #endif /* SHOPT_FIXEDARRAY */ 174 if(*cp!='[') 175 break; 176 skip: 177 if(nq = nv_opensub(np)) 178 np = nq; 179 else 180 { 181 ap = nv_arrayptr(np); 182 if(ap && !ap->table) 183 ap->table = dtopen(&_Nvdisc,Dtoset); 184 if(ap && ap->table && (nq=nv_search(nv_getsub(np),ap->table,NV_ADD))) 185 nq->nvenv = (char*)np; 186 if(nq && nv_isnull(nq)) 187 np = nv_arraychild(np,nq,0); 188 } 189 sub = cp; 190 } 191 } 192 else if(nosub>0) 193 nv_putsub(np,(char*)0,nosub-1); 194 return(np); 195 } 196 197 static Math_f sh_mathstdfun(const char *fname, size_t fsize, short * nargs) 198 { 199 register const struct mathtab *tp; 200 register char c = fname[0]; 201 for(tp=shtab_math; *tp->fname; tp++) 202 { 203 if(*tp->fname > c) 204 break; 205 if(tp->fname[1]==c && tp->fname[fsize+1]==0 && strncmp(&tp->fname[1],fname,fsize)==0) 206 { 207 if(nargs) 208 *nargs = *tp->fname; 209 return(tp->fnptr); 210 } 211 } 212 return(0); 213 } 214 215 int sh_mathstd(const char *name) 216 { 217 return(sh_mathstdfun(name,strlen(name),NULL)!=0); 218 } 219 220 static Sfdouble_t arith(const char **ptr, struct lval *lvalue, int type, Sfdouble_t n) 221 { 222 Shell_t *shp = lvalue->shp; 223 register Sfdouble_t r= 0; 224 char *str = (char*)*ptr; 225 register char *cp; 226 switch(type) 227 { 228 case ASSIGN: 229 { 230 register Namval_t *np = (Namval_t*)(lvalue->value); 231 np = scope(np,lvalue,1); 232 nv_putval(np, (char*)&n, NV_LDOUBLE); 233 if(lvalue->eflag) 234 lvalue->ptr = (void*)nv_hasdisc(np,&ENUM_disc); 235 lvalue->eflag = 0; 236 r=nv_getnum(np); 237 lvalue->value = (char*)np; 238 break; 239 } 240 case LOOKUP: 241 { 242 register int c = *str; 243 register char *xp=str; 244 lvalue->value = (char*)0; 245 if(c=='.') 246 str++; 247 c = mbchar(str); 248 if(isaletter(c)) 249 { 250 register Namval_t *np; 251 int dot=0; 252 while(1) 253 { 254 while(xp=str, c=mbchar(str), isaname(c)); 255 str = xp; 256 while(c=='[' && dot==NV_NOADD) 257 { 258 str = nv_endsubscript((Namval_t*)0,str,0); 259 c = *str; 260 } 261 if(c!='.') 262 break; 263 dot=NV_NOADD; 264 if((c = *++str) !='[') 265 continue; 266 str = nv_endsubscript((Namval_t*)0,cp=str,NV_SUBQUOTE)-1; 267 if(sh_checkid(cp+1,(char*)0)) 268 str -=2; 269 } 270 if(c=='(') 271 { 272 int off=stktell(shp->stk); 273 int fsize = str- (char*)(*ptr); 274 const struct mathtab *tp; 275 Namval_t *np; 276 c = **ptr; 277 lvalue->fun = 0; 278 sfprintf(shp->stk,".sh.math.%.*s%c",fsize,*ptr,0); 279 stkseek(shp->stk,off); 280 if(np=nv_search(stkptr(shp->stk,off),shp->fun_tree,0)) 281 { 282 lvalue->nargs = -np->nvalue.rp->argc; 283 lvalue->fun = (Math_f)np; 284 break; 285 } 286 if(fsize<=(sizeof(tp->fname)-2)) 287 lvalue->fun = (Math_f)sh_mathstdfun(*ptr,fsize,&lvalue->nargs); 288 if(lvalue->fun) 289 break; 290 if(lvalue->emode&ARITH_COMP) 291 lvalue->value = (char*)e_function; 292 else 293 lvalue->value = (char*)ERROR_dictionary(e_function); 294 return(r); 295 } 296 if((lvalue->emode&ARITH_COMP) && dot) 297 { 298 lvalue->value = (char*)*ptr; 299 lvalue->flag = str-lvalue->value; 300 break; 301 } 302 *str = 0; 303 if(sh_isoption(SH_NOEXEC)) 304 np = L_ARGNOD; 305 else 306 { 307 int offset = staktell(); 308 char *saveptr = stakfreeze(0); 309 Dt_t *root = (lvalue->emode&ARITH_COMP)?shp->var_base:shp->var_tree; 310 *str = c; 311 cp = str; 312 while(c=='[' || c=='.') 313 { 314 if(c=='[') 315 { 316 str = nv_endsubscript(np,str,0); 317 if((c= *str)!='[' && c!='.') 318 { 319 str = cp; 320 c = '['; 321 break; 322 } 323 } 324 else 325 { 326 dot = NV_NOADD|NV_NOFAIL; 327 str++; 328 while(xp=str, c=mbchar(str), isaname(c)); 329 str = xp; 330 } 331 } 332 *str = 0; 333 cp = (char*)*ptr; 334 if ((cp[0] == 'i' || cp[0] == 'I') && (cp[1] == 'n' || cp[1] == 'N') && (cp[2] == 'f' || cp[2] == 'F') && cp[3] == 0) 335 { 336 Inf = strtold("Inf", NiL); 337 Infnod.nvalue.ldp = &Inf; 338 np = &Infnod; 339 nv_onattr(np,NV_NOFREE|NV_LDOUBLE|NV_RDONLY); 340 } 341 else if ((cp[0] == 'n' || cp[0] == 'N') && (cp[1] == 'a' || cp[1] == 'A') && (cp[2] == 'n' || cp[2] == 'N') && cp[3] == 0) 342 { 343 NaN = strtold("NaN", NiL); 344 NaNnod.nvalue.ldp = &NaN; 345 np = &NaNnod; 346 nv_onattr(np,NV_NOFREE|NV_LDOUBLE|NV_RDONLY); 347 } 348 else if(!(np = nv_open(*ptr,root,NV_NOREF|NV_NOASSIGN|NV_VARNAME|dot))) 349 { 350 lvalue->value = (char*)*ptr; 351 lvalue->flag = str-lvalue->value; 352 } 353 if(saveptr != stakptr(0)) 354 stakset(saveptr,offset); 355 else 356 stakseek(offset); 357 } 358 *str = c; 359 if(!np && lvalue->value) 360 break; 361 lvalue->value = (char*)np; 362 /* bind subscript later */ 363 if(nv_isattr(np,NV_DOUBLE)==NV_DOUBLE) 364 lvalue->isfloat=1; 365 lvalue->flag = 0; 366 if(c=='[') 367 { 368 lvalue->flag = (str-lvalue->expr); 369 do 370 { 371 while(c=='.') 372 { 373 str++; 374 while(xp=str, c=mbchar(str), isaname(c)); 375 c = *(str = xp); 376 } 377 if(c=='[') 378 str = nv_endsubscript(np,str,0); 379 } 380 while((c= *str)=='[' || c=='.'); 381 break; 382 } 383 } 384 else 385 { 386 char lastbase=0, *val = xp, oerrno = errno; 387 lvalue->eflag = 0; 388 errno = 0; 389 if(shp->bltindata.bnode==SYSLET && !sh_isoption(SH_LETOCTAL)) 390 { 391 while(*val=='0' && isdigit(val[1])) 392 val++; 393 } 394 r = strtonll(val,&str, &lastbase,-1); 395 if(*str=='8' || *str=='9') 396 { 397 lastbase=10; 398 errno = 0; 399 r = strtonll(val,&str, &lastbase,-1); 400 } 401 if(lastbase<=1) 402 lastbase=10; 403 if(*val=='0') 404 { 405 while(*val=='0') 406 val++; 407 if(*val==0 || *val=='.' || *val=='x' || *val=='X') 408 val--; 409 } 410 if(r==LLONG_MAX && errno) 411 c='e'; 412 else 413 c = *str; 414 if(c==GETDECIMAL(0) || c=='e' || c == 'E' || lastbase == 415 16 && (c == 'p' || c == 'P')) 416 { 417 lvalue->isfloat=1; 418 r = strtold(val,&str); 419 } 420 else if(lastbase==10 && val[1]) 421 { 422 if(val[2]=='#') 423 val += 3; 424 if((str-val)>2*sizeof(Sflong_t)) 425 { 426 Sfdouble_t rr; 427 rr = strtold(val,&str); 428 if(rr!=r) 429 { 430 r = rr; 431 lvalue->isfloat=1; 432 } 433 } 434 } 435 errno = oerrno; 436 } 437 break; 438 } 439 case VALUE: 440 { 441 register Namval_t *np = (Namval_t*)(lvalue->value); 442 if(sh_isoption(SH_NOEXEC)) 443 return(0); 444 np = scope(np,lvalue,0); 445 if(!np) 446 { 447 if(sh_isoption(SH_NOUNSET)) 448 { 449 *ptr = lvalue->value; 450 goto skip; 451 } 452 return(0); 453 } 454 lvalue->ovalue = (char*)np; 455 if(lvalue->eflag) 456 lvalue->ptr = (void*)nv_hasdisc(np,&ENUM_disc); 457 else if((Namfun_t*)lvalue->ptr && !nv_hasdisc(np,&ENUM_disc) && !nv_isattr(np,NV_INTEGER)) 458 { 459 Namval_t *mp,node; 460 mp = ((Namfun_t*)lvalue->ptr)->type; 461 memset(&node,0,sizeof(node)); 462 nv_clone(mp,&node,0); 463 nv_offattr(&node,NV_RDONLY|NV_NOFREE); 464 nv_putval(&node,np->nvname,0); 465 if(nv_isattr(&node,NV_NOFREE)) 466 return(r=nv_getnum(&node)); 467 } 468 lvalue->eflag = 0; 469 if(((lvalue->emode&2) || lvalue->level>1 || sh_isoption(SH_NOUNSET)) && nv_isnull(np) && !nv_isattr(np,NV_INTEGER)) 470 { 471 *ptr = nv_name(np); 472 skip: 473 lvalue->value = (char*)ERROR_dictionary(e_notset); 474 lvalue->emode |= 010; 475 return(0); 476 } 477 r = nv_getnum(np); 478 if(nv_isattr(np,NV_INTEGER|NV_BINARY)==(NV_INTEGER|NV_BINARY)) 479 lvalue->isfloat= (r!=(Sflong_t)r); 480 else if(nv_isattr(np,NV_DOUBLE)==NV_DOUBLE) 481 lvalue->isfloat=1; 482 if((lvalue->emode&ARITH_ASSIGNOP) && nv_isarray(np)) 483 lvalue->nosub = nv_aindex(np)+1; 484 return(r); 485 } 486 487 case MESSAGE: 488 sfsync(NIL(Sfio_t*)); 489 #if 0 490 if(warn) 491 errormsg(SH_DICT,ERROR_warn(0),lvalue->value,*ptr); 492 else 493 #endif 494 if(lvalue->emode&ARITH_COMP) 495 return(-1); 496 497 errormsg(SH_DICT,ERROR_exit((lvalue->emode&3)!=0),lvalue->value,*ptr); 498 } 499 *ptr = str; 500 return(r); 501 } 502 503 /* 504 * convert number defined by string to a Sfdouble_t 505 * ptr is set to the last character processed 506 * if mode>0, an error will be fatal with value <mode> 507 */ 508 509 Sfdouble_t sh_strnum(register const char *str, char** ptr, int mode) 510 { 511 Shell_t *shp = sh_getinterp(); 512 register Sfdouble_t d; 513 char base=(shp->inarith?0:10), *last; 514 if(*str==0) 515 { 516 if(ptr) 517 *ptr = (char*)str; 518 return(0); 519 } 520 errno = 0; 521 d = strtonll(str,&last,&base,-1); 522 if(*last || errno) 523 { 524 if (sh_isstate(SH_INIT)) { 525 // Initializing means importing untrusted env vars. 526 // Since the string does not appear to be a recognized 527 // numeric literal give up. We can't safely call 528 // strval() since that allows arbitrary expressions 529 // which would create a security vulnerability. 530 d = 0.0; 531 } else { 532 if(!last || *last!='.' || last[1]!='.') 533 d = strval(shp,str,&last,arith,mode); 534 if(!ptr && *last && mode>0) 535 errormsg(SH_DICT,ERROR_exit(1),e_lexbadchar,*last,str); 536 } 537 } 538 else if (!d && *str=='-') 539 d = -0.0; 540 if(ptr) 541 *ptr = last; 542 return(d); 543 } 544 545 Sfdouble_t sh_arith(Shell_t *shp,register const char *str) 546 { 547 return(sh_strnum(str, (char**)0, 1)); 548 } 549 550 void *sh_arithcomp(Shell_t *shp,register char *str) 551 { 552 const char *ptr = str; 553 Arith_t *ep; 554 ep = arith_compile(shp,str,(char**)&ptr,arith,ARITH_COMP|1); 555 if(*ptr) 556 errormsg(SH_DICT,ERROR_exit(1),e_lexbadchar,*ptr,str); 557 return((void*)ep); 558 } 559