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