1N/A/***********************************************************************
1N/A* *
1N/A* This software is part of the ast package *
1N/A* Copyright (c) 1982-2011 AT&T Intellectual Property *
1N/A* and is licensed under the *
1N/A* Common Public License, Version 1.0 *
1N/A* by AT&T Intellectual Property *
1N/A* *
1N/A* A copy of the License is available at *
1N/A* http://www.opensource.org/licenses/cpl1.0.txt *
1N/A* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
1N/A* *
1N/A* Information and Software Systems Research *
1N/A* AT&T Research *
1N/A* Florham Park NJ *
1N/A* *
1N/A* David Korn <dgk@research.att.com> *
1N/A* *
1N/A***********************************************************************/
1N/A#pragma prototyped
1N/A/*
1N/A * Shell arithmetic - uses streval library
1N/A * David Korn
1N/A * AT&T Labs
1N/A */
1N/A
1N/A#include "defs.h"
1N/A#include "lexstates.h"
1N/A#include "name.h"
1N/A#include "streval.h"
1N/A#include "variables.h"
1N/A
1N/A#ifndef LLONG_MAX
1N/A#define LLONG_MAX LONG_MAX
1N/A#endif
1N/A
1N/Aextern const Namdisc_t ENUM_disc;
1N/Astatic Sfdouble_t NaN, Inf, Fun;
1N/Astatic Namval_t Infnod =
1N/A{
1N/A { 0 },
1N/A "Inf",
1N/A NV_NOFREE|NV_LDOUBLE,NV_RDONLY
1N/A};
1N/A
1N/Astatic Namval_t NaNnod =
1N/A{
1N/A { 0 },
1N/A "NaN",
1N/A NV_NOFREE|NV_LDOUBLE,NV_RDONLY
1N/A};
1N/A
1N/Astatic Namval_t FunNode =
1N/A{
1N/A { 0 },
1N/A "?",
1N/A NV_NOFREE|NV_LDOUBLE,NV_RDONLY
1N/A};
1N/A
1N/Astatic Namval_t *scope(register Namval_t *np,register struct lval *lvalue,int assign)
1N/A{
1N/A register int flag = lvalue->flag;
1N/A register char *sub=0, *cp=(char*)np;
1N/A register Namval_t *mp;
1N/A Shell_t *shp = lvalue->shp;
1N/A int flags = HASH_NOSCOPE|HASH_SCOPE|HASH_BUCKET;
1N/A int c=0,nosub = lvalue->nosub;
1N/A Dt_t *sdict = (shp->st.real_fun? shp->st.real_fun->sdict:0);
1N/A Dt_t *root = shp->var_tree;
1N/A assign = assign?NV_ASSIGN:NV_NOASSIGN;
1N/A lvalue->nosub = 0;
1N/A if(cp>=lvalue->expr && cp < lvalue->expr+lvalue->elen)
1N/A {
1N/A int offset;
1N/A /* do binding to node now */
1N/A int c = cp[flag];
1N/A cp[flag] = 0;
1N/A if((!(np = nv_open(cp,shp->var_tree,assign|NV_VARNAME|NV_NOADD|NV_NOFAIL)) || nv_isnull(np)) && sh_macfun(shp,cp, offset = staktell()))
1N/A {
1N/A Fun = sh_arith(shp,sub=stakptr(offset));
1N/A FunNode.nvalue.ldp = &Fun;
1N/A cp[flag] = c;
1N/A return(&FunNode);
1N/A }
1N/A if(!np && assign)
1N/A np = nv_open(cp,shp->var_tree,assign|NV_VARNAME);
1N/A cp[flag] = c;
1N/A if(!np)
1N/A return(0);
1N/A root = shp->last_root;
1N/A if(cp[flag+1]=='[')
1N/A flag++;
1N/A else
1N/A flag = 0;
1N/A cp = (char*)np;
1N/A }
1N/A else if(assign==NV_ASSIGN && nv_isnull(np) && !nv_isattr(np, ~(NV_MINIMAL|NV_NOFREE)))
1N/A flags |= NV_ADD;
1N/A if((lvalue->emode&ARITH_COMP) && dtvnext(root) && ((sdict && (mp=nv_search(cp,sdict,flags&~NV_ADD))) || (mp=nv_search(cp,root,flags&~(NV_ADD|HASH_NOSCOPE)))))
1N/A np = mp;
1N/A while(nv_isref(np))
1N/A {
1N/A#if SHOPT_FIXEDARRAY
1N/A int n,dim;
1N/A dim = nv_refdimen(np);
1N/A n = nv_refindex(np);
1N/A#endif /* SHOPT_FIXEDARRAY */
1N/A sub = nv_refsub(np);
1N/A np = nv_refnode(np);
1N/A#if SHOPT_FIXEDARRAY
1N/A if(n)
1N/A {
1N/A Namarr_t *ap = nv_arrayptr(np);
1N/A ap->nelem = dim;
1N/A nv_putsub(np,(char*)0,n);
1N/A }
1N/A else
1N/A#endif /* SHOPT_FIXEDARRAY */
1N/A if(sub)
1N/A nv_putsub(np,sub,assign==NV_ASSIGN?ARRAY_ADD:0);
1N/A }
1N/A if(!nosub && flag)
1N/A {
1N/A cp = (char*)&lvalue->expr[flag];
1N/A if(sub)
1N/A {
1N/A goto skip;
1N/A }
1N/A sub = cp;
1N/A while(1)
1N/A {
1N/A Namarr_t *ap;
1N/A Namval_t *nq;
1N/A cp = nv_endsubscript(np,cp,0);
1N/A if(c || *cp=='.')
1N/A {
1N/A c = '.';
1N/A while(*cp=='.')
1N/A {
1N/A cp++;
1N/A while(c=mbchar(cp),isaname(c));
1N/A }
1N/A if(c=='[')
1N/A continue;
1N/A }
1N/A flag = *cp;
1N/A *cp = 0;
1N/A if(c)
1N/A {
1N/A sfprintf(shp->strbuf,"%s%s%c",nv_name(np),sub,0);
1N/A sub = sfstruse(shp->strbuf);
1N/A }
1N/A if(strchr(sub,'$'))
1N/A sub = sh_mactrim(shp,sub,0);
1N/A *cp = flag;
1N/A if(c)
1N/A {
1N/A np = nv_open(sub,shp->var_tree,assign);
1N/A return(np);
1N/A }
1N/A#if SHOPT_FIXEDARRAY
1N/A ap = nv_arrayptr(np);
1N/A cp = nv_endsubscript(np,sub,NV_ADD|NV_SUBQUOTE|(ap&&ap->fixed?NV_FARRAY:0));
1N/A#else
1N/A cp = nv_endsubscript(np,sub,NV_ADD|NV_SUBQUOTE);
1N/A#endif /* SHOPT_FIXEDARRAY */
1N/A if(*cp!='[')
1N/A break;
1N/A skip:
1N/A if(nq = nv_opensub(np))
1N/A np = nq;
1N/A else
1N/A {
1N/A ap = nv_arrayptr(np);
1N/A if(ap && !ap->table)
1N/A ap->table = dtopen(&_Nvdisc,Dtoset);
1N/A if(ap && ap->table && (nq=nv_search(nv_getsub(np),ap->table,NV_ADD)))
1N/A nq->nvenv = (char*)np;
1N/A if(nq && nv_isnull(nq))
1N/A np = nv_arraychild(np,nq,0);
1N/A }
1N/A sub = cp;
1N/A }
1N/A }
1N/A else if(nosub>0)
1N/A nv_putsub(np,(char*)0,nosub-1);
1N/A return(np);
1N/A}
1N/A
1N/Astatic Sfdouble_t arith(const char **ptr, struct lval *lvalue, int type, Sfdouble_t n)
1N/A{
1N/A Shell_t *shp = lvalue->shp;
1N/A register Sfdouble_t r= 0;
1N/A char *str = (char*)*ptr;
1N/A register char *cp;
1N/A switch(type)
1N/A {
1N/A case ASSIGN:
1N/A {
1N/A register Namval_t *np = (Namval_t*)(lvalue->value);
1N/A np = scope(np,lvalue,1);
1N/A nv_putval(np, (char*)&n, NV_LDOUBLE);
1N/A if(lvalue->eflag)
1N/A lvalue->ptr = (void*)nv_hasdisc(np,&ENUM_disc);
1N/A lvalue->eflag = 0;
1N/A r=nv_getnum(np);
1N/A lvalue->value = (char*)np;
1N/A break;
1N/A }
1N/A case LOOKUP:
1N/A {
1N/A register int c = *str;
1N/A register char *xp=str;
1N/A lvalue->value = (char*)0;
1N/A if(c=='.')
1N/A str++;
1N/A c = mbchar(str);
1N/A if(isaletter(c))
1N/A {
1N/A register Namval_t *np;
1N/A int dot=0;
1N/A while(1)
1N/A {
1N/A while(xp=str, c=mbchar(str), isaname(c));
1N/A str = xp;
1N/A while(c=='[' && dot==NV_NOADD)
1N/A {
1N/A str = nv_endsubscript((Namval_t*)0,str,0);
1N/A c = *str;
1N/A }
1N/A if(c!='.')
1N/A break;
1N/A dot=NV_NOADD;
1N/A if((c = *++str) !='[')
1N/A continue;
1N/A str = nv_endsubscript((Namval_t*)0,cp=str,NV_SUBQUOTE)-1;
1N/A if(sh_checkid(cp+1,(char*)0))
1N/A str -=2;
1N/A }
1N/A if(c=='(')
1N/A {
1N/A int off=stktell(shp->stk);
1N/A int fsize = str- (char*)(*ptr);
1N/A const struct mathtab *tp;
1N/A Namval_t *np;
1N/A c = **ptr;
1N/A lvalue->fun = 0;
1N/A sfprintf(shp->stk,".sh.math.%.*s%c",fsize,*ptr,0);
1N/A stkseek(shp->stk,off);
1N/A if(np=nv_search(stkptr(shp->stk,off),shp->fun_tree,0))
1N/A {
1N/A lvalue->nargs = -np->nvalue.rp->argc;
1N/A lvalue->fun = (Sfdouble_t(*)(Sfdouble_t,...))np;
1N/A break;
1N/A }
1N/A if(fsize<=(sizeof(tp->fname)-2)) for(tp=shtab_math; *tp->fname; tp++)
1N/A {
1N/A if(*tp->fname > c)
1N/A break;
1N/A if(tp->fname[1]==c && tp->fname[fsize+1]==0 && strncmp(&tp->fname[1],*ptr,fsize)==0)
1N/A {
1N/A lvalue->fun = tp->fnptr;
1N/A lvalue->nargs = *tp->fname;
1N/A break;
1N/A }
1N/A }
1N/A if(lvalue->fun)
1N/A break;
1N/A if(lvalue->emode&ARITH_COMP)
1N/A lvalue->value = (char*)e_function;
1N/A else
1N/A lvalue->value = (char*)ERROR_dictionary(e_function);
1N/A return(r);
1N/A }
1N/A if((lvalue->emode&ARITH_COMP) && dot)
1N/A {
1N/A lvalue->value = (char*)*ptr;
1N/A lvalue->flag = str-lvalue->value;
1N/A break;
1N/A }
1N/A *str = 0;
1N/A if(sh_isoption(SH_NOEXEC))
1N/A np = L_ARGNOD;
1N/A else
1N/A {
1N/A int offset = staktell();
1N/A char *saveptr = stakfreeze(0);
1N/A Dt_t *root = (lvalue->emode&ARITH_COMP)?shp->var_base:shp->var_tree;
1N/A *str = c;
1N/A cp = str;
1N/A while(c=='[' || c=='.')
1N/A {
1N/A if(c=='[')
1N/A {
1N/A str = nv_endsubscript(np,str,0);
1N/A if((c= *str)!='[' && c!='.')
1N/A {
1N/A str = cp;
1N/A c = '[';
1N/A break;
1N/A }
1N/A }
1N/A else
1N/A {
1N/A dot = NV_NOADD|NV_NOFAIL;
1N/A str++;
1N/A while(xp=str, c=mbchar(str), isaname(c));
1N/A str = xp;
1N/A }
1N/A }
1N/A *str = 0;
1N/A cp = (char*)*ptr;
1N/A if ((cp[0] == 'i' || cp[0] == 'I') && (cp[1] == 'n' || cp[1] == 'N') && (cp[2] == 'f' || cp[2] == 'F') && cp[3] == 0)
1N/A {
1N/A Inf = strtold("Inf", NiL);
1N/A Infnod.nvalue.ldp = &Inf;
1N/A np = &Infnod;
1N/A }
1N/A else if ((cp[0] == 'n' || cp[0] == 'N') && (cp[1] == 'a' || cp[1] == 'A') && (cp[2] == 'n' || cp[2] == 'N') && cp[3] == 0)
1N/A {
1N/A NaN = strtold("NaN", NiL);
1N/A NaNnod.nvalue.ldp = &NaN;
1N/A np = &NaNnod;
1N/A }
1N/A else if(!(np = nv_open(*ptr,root,NV_NOREF|NV_NOASSIGN|NV_VARNAME|dot)))
1N/A {
1N/A lvalue->value = (char*)*ptr;
1N/A lvalue->flag = str-lvalue->value;
1N/A }
1N/A if(saveptr != stakptr(0))
1N/A stakset(saveptr,offset);
1N/A else
1N/A stakseek(offset);
1N/A }
1N/A *str = c;
1N/A if(!np && lvalue->value)
1N/A break;
1N/A lvalue->value = (char*)np;
1N/A /* bind subscript later */
1N/A if(nv_isattr(np,NV_DOUBLE)==NV_DOUBLE)
1N/A lvalue->isfloat=1;
1N/A lvalue->flag = 0;
1N/A if(c=='[')
1N/A {
1N/A lvalue->flag = (str-lvalue->expr);
1N/A do
1N/A {
1N/A while(c=='.')
1N/A {
1N/A str++;
1N/A while(xp=str, c=mbchar(str), isaname(c));
1N/A c = *(str = xp);
1N/A }
1N/A if(c=='[')
1N/A str = nv_endsubscript(np,str,0);
1N/A }
1N/A while((c= *str)=='[' || c=='.');
1N/A break;
1N/A }
1N/A }
1N/A else
1N/A {
1N/A char lastbase=0, *val = xp, oerrno = errno;
1N/A lvalue->eflag = 0;
1N/A errno = 0;
1N/A r = strtonll(val,&str, &lastbase,-1);
1N/A if(*str=='8' || *str=='9')
1N/A {
1N/A lastbase=10;
1N/A errno = 0;
1N/A r = strtonll(val,&str, &lastbase,-1);
1N/A }
1N/A if(lastbase<=1)
1N/A lastbase=10;
1N/A if(*val=='0')
1N/A {
1N/A while(*val=='0')
1N/A val++;
1N/A if(*val==0 || *val=='.' || *val=='x' || *val=='X')
1N/A val--;
1N/A }
1N/A if(r==LLONG_MAX && errno)
1N/A c='e';
1N/A else
1N/A c = *str;
1N/A if(c==GETDECIMAL(0) || c=='e' || c == 'E' || lastbase ==
1N/A 16 && (c == 'p' || c == 'P'))
1N/A {
1N/A lvalue->isfloat=1;
1N/A r = strtold(val,&str);
1N/A }
1N/A else if(lastbase==10 && val[1])
1N/A {
1N/A if(val[2]=='#')
1N/A val += 3;
1N/A if((str-val)>2*sizeof(Sflong_t))
1N/A {
1N/A Sfdouble_t rr;
1N/A rr = strtold(val,&str);
1N/A if(rr!=r)
1N/A {
1N/A r = rr;
1N/A lvalue->isfloat=1;
1N/A }
1N/A }
1N/A }
1N/A errno = oerrno;
1N/A }
1N/A break;
1N/A }
1N/A case VALUE:
1N/A {
1N/A register Namval_t *np = (Namval_t*)(lvalue->value);
1N/A if(sh_isoption(SH_NOEXEC))
1N/A return(0);
1N/A np = scope(np,lvalue,0);
1N/A if(!np)
1N/A {
1N/A if(sh_isoption(SH_NOUNSET))
1N/A {
1N/A *ptr = lvalue->value;
1N/A goto skip;
1N/A }
1N/A return(0);
1N/A }
1N/A if(lvalue->eflag)
1N/A lvalue->ptr = (void*)nv_hasdisc(np,&ENUM_disc);
1N/A else if((Namfun_t*)lvalue->ptr && !nv_hasdisc(np,&ENUM_disc) && !nv_isattr(np,NV_INTEGER))
1N/A {
1N/A Namval_t *mp,node;
1N/A mp = ((Namfun_t*)lvalue->ptr)->type;
1N/A memset(&node,0,sizeof(node));
1N/A nv_clone(mp,&node,0);
1N/A nv_offattr(&node,NV_RDONLY|NV_NOFREE);
1N/A nv_putval(&node,np->nvname,0);
1N/A if(nv_isattr(&node,NV_NOFREE))
1N/A return(r=nv_getnum(&node));
1N/A }
1N/A lvalue->eflag = 0;
1N/A if(((lvalue->emode&2) || lvalue->level>1 || sh_isoption(SH_NOUNSET)) && nv_isnull(np) && !nv_isattr(np,NV_INTEGER))
1N/A {
1N/A *ptr = nv_name(np);
1N/A skip:
1N/A lvalue->value = (char*)ERROR_dictionary(e_notset);
1N/A lvalue->emode |= 010;
1N/A return(0);
1N/A }
1N/A r = nv_getnum(np);
1N/A if(nv_isattr(np,NV_INTEGER|NV_BINARY)==(NV_INTEGER|NV_BINARY))
1N/A lvalue->isfloat= (r!=(Sflong_t)r);
1N/A else if(nv_isattr(np,NV_DOUBLE)==NV_DOUBLE)
1N/A lvalue->isfloat=1;
1N/A if((lvalue->emode&ARITH_ASSIGNOP) && nv_isarray(np))
1N/A lvalue->nosub = nv_aindex(np)+1;
1N/A return(r);
1N/A }
1N/A
1N/A case MESSAGE:
1N/A sfsync(NIL(Sfio_t*));
1N/A#if 0
1N/A if(warn)
1N/A errormsg(SH_DICT,ERROR_warn(0),lvalue->value,*ptr);
1N/A else
1N/A#endif
1N/A if(lvalue->emode&ARITH_COMP)
1N/A return(-1);
1N/A
1N/A errormsg(SH_DICT,ERROR_exit((lvalue->emode&3)!=0),lvalue->value,*ptr);
1N/A }
1N/A *ptr = str;
1N/A return(r);
1N/A}
1N/A
1N/A/*
1N/A * convert number defined by string to a Sfdouble_t
1N/A * ptr is set to the last character processed
1N/A * if mode>0, an error will be fatal with value <mode>
1N/A */
1N/A
1N/ASfdouble_t sh_strnum(register const char *str, char** ptr, int mode)
1N/A{
1N/A Shell_t *shp = sh_getinterp();
1N/A register Sfdouble_t d;
1N/A char base=0, *last;
1N/A if(*str==0)
1N/A {
1N/A if(ptr)
1N/A *ptr = (char*)str;
1N/A return(0);
1N/A }
1N/A errno = 0;
1N/A d = strtonll(str,&last,&base,-1);
1N/A if(*last || errno)
1N/A {
1N/A if(!last || *last!='.' || last[1]!='.')
1N/A d = strval(shp,str,&last,arith,mode);
1N/A if(!ptr && *last && mode>0)
1N/A errormsg(SH_DICT,ERROR_exit(1),e_lexbadchar,*last,str);
1N/A }
1N/A else if (!d && *str=='-')
1N/A d = -0.0;
1N/A if(ptr)
1N/A *ptr = last;
1N/A return(d);
1N/A}
1N/A
1N/ASfdouble_t sh_arith(Shell_t *shp,register const char *str)
1N/A{
1N/A return(sh_strnum(str, (char**)0, 1));
1N/A}
1N/A
1N/Avoid *sh_arithcomp(Shell_t *shp,register char *str)
1N/A{
1N/A const char *ptr = str;
1N/A Arith_t *ep;
1N/A ep = arith_compile(shp,str,(char**)&ptr,arith,ARITH_COMP|1);
1N/A if(*ptr)
1N/A errormsg(SH_DICT,ERROR_exit(1),e_lexbadchar,*ptr,str);
1N/A return((void*)ep);
1N/A}