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
scope(Shell_t * shp,register Namval_t * np,register struct lval * lvalue,int assign)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
arith(const char ** ptr,struct lval * lvalue,int type,Sfdouble_t n)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
sh_strnum(register const char * str,char ** ptr,int mode)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
sh_arith(register const char * str)383 Sfdouble_t sh_arith(register const char *str)
384 {
385 return(sh_strnum(str, (char**)0, 1));
386 }
387
sh_arithcomp(register char * str)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