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
scope(register Namval_t * np,register struct lval * lvalue,int assign)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
sh_mathstdfun(const char * fname,size_t fsize,short * nargs)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
sh_mathstd(const char * name)215 int sh_mathstd(const char *name)
216 {
217 return(sh_mathstdfun(name,strlen(name),NULL)!=0);
218 }
219
arith(const char ** ptr,struct lval * lvalue,int type,Sfdouble_t n)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
sh_strnum(register const char * str,char ** ptr,int mode)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
sh_arith(Shell_t * shp,register const char * str)545 Sfdouble_t sh_arith(Shell_t *shp,register const char *str)
546 {
547 return(sh_strnum(str, (char**)0, 1));
548 }
549
sh_arithcomp(Shell_t * shp,register char * str)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