mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
arithmetic: Fix the octal leading zero mess (#337)
In C/POSIX arithmetic, a leading 0 denotes an octal number, e.g.
010 == 8. But this is not a desirable feature as it can cause
problems with processing things like dates with a leading zero.
In ksh, you should use 8#10 instead ("10" with base 8).
It would be tolerable if ksh at least implemented it consistently.
But AT&T made an incredible mess of it. For anyone who is not
intimately familiar with ksh internals, it is inscrutable where
arithmetic evaluation special-cases a leading 0 and where it
doesn't. Here are just some of the surprises/inconsistencies:
1. The AT&T maintainers tried to honour a leading 0 inside of
((...)) and $((...)) and not for arithmetic contexts outside it,
but even that inconsistency was never quite consistent.
2. Since 2010-12-12, $((x)) and $(($x)) are different:
$ /bin/ksh -c 'x=010; echo $((x)) $(($x))'
10 8
That's a clear violation of both POSIX and the principle of
least astonishment. $((x)) and $(($x)) should be the same in
all cases.
3. 'let' with '-o letoctal' acts in this bizarre way:
$ set -o letoctal; x=010; let "y1=$x" "y2=010"; echo $y1 $y2
10 8
That's right, 'let y=$x' is different from 'let y=010' even
when $x contains the same string value '010'! This violates
established shell grammar on the most basic level.
This commit introduces consistency. By default, ksh now acts like
mksh and zsh: the octal leading zero is disabled in all arithmetic
contexts equally. In POSIX mode, it is enabled equally.
The one exception is the 'let' built-in, where this can still be
controlled independently with the letoctal option as before (but,
because letoctal is synched with posix when switching that on/off,
it's consistent by default).
We're also removing the hackery that causes variable expansions for
the 'let' builtin to be quietly altered, so that 'x=010; let y=$x'
now does the same as 'let y=010' even with letoctal on.
Various files:
- Get rid of now-redundant sh.inarith (shp->inarith) flag, as we're
no longer distinguishing between being inside or outside ((...)).
src/cmd/ksh93/sh/arith.c:
- arith(): Let disabling POSIX octal constants by skipping leading
zeros depend on either the letoctal option being off (if we're
running the "let" built-in") or the posix option being off.
- sh_strnum(): Preset a base of 10 for strtonll(3) depending on the
posix or letoctal option being off, not on the sh.inarith flag.
src/cmd/ksh93/include/argnod.h,
src/cmd/ksh93/sh/args.c,
src/cmd/ksh93/sh/macro.c:
- Remove astonishing hackery that violated shell grammar for 'let'.
src/cmd/ksh93/sh/name.c (nv_getnum()),
src/cmd/ksh93/sh/nvdisc.c (nv_getn()):
- Remove loops for skipping leading zeroes that included a broken
check for justify/zerofill attributes, thereby fixing this bug:
$ typeset -Z x=0x15; echo $((x))
-ksh: x15: parameter not set
Even if this code wasn't redundant before, it is now: sh_arith()
is called immediately after the removed code and it ignores
leading zeroes via sh_strnum() and strtonll(3).
Resolves: https://github.com/ksh93/ksh/issues/334
This commit is contained in:
parent
257eea612a
commit
c734568b02
14 changed files with 86 additions and 93 deletions
|
|
@ -664,8 +664,6 @@ char **sh_argbuild(Shell_t *shp,int *nargs, const struct comnod *comptr,int flag
|
|||
*nargs = 0;
|
||||
if(ac)
|
||||
{
|
||||
if(ac->comnamp == SYSLET)
|
||||
flag |= ARG_LET;
|
||||
argp = ac->comarg;
|
||||
while(argp)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -405,11 +405,9 @@ static Sfdouble_t arith(const char **ptr, struct lval *lvalue, int type, Sfdoubl
|
|||
const char radix = GETDECIMAL(0);
|
||||
lvalue->eflag = 0;
|
||||
errno = 0;
|
||||
if(shp->bltindata.bnode==SYSLET && !sh_isoption(SH_LETOCTAL))
|
||||
{ /*
|
||||
* Since we're running the "let" builtin, disable octal number processing by
|
||||
* skipping all initial zeros, unless the 'letoctal' option is on.
|
||||
*/
|
||||
if(!sh_isoption(shp->bltindata.bnode==SYSLET ? SH_LETOCTAL : SH_POSIX))
|
||||
{
|
||||
/* Skip leading zeros to avoid parsing as octal */
|
||||
while(*val=='0' && isdigit(val[1]))
|
||||
val++;
|
||||
}
|
||||
|
|
@ -534,7 +532,7 @@ Sfdouble_t sh_strnum(register const char *str, char** ptr, int mode)
|
|||
{
|
||||
Shell_t *shp = sh_getinterp();
|
||||
register Sfdouble_t d;
|
||||
char base=(shp->inarith?0:10), *last;
|
||||
char base = (sh_isoption(shp->bltindata.bnode==SYSLET ? SH_LETOCTAL : SH_POSIX) ? 0 : 10), *last;
|
||||
if(*str==0)
|
||||
{
|
||||
d = 0.0;
|
||||
|
|
@ -544,7 +542,7 @@ Sfdouble_t sh_strnum(register const char *str, char** ptr, int mode)
|
|||
{
|
||||
errno = 0;
|
||||
d = strtonll(str,&last,&base,-1);
|
||||
if(*last && !shp->inarith && sh_isstate(SH_INIT))
|
||||
if(*last && sh_isstate(SH_INIT))
|
||||
{
|
||||
/* This call is to handle "base#value" literals if we're importing untrusted env vars. */
|
||||
errno = 0;
|
||||
|
|
@ -580,6 +578,7 @@ Sfdouble_t sh_strnum(register const char *str, char** ptr, int mode)
|
|||
|
||||
Sfdouble_t sh_arith(Shell_t *shp,register const char *str)
|
||||
{
|
||||
NOT_USED(shp);
|
||||
return(sh_strnum(str, (char**)0, 1));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,8 +74,6 @@ typedef struct _mac_
|
|||
char patfound; /* set if pattern character found */
|
||||
char assign; /* set for assignments */
|
||||
char arith; /* set for ((...)) */
|
||||
char let; /* set when expanding let arguments */
|
||||
char zeros; /* strip leading zeros when set */
|
||||
char arrayok; /* $x[] ok for arrays */
|
||||
char subcopy; /* set when copying subscript */
|
||||
int dotdot; /* set for .. in subscript */
|
||||
|
|
@ -161,7 +159,6 @@ char *sh_mactrim(Shell_t *shp, char *str, register int mode)
|
|||
savemac = *mp;
|
||||
stkseek(stkp,0);
|
||||
mp->arith = (mode==3);
|
||||
mp->let = 0;
|
||||
shp->argaddr = 0;
|
||||
mp->pattern = (mode==1||mode==2);
|
||||
mp->patfound = 0;
|
||||
|
|
@ -219,7 +216,6 @@ int sh_macexpand(Shell_t* shp, register struct argnod *argp, struct argnod **arg
|
|||
mp->arghead = arghead;
|
||||
mp->quoted = mp->lit = mp->quote = 0;
|
||||
mp->arith = ((flag&ARG_ARITH)!=0);
|
||||
mp->let = ((flag&ARG_LET)!=0);
|
||||
mp->split = !(flag&ARG_ASSIGN);
|
||||
mp->assign = !mp->split;
|
||||
mp->pattern = mp->split && !(flag&ARG_NOGLOB) && !sh_isoption(SH_NOGLOB);
|
||||
|
|
@ -279,7 +275,7 @@ void sh_machere(Shell_t *shp,Sfio_t *infile, Sfio_t *outfile, char *string)
|
|||
stkseek(stkp,0);
|
||||
shp->argaddr = 0;
|
||||
mp->sp = outfile;
|
||||
mp->split = mp->assign = mp->pattern = mp->patfound = mp->lit = mp->arith = mp->let = 0;
|
||||
mp->split = mp->assign = mp->pattern = mp->patfound = mp->lit = mp->arith = 0;
|
||||
mp->quote = 1;
|
||||
mp->ifsp = nv_getval(sh_scoped(shp,IFSNOD));
|
||||
mp->ifs = ' ';
|
||||
|
|
@ -1101,7 +1097,6 @@ static int varsub(Mac_t *mp)
|
|||
int var=1,addsub=0,oldpat=mp->pattern,idnum=0,flag=0,d;
|
||||
Stk_t *stkp = mp->shp->stk;
|
||||
retry1:
|
||||
mp->zeros = 0;
|
||||
idbuff[0] = 0;
|
||||
idbuff[1] = 0;
|
||||
c = fcmbget(&LEN);
|
||||
|
|
@ -1466,9 +1461,6 @@ retry1:
|
|||
else
|
||||
v = nv_getval(np);
|
||||
mp->atmode = (v && mp->quoted && mode=='@');
|
||||
/* special case --- ignore leading zeros */
|
||||
if((mp->let || (mp->arith&&nv_isattr(np,(NV_LJUST|NV_RJUST|NV_ZFILL)))) && !nv_isattr(np,NV_INTEGER) && (offset==0 || isspace(c) || strchr(",.+-*/=%&|^?!<>",c)))
|
||||
mp->zeros = 1;
|
||||
}
|
||||
if(savptr==stakptr(0))
|
||||
stkseek(stkp,offset);
|
||||
|
|
@ -1621,7 +1613,6 @@ retry1:
|
|||
int split = mp->split;
|
||||
int quoted = mp->quoted;
|
||||
int arith = mp->arith;
|
||||
int zeros = mp->zeros;
|
||||
int assign = mp->assign;
|
||||
if(newops)
|
||||
{
|
||||
|
|
@ -1638,7 +1629,7 @@ retry1:
|
|||
mp->split = 0;
|
||||
mp->quoted = 0;
|
||||
mp->assign &= ~1;
|
||||
mp->arith = mp->zeros = 0;
|
||||
mp->arith = 0;
|
||||
newquote = 0;
|
||||
}
|
||||
else if(c=='?' || c=='=')
|
||||
|
|
@ -1650,7 +1641,6 @@ retry1:
|
|||
mp->split = split;
|
||||
mp->quoted = quoted;
|
||||
mp->arith = arith;
|
||||
mp->zeros = zeros;
|
||||
mp->assign = assign;
|
||||
/* add null byte */
|
||||
sfputc(stkp,0);
|
||||
|
|
@ -2111,7 +2101,6 @@ static void comsubst(Mac_t *mp,register Shnode_t* t, int type)
|
|||
t = sh_dolparen((Lex_t*)mp->shp->lex_context);
|
||||
if(t && t->tre.tretyp==TARITH)
|
||||
{
|
||||
mp->shp->inarith = 1;
|
||||
fcsave(&save);
|
||||
if(t->ar.arcomp)
|
||||
num = arith_exec(t->ar.arcomp);
|
||||
|
|
@ -2119,7 +2108,6 @@ static void comsubst(Mac_t *mp,register Shnode_t* t, int type)
|
|||
num = sh_arith(mp->shp,t->ar.arexpr->argval);
|
||||
else
|
||||
num = sh_arith(mp->shp,sh_mactrim(mp->shp,t->ar.arexpr->argval,3));
|
||||
mp->shp->inarith = 0;
|
||||
out_offset:
|
||||
stkset(stkp,savptr,savtop);
|
||||
*mp = savemac;
|
||||
|
|
@ -2330,18 +2318,6 @@ static void mac_copy(register Mac_t *mp,register const char *str, register int s
|
|||
Stk_t *stkp=mp->shp->stk;
|
||||
int oldpat = mp->pattern;
|
||||
nopat = (mp->quote||(mp->assign==1)||mp->arith);
|
||||
if(mp->zeros)
|
||||
{
|
||||
/* prevent leading 0's from becoming octal constants */
|
||||
while(size>1 && *str=='0')
|
||||
{
|
||||
if(str[1]=='x' || str[1]=='X')
|
||||
break;
|
||||
str++,size--;
|
||||
}
|
||||
mp->zeros = 0;
|
||||
cp = str;
|
||||
}
|
||||
if(mp->sp)
|
||||
sfwrite(mp->sp,str,size);
|
||||
else if(mp->pattern>=2 || (mp->pattern && nopat) || mp->assign==3)
|
||||
|
|
|
|||
|
|
@ -2954,14 +2954,7 @@ Sfdouble_t nv_getnum(register Namval_t *np)
|
|||
}
|
||||
}
|
||||
else if((str=nv_getval(np)) && *str!=0)
|
||||
{
|
||||
if(nv_isattr(np,NV_LJUST|NV_RJUST) || (*str=='0' && !(str[1]=='x'||str[1]=='X')))
|
||||
{
|
||||
while(*str=='0')
|
||||
str++;
|
||||
}
|
||||
r = sh_arith(shp,str);
|
||||
}
|
||||
return(r);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -105,14 +105,7 @@ Sfdouble_t nv_getn(Namval_t *np, register Namfun_t *nfp)
|
|||
else
|
||||
str = nv_getv(np,fp?fp:nfp);
|
||||
if(str && *str)
|
||||
{
|
||||
if(nv_isattr(np,NV_LJUST|NV_RJUST) || (*str=='0' && !(str[1]=='x'||str[1]=='X')))
|
||||
{
|
||||
while(*str=='0')
|
||||
str++;
|
||||
}
|
||||
d = sh_arith(shp,str);
|
||||
}
|
||||
}
|
||||
return(d);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -470,7 +470,6 @@ Sfio_t *sh_subshell(Shell_t *shp,Shnode_t *t, volatile int flags, int comsub)
|
|||
struct rand *rp; /* current $RANDOM discipline function data */
|
||||
unsigned int save_rand_seed; /* parent shell $RANDOM seed */
|
||||
int save_rand_last; /* last random number from $RANDOM in parent shell */
|
||||
char save_inarith; /* flag indicating POSIX arithmetic context */
|
||||
memset((char*)sp, 0, sizeof(*sp));
|
||||
sfsync(shp->outpool);
|
||||
sh_sigcheck(shp);
|
||||
|
|
@ -592,9 +591,6 @@ Sfio_t *sh_subshell(Shell_t *shp,Shnode_t *t, volatile int flags, int comsub)
|
|||
{
|
||||
if(comsub)
|
||||
{
|
||||
/* a comsub within an arithmetic expression must not itself be in an arithmetic context */
|
||||
save_inarith = shp->inarith;
|
||||
shp->inarith = 0;
|
||||
/* disable job control */
|
||||
shp->spid = 0;
|
||||
sp->jobcontrol = job.jobcontrol;
|
||||
|
|
@ -738,8 +734,6 @@ Sfio_t *sh_subshell(Shell_t *shp,Shnode_t *t, volatile int flags, int comsub)
|
|||
sh_close(sp->tmpfd);
|
||||
}
|
||||
shp->fdstatus[1] = sp->fdstatus;
|
||||
/* restore POSIX arithmetic context flag */
|
||||
shp->inarith = save_inarith;
|
||||
}
|
||||
if(!shp->subshare)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue