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