xref: /titanic_50/usr/src/lib/libshell/common/sh/arith.c (revision 3e5bc1d795e8c41f3680a71e3954e72d079ee46d)
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  * Shell arithmetic - uses streval library
23  *   David Korn
24  *   AT&T Labs
25  */
26 
27 #include	"defs.h"
28 #include	<ctype.h>
29 #include	"lexstates.h"
30 #include	"name.h"
31 #include	"streval.h"
32 #include	"variables.h"
33 
34 #ifndef LLONG_MAX
35 #define LLONG_MAX	LONG_MAX
36 #endif
37 
38 static Sfdouble_t	NaN, Inf, Fun;
39 static Namval_t Infnod =
40 {
41 	{ 0 },
42 	"Inf",
43 	NV_NOFREE|NV_LDOUBLE,NV_RDONLY
44 };
45 
46 static Namval_t NaNnod =
47 {
48 	{ 0 },
49 	"NaN",
50 	NV_NOFREE|NV_LDOUBLE,NV_RDONLY
51 };
52 
53 static Namval_t FunNode =
54 {
55 	{ 0 },
56 	"?",
57 	NV_NOFREE|NV_LDOUBLE,NV_RDONLY
58 };
59 
60 static Namval_t *scope(Shell_t *shp,register Namval_t *np,register struct lval *lvalue,int assign)
61 {
62 	register Namarr_t *ap;
63 	register int flag = lvalue->flag;
64 	register char *sub=0, *cp=(char*)np;
65 	register Namval_t *mp;
66 	int	flags = HASH_NOSCOPE|HASH_SCOPE|HASH_BUCKET;
67 	Dt_t	*sdict = (shp->st.real_fun? shp->st.real_fun->sdict:0);
68 	assign = assign?NV_ASSIGN:NV_NOASSIGN;
69 	if(cp>=lvalue->expr &&  cp < lvalue->expr+lvalue->elen)
70 	{
71 		int offset;
72 		/* do binding to node now */
73 		int c = cp[flag];
74 		cp[flag] = 0;
75 		if((!(np = nv_open(cp,shp->var_tree,assign|NV_VARNAME|NV_NOADD|NV_NOFAIL)) || nv_isnull(np)) && sh_macfun(shp,cp, offset = staktell()))
76 		{
77 			Fun = sh_arith(sub=stakptr(offset));
78 			FunNode.nvalue.ldp = &Fun;
79 			cp[flag] = c;
80 			return(&FunNode);
81 		}
82 		np = nv_open(cp,shp->var_tree,assign|NV_VARNAME);
83 		cp[flag] = c;
84 		if(cp[flag+1]=='[')
85 			flag++;
86 		else
87 			flag = 0;
88 		cp = (char*)np;
89 	}
90 	if((lvalue->emode&ARITH_COMP) && dtvnext(shp->var_tree) && ((mp=nv_search(cp,shp->var_tree,flags))||(sdict && (mp=nv_search(cp,sdict,flags)))))
91 	{
92 		while(nv_isref(mp))
93 		{
94 			sub = nv_refsub(mp);
95 			mp = nv_refnode(mp);
96 		}
97 		np = mp;
98 	}
99 	if(flag || sub)
100 	{
101 		if(!sub)
102 			sub = (char*)&lvalue->expr[flag];
103 		if(((ap=nv_arrayptr(np)) && array_assoc(ap)) || (lvalue->emode&ARITH_COMP))
104 			nv_endsubscript(np,sub,NV_ADD|NV_SUBQUOTE);
105 		else
106 			nv_putsub(np, NIL(char*),flag);
107 	}
108 	return(np);
109 }
110 
111 static Sfdouble_t arith(const char **ptr, struct lval *lvalue, int type, Sfdouble_t n)
112 {
113 	Shell_t		*shp = &sh;
114 	register Sfdouble_t r= 0;
115 	char *str = (char*)*ptr;
116 	register char *cp;
117 	switch(type)
118 	{
119 	    case ASSIGN:
120 	    {
121 		register Namval_t *np = (Namval_t*)(lvalue->value);
122 		np = scope(shp,np,lvalue,1);
123 		nv_putval(np, (char*)&n, NV_LDOUBLE);
124 		r=nv_getnum(np);
125 		break;
126 	    }
127 	    case LOOKUP:
128 	    {
129 		register int c = *str;
130 		register char *xp=str;
131 		lvalue->value = (char*)0;
132 		if(c=='.')
133 			str++;
134 		c = mbchar(str);
135 		if(isaletter(c))
136 		{
137 			register Namval_t *np;
138 			int dot=0;
139 			while(1)
140 			{
141 				while(xp=str, c=mbchar(str), isaname(c));
142 				str = xp;
143 				if(c=='[' && dot==NV_NOADD)
144 				{
145 					str = nv_endsubscript((Namval_t*)0,str,0);
146 					c = *str;
147 				}
148 				if(c!='.')
149 					break;
150 				dot=NV_NOADD;
151 				if((c = *++str) !='[')
152 					continue;
153 				str = nv_endsubscript((Namval_t*)0,cp=str,NV_SUBQUOTE)-1;
154 				if(sh_checkid(cp+1,(char*)0))
155 					str -=2;
156 			}
157 			if(c=='(')
158 			{
159 				int fsize = str- (char*)(*ptr);
160 				const struct mathtab *tp;
161 				c = **ptr;
162 				lvalue->fun = 0;
163 				if(fsize<=(sizeof(tp->fname)-2)) for(tp=shtab_math; *tp->fname; tp++)
164 				{
165 					if(*tp->fname > c)
166 						break;
167 					if(tp->fname[1]==c && tp->fname[fsize+1]==0 && strncmp(&tp->fname[1],*ptr,fsize)==0)
168 					{
169 						lvalue->fun = tp->fnptr;
170 						lvalue->nargs = *tp->fname;
171 						break;
172 					}
173 				}
174 				if(lvalue->fun)
175 					break;
176 				lvalue->value = (char*)ERROR_dictionary(e_function);
177 				return(r);
178 			}
179 			if((lvalue->emode&ARITH_COMP) && dot)
180 			{
181 				lvalue->value = (char*)*ptr;
182 				lvalue->flag =  str-lvalue->value;
183 				break;
184 			}
185 			*str = 0;
186 			if(sh_isoption(SH_NOEXEC))
187 				np = L_ARGNOD;
188 			else
189 			{
190 				int offset = staktell();
191 				char *saveptr = stakfreeze(0);
192 				Dt_t  *root = (lvalue->emode&ARITH_COMP)?shp->var_base:shp->var_tree;
193 				*str = c;
194 				while(c=='[' || c=='.')
195 				{
196 					if(c=='[')
197 					{
198 						str = nv_endsubscript(np,cp=str,0);
199 						if((c= *str)!='[' &&  c!='.')
200 						{
201 							str = cp;
202 							c = '[';
203 							break;
204 						}
205 					}
206 					else
207 					{
208 						dot = NV_NOADD|NV_NOFAIL;
209 						str++;
210 						while(xp=str, c=mbchar(str), isaname(c));
211 						str = xp;
212 					}
213 				}
214 				*str = 0;
215 				cp = (char*)*ptr;
216 				if ((cp[0] == 'i' || cp[0] == 'I') && (cp[1] == 'n' || cp[1] == 'N') && (cp[2] == 'f' || cp[2] == 'F') && cp[3] == 0)
217 				{
218 					Inf = strtold("Inf", NiL);
219 					Infnod.nvalue.ldp = &Inf;
220 					np = &Infnod;
221 				}
222 				else if ((cp[0] == 'n' || cp[0] == 'N') && (cp[1] == 'a' || cp[1] == 'A') && (cp[2] == 'n' || cp[2] == 'N') && cp[3] == 0)
223 				{
224 					NaN = strtold("NaN", NiL);
225 					NaNnod.nvalue.ldp = &NaN;
226 					np = &NaNnod;
227 				}
228 				else if(!(np = nv_open(*ptr,root,NV_NOASSIGN|NV_VARNAME|dot)))
229 				{
230 					lvalue->value = (char*)*ptr;
231 					lvalue->flag =  str-lvalue->value;
232 				}
233 				if(saveptr != stakptr(0))
234 					stakset(saveptr,offset);
235 				else
236 					stakseek(offset);
237 			}
238 			*str = c;
239 			if(!np && lvalue->value)
240 				break;
241 			lvalue->value = (char*)np;
242 			if((lvalue->emode&ARITH_COMP) || (nv_isarray(np) && nv_aindex(np)<0))
243 			{
244 				/* bind subscript later */
245 				lvalue->flag = 0;
246 				if(c=='[')
247 				{
248 					lvalue->flag = (str-lvalue->expr);
249 					do
250 						str = nv_endsubscript(np,str,0);
251 					while((c= *str)=='[');
252 				}
253 				break;
254 			}
255 			if(c=='[')
256 			{
257 				do
258 					str = nv_endsubscript(np,str,NV_ADD|NV_SUBQUOTE);
259 				while((c=*str)=='[');
260 			}
261 			if(nv_isattr(np,NV_DOUBLE)==NV_DOUBLE)
262 				lvalue->isfloat=1;
263 			lvalue->flag = nv_aindex(np);
264 		}
265 		else
266 		{
267 			char	lastbase=0, *val = xp, oerrno = errno;
268 			errno = 0;
269 			r = strtonll(val,&str, &lastbase,-1);
270 			if(*str=='8' || *str=='9')
271 			{
272 				lastbase=10;
273 				errno = 0;
274 				r = strtonll(val,&str, &lastbase,-1);
275 			}
276 			if(lastbase<=1)
277 				lastbase=10;
278 			if(*val=='0')
279 			{
280 				while(*val=='0')
281 					val++;
282 				if(*val==0 || *val=='.' || *val=='x' || *val=='X')
283 					val--;
284 			}
285 			if(r==LLONG_MAX && errno)
286 				c='e';
287 			else
288 				c = *str;
289 			if(c==GETDECIMAL(0) || c=='e' || c == 'E' || lastbase ==
290  16 && (c == 'p' || c == 'P'))
291 			{
292 				lvalue->isfloat=1;
293 				r = strtold(val,&str);
294 			}
295 			else if(lastbase==10 && val[1])
296 			{
297 				if(val[2]=='#')
298 					val += 3;
299 				if((str-val)>2*sizeof(Sflong_t))
300 				{
301 					Sfdouble_t rr;
302 					rr = strtold(val,&str);
303 					if(rr!=r)
304 					{
305 						r = rr;
306 						lvalue->isfloat=1;
307 					}
308 				}
309 			}
310 			errno = oerrno;
311 		}
312 		break;
313 	    }
314 	    case VALUE:
315 	    {
316 		register Namval_t *np = (Namval_t*)(lvalue->value);
317 		if(sh_isoption(SH_NOEXEC))
318 			return(0);
319 		np = scope(shp,np,lvalue,0);
320 		if(((lvalue->emode&2) || lvalue->level>1 || sh_isoption(SH_NOUNSET)) && nv_isnull(np) && !nv_isattr(np,NV_INTEGER))
321 		{
322 			*ptr = nv_name(np);
323 			lvalue->value = (char*)ERROR_dictionary(e_notset);
324 			lvalue->emode |= 010;
325 			return(0);
326 		}
327 		r = nv_getnum(np);
328 		if(nv_isattr(np,NV_INTEGER|NV_BINARY)==(NV_INTEGER|NV_BINARY))
329 			lvalue->isfloat= (r!=(Sflong_t)r);
330 		else if(nv_isattr(np,NV_DOUBLE)==NV_DOUBLE)
331 			lvalue->isfloat=1;
332 		return(r);
333 	    }
334 
335 	    case MESSAGE:
336 		sfsync(NIL(Sfio_t*));
337 #if 0
338 		if(warn)
339 			errormsg(SH_DICT,ERROR_warn(0),lvalue->value,*ptr);
340 		else
341 #endif
342 			errormsg(SH_DICT,ERROR_exit((lvalue->emode&3)!=0),lvalue->value,*ptr);
343 	}
344 	*ptr = str;
345 	return(r);
346 }
347 
348 /*
349  * convert number defined by string to a Sfdouble_t
350  * ptr is set to the last character processed
351  * if mode>0, an error will be fatal with value <mode>
352  */
353 
354 Sfdouble_t sh_strnum(register const char *str, char** ptr, int mode)
355 {
356 	register Sfdouble_t d;
357 	char base=0, *last;
358 	if(*str==0)
359 	{
360 		if(ptr)
361 			*ptr = (char*)str;
362 		return(0);
363 	}
364 	errno = 0;
365 	d = strtonll(str,&last,&base,-1);
366 	if(*last || errno)
367 	{
368 		if(!last || *last!='.' || last[1]!='.')
369 			d = strval(str,&last,arith,mode);
370 		if(!ptr && *last && mode>0)
371 			errormsg(SH_DICT,ERROR_exit(1),e_lexbadchar,*last,str);
372 	}
373 	else if (!d && *str=='-')
374 		d = -0.0;
375 	if(ptr)
376 		*ptr = last;
377 	return(d);
378 }
379 
380 Sfdouble_t sh_arith(register const char *str)
381 {
382 	return(sh_strnum(str, (char**)0, 1));
383 }
384 
385 void	*sh_arithcomp(register char *str)
386 {
387 	const char *ptr = str;
388 	Arith_t *ep;
389 	ep = arith_compile(str,(char**)&ptr,arith,ARITH_COMP|1);
390 	if(*ptr)
391 		errormsg(SH_DICT,ERROR_exit(1),e_lexbadchar,*ptr,str);
392 	return((void*)ep);
393 }
394