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