mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-02-13 03:32:24 +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
18
NEWS
18
NEWS
|
@ -3,6 +3,19 @@ For full details, see the git log at: https://github.com/ksh93/ksh
|
||||||
|
|
||||||
Any uppercase BUG_* names are modernish shell bug IDs.
|
Any uppercase BUG_* names are modernish shell bug IDs.
|
||||||
|
|
||||||
|
2021-11-16:
|
||||||
|
|
||||||
|
- By default, arithmetic expressions in ksh no longer interpret a number
|
||||||
|
with a leading zero as octal in any context. Use 8#octalnumber instead.
|
||||||
|
Before, ksh would arbitrarily recognize the leading octal zero in some
|
||||||
|
contexts but not others, e.g., both of:
|
||||||
|
$ x=010; echo "$((x)), $(($x))"
|
||||||
|
$ set -o letoctal; x=010; let y=$x z=010; echo "$y, $z"
|
||||||
|
would output '10, 8'. These now output '10, 10' and '8, 8', respectively.
|
||||||
|
Arithmetic expressions now also behave identically within and outside
|
||||||
|
((...)) and $((...)). Setting the --posix compliance option turns on the
|
||||||
|
recognition of the leading octal zero for all arithmetic contexts.
|
||||||
|
|
||||||
2021-11-15:
|
2021-11-15:
|
||||||
|
|
||||||
- In arithmetic evaluation, the --posix compliance option now disables the
|
- In arithmetic evaluation, the --posix compliance option now disables the
|
||||||
|
@ -42,11 +55,6 @@ Any uppercase BUG_* names are modernish shell bug IDs.
|
||||||
|
|
||||||
2021-09-13:
|
2021-09-13:
|
||||||
|
|
||||||
- Fixed a bug introduced in 93u+ 2012-02-07 that caused the 'printf' builtin
|
|
||||||
(and its 'print -f' equivalent) to fail to recognise integer arguments with a
|
|
||||||
leading zero as octal numbers. For example, 'printf "%d\n" 010' now once
|
|
||||||
again outputs '8' instead of '10'.
|
|
||||||
|
|
||||||
- Disable the POSIX arithmetic context while running a command substitution
|
- Disable the POSIX arithmetic context while running a command substitution
|
||||||
invoked from within an arithmetic expression. This fixes a bug that caused
|
invoked from within an arithmetic expression. This fixes a bug that caused
|
||||||
integer arguments with a leading zero to be incorrectly interpreted as octal
|
integer arguments with a leading zero to be incorrectly interpreted as octal
|
||||||
|
|
|
@ -151,10 +151,15 @@ For more details, see the NEWS file and for complete details, see the git log.
|
||||||
correctly negates another '!', e.g., [[ ! ! 1 -eq 1 ]] now returns
|
correctly negates another '!', e.g., [[ ! ! 1 -eq 1 ]] now returns
|
||||||
0/true. Note that this has always been the case for 'test'/'['.
|
0/true. Note that this has always been the case for 'test'/'['.
|
||||||
|
|
||||||
28. In the 'printf' builtin (and the 'print -f' equivalent), numeric
|
28. By default, arithmetic expressions in ksh no longer interpret a number
|
||||||
arguments with a leading zero are now once again recognized as octal
|
with a leading zero as octal in any context. Use 8#octalnumber instead.
|
||||||
numbers as in ksh93 versions before 2012-02-07, and as POSIX requires.
|
Before, ksh would arbitrarily recognize the leading octal zero in some
|
||||||
For example, 'printf "%d\n" 010' now once again outputs '8'.
|
contexts but not others. One of several examples is:
|
||||||
|
x=010; echo "$((x)), $(($x))"
|
||||||
|
would output '10, 8'. This now outputs '10, 10'. Arithmetic
|
||||||
|
expressions now also behave identically within and outside ((...))
|
||||||
|
and $((...)). Setting the --posix compliance option turns on the
|
||||||
|
recognition of the leading octal zero for all arithmetic contexts.
|
||||||
|
|
||||||
____________________________________________________________________________
|
____________________________________________________________________________
|
||||||
|
|
||||||
|
|
|
@ -884,9 +884,7 @@ static int extend(Sfio_t* sp, void* v, Sffmt_t* fe)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
shp->inarith = 1; /* POSIX compliance: recognize octal constants, e.g. printf '%d\n' 010 */
|
|
||||||
d = sh_strnum(argp,&lastchar,0);
|
d = sh_strnum(argp,&lastchar,0);
|
||||||
shp->inarith = 0;
|
|
||||||
if(d<longmin)
|
if(d<longmin)
|
||||||
{
|
{
|
||||||
errormsg(SH_DICT,ERROR_warn(0),e_overflow,argp);
|
errormsg(SH_DICT,ERROR_warn(0),e_overflow,argp);
|
||||||
|
|
|
@ -125,7 +125,6 @@ struct argnod
|
||||||
#define ARG_ARITH 0x100 /* arithmetic expansion */
|
#define ARG_ARITH 0x100 /* arithmetic expansion */
|
||||||
#define ARG_OPTIMIZE 0x200 /* try to optimize */
|
#define ARG_OPTIMIZE 0x200 /* try to optimize */
|
||||||
#define ARG_NOGLOB 0x400 /* no file name expansion */
|
#define ARG_NOGLOB 0x400 /* no file name expansion */
|
||||||
#define ARG_LET 0x800 /* processing let command arguments */
|
|
||||||
#define ARG_ARRAYOK 0x1000 /* $x[sub] ==> ${x[sub]} */
|
#define ARG_ARRAYOK 0x1000 /* $x[sub] ==> ${x[sub]} */
|
||||||
|
|
||||||
extern struct dolnod *sh_argcreate(char*[]);
|
extern struct dolnod *sh_argcreate(char*[]);
|
||||||
|
|
|
@ -204,7 +204,6 @@ struct shared
|
||||||
char used_pos; /* used positional parameter */\
|
char used_pos; /* used positional parameter */\
|
||||||
char universe; \
|
char universe; \
|
||||||
char winch; \
|
char winch; \
|
||||||
char inarith; /* set when in POSIX arith context, i.e. leading zero = octal, e.g. in ((...)) */ \
|
|
||||||
short arithrecursion; /* current arithmetic recursion level */ \
|
short arithrecursion; /* current arithmetic recursion level */ \
|
||||||
char indebug; /* set when in debug trap */ \
|
char indebug; /* set when in debug trap */ \
|
||||||
unsigned char ignsig; /* ignored signal in subshell */ \
|
unsigned char ignsig; /* ignored signal in subshell */ \
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
#define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */
|
#define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */
|
||||||
#define SH_RELEASE_SVER "1.0.0-beta.2" /* semantic version number: https://semver.org */
|
#define SH_RELEASE_SVER "1.0.0-beta.2" /* semantic version number: https://semver.org */
|
||||||
#define SH_RELEASE_DATE "2021-11-15" /* must be in this format for $((.sh.version)) */
|
#define SH_RELEASE_DATE "2021-11-16" /* must be in this format for $((.sh.version)) */
|
||||||
#define SH_RELEASE_CPYR "(c) 2020-2021 Contributors to ksh " SH_RELEASE_FORK
|
#define SH_RELEASE_CPYR "(c) 2020-2021 Contributors to ksh " SH_RELEASE_FORK
|
||||||
|
|
||||||
/* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */
|
/* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */
|
||||||
|
|
|
@ -332,7 +332,7 @@ The arithmetic expression
|
||||||
.I expr1
|
.I expr1
|
||||||
is evaluated first
|
is evaluated first
|
||||||
(see
|
(see
|
||||||
.I "Arithmetic evaluation"
|
.I "Arithmetic Evaluation"
|
||||||
below).
|
below).
|
||||||
The arithmetic expression
|
The arithmetic expression
|
||||||
.I expr2
|
.I expr2
|
||||||
|
@ -1029,7 +1029,7 @@ for an indexed array is denoted by
|
||||||
an
|
an
|
||||||
.I arithmetic expression\^
|
.I arithmetic expression\^
|
||||||
(see
|
(see
|
||||||
.I "Arithmetic evaluation"
|
.I "Arithmetic Evaluation"
|
||||||
below)
|
below)
|
||||||
between a
|
between a
|
||||||
.B [
|
.B [
|
||||||
|
@ -1693,7 +1693,7 @@ is assigned a new value.
|
||||||
.B .sh.math
|
.B .sh.math
|
||||||
Used for defining arithmetic functions
|
Used for defining arithmetic functions
|
||||||
(see
|
(see
|
||||||
.I "Arithmetic evaluation"
|
.I "Arithmetic Evaluation"
|
||||||
below)
|
below)
|
||||||
and stores the list of user defined arithmetic functions.
|
and stores the list of user defined arithmetic functions.
|
||||||
.TP
|
.TP
|
||||||
|
@ -6447,7 +6447,7 @@ is a separate
|
||||||
.I "arithmetic expression"
|
.I "arithmetic expression"
|
||||||
to be evaluated.
|
to be evaluated.
|
||||||
.B let
|
.B let
|
||||||
only recognizes octal constants starting with
|
only recognizes octal numbers starting with
|
||||||
.B 0
|
.B 0
|
||||||
when the
|
when the
|
||||||
.B set
|
.B set
|
||||||
|
@ -6456,7 +6456,7 @@ option
|
||||||
is on.
|
is on.
|
||||||
See
|
See
|
||||||
.I "Arithmetic Evaluation"
|
.I "Arithmetic Evaluation"
|
||||||
above, for a description of arithmetic expression evaluation.
|
above for a description of arithmetic expression evaluation.
|
||||||
.sp .5
|
.sp .5
|
||||||
The exit status is
|
The exit status is
|
||||||
0 if the value of the last expression
|
0 if the value of the last expression
|
||||||
|
@ -7174,7 +7174,7 @@ Same as
|
||||||
.B letoctal
|
.B letoctal
|
||||||
The
|
The
|
||||||
.B let
|
.B let
|
||||||
command allows octal constants starting with
|
command allows octal numbers starting with
|
||||||
.BR 0 .
|
.BR 0 .
|
||||||
On by default if ksh is invoked as \fBsh\fR or \fBrsh\fR.
|
On by default if ksh is invoked as \fBsh\fR or \fBrsh\fR.
|
||||||
.TP 8
|
.TP 8
|
||||||
|
@ -7250,6 +7250,10 @@ disables the special floating point constants \fBInf\fR and \fBNaN\fR in
|
||||||
arithmetic evaluation so that, e.g., \fB$((inf))\fR and \fB$((nan))\fR refer
|
arithmetic evaluation so that, e.g., \fB$((inf))\fR and \fB$((nan))\fR refer
|
||||||
to the variables by those names;
|
to the variables by those names;
|
||||||
.IP \[bu]
|
.IP \[bu]
|
||||||
|
enables the recognition of a leading zero as introducing an octal number in
|
||||||
|
all arithmetic evaluation contexts, except in the \fBlet\fR built-in while
|
||||||
|
\fBletoctal\fR is off;
|
||||||
|
.IP \[bu]
|
||||||
changes the \fBtest\fR/\fB[\fR built-in command to make its deprecated
|
changes the \fBtest\fR/\fB[\fR built-in command to make its deprecated
|
||||||
\fIexpr1\fR \fB-a\fR \fIexpr2\fR and \fIexpr1\fR \fB-o\fR \fIexpr2\fR operators
|
\fIexpr1\fR \fB-a\fR \fIexpr2\fR and \fIexpr1\fR \fB-o\fR \fIexpr2\fR operators
|
||||||
work even if \fIexpr1\fR equals "\fB!\fR" or "\fb(\fR" (which means the
|
work even if \fIexpr1\fR equals "\fB!\fR" or "\fb(\fR" (which means the
|
||||||
|
|
|
@ -664,8 +664,6 @@ char **sh_argbuild(Shell_t *shp,int *nargs, const struct comnod *comptr,int flag
|
||||||
*nargs = 0;
|
*nargs = 0;
|
||||||
if(ac)
|
if(ac)
|
||||||
{
|
{
|
||||||
if(ac->comnamp == SYSLET)
|
|
||||||
flag |= ARG_LET;
|
|
||||||
argp = ac->comarg;
|
argp = ac->comarg;
|
||||||
while(argp)
|
while(argp)
|
||||||
{
|
{
|
||||||
|
|
|
@ -405,11 +405,9 @@ static Sfdouble_t arith(const char **ptr, struct lval *lvalue, int type, Sfdoubl
|
||||||
const char radix = GETDECIMAL(0);
|
const char radix = GETDECIMAL(0);
|
||||||
lvalue->eflag = 0;
|
lvalue->eflag = 0;
|
||||||
errno = 0;
|
errno = 0;
|
||||||
if(shp->bltindata.bnode==SYSLET && !sh_isoption(SH_LETOCTAL))
|
if(!sh_isoption(shp->bltindata.bnode==SYSLET ? SH_LETOCTAL : SH_POSIX))
|
||||||
{ /*
|
{
|
||||||
* Since we're running the "let" builtin, disable octal number processing by
|
/* Skip leading zeros to avoid parsing as octal */
|
||||||
* skipping all initial zeros, unless the 'letoctal' option is on.
|
|
||||||
*/
|
|
||||||
while(*val=='0' && isdigit(val[1]))
|
while(*val=='0' && isdigit(val[1]))
|
||||||
val++;
|
val++;
|
||||||
}
|
}
|
||||||
|
@ -534,7 +532,7 @@ Sfdouble_t sh_strnum(register const char *str, char** ptr, int mode)
|
||||||
{
|
{
|
||||||
Shell_t *shp = sh_getinterp();
|
Shell_t *shp = sh_getinterp();
|
||||||
register Sfdouble_t d;
|
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)
|
if(*str==0)
|
||||||
{
|
{
|
||||||
d = 0.0;
|
d = 0.0;
|
||||||
|
@ -544,7 +542,7 @@ Sfdouble_t sh_strnum(register const char *str, char** ptr, int mode)
|
||||||
{
|
{
|
||||||
errno = 0;
|
errno = 0;
|
||||||
d = strtonll(str,&last,&base,-1);
|
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. */
|
/* This call is to handle "base#value" literals if we're importing untrusted env vars. */
|
||||||
errno = 0;
|
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)
|
Sfdouble_t sh_arith(Shell_t *shp,register const char *str)
|
||||||
{
|
{
|
||||||
|
NOT_USED(shp);
|
||||||
return(sh_strnum(str, (char**)0, 1));
|
return(sh_strnum(str, (char**)0, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,8 +74,6 @@ typedef struct _mac_
|
||||||
char patfound; /* set if pattern character found */
|
char patfound; /* set if pattern character found */
|
||||||
char assign; /* set for assignments */
|
char assign; /* set for assignments */
|
||||||
char arith; /* set for ((...)) */
|
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 arrayok; /* $x[] ok for arrays */
|
||||||
char subcopy; /* set when copying subscript */
|
char subcopy; /* set when copying subscript */
|
||||||
int dotdot; /* set for .. in subscript */
|
int dotdot; /* set for .. in subscript */
|
||||||
|
@ -161,7 +159,6 @@ char *sh_mactrim(Shell_t *shp, char *str, register int mode)
|
||||||
savemac = *mp;
|
savemac = *mp;
|
||||||
stkseek(stkp,0);
|
stkseek(stkp,0);
|
||||||
mp->arith = (mode==3);
|
mp->arith = (mode==3);
|
||||||
mp->let = 0;
|
|
||||||
shp->argaddr = 0;
|
shp->argaddr = 0;
|
||||||
mp->pattern = (mode==1||mode==2);
|
mp->pattern = (mode==1||mode==2);
|
||||||
mp->patfound = 0;
|
mp->patfound = 0;
|
||||||
|
@ -219,7 +216,6 @@ int sh_macexpand(Shell_t* shp, register struct argnod *argp, struct argnod **arg
|
||||||
mp->arghead = arghead;
|
mp->arghead = arghead;
|
||||||
mp->quoted = mp->lit = mp->quote = 0;
|
mp->quoted = mp->lit = mp->quote = 0;
|
||||||
mp->arith = ((flag&ARG_ARITH)!=0);
|
mp->arith = ((flag&ARG_ARITH)!=0);
|
||||||
mp->let = ((flag&ARG_LET)!=0);
|
|
||||||
mp->split = !(flag&ARG_ASSIGN);
|
mp->split = !(flag&ARG_ASSIGN);
|
||||||
mp->assign = !mp->split;
|
mp->assign = !mp->split;
|
||||||
mp->pattern = mp->split && !(flag&ARG_NOGLOB) && !sh_isoption(SH_NOGLOB);
|
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);
|
stkseek(stkp,0);
|
||||||
shp->argaddr = 0;
|
shp->argaddr = 0;
|
||||||
mp->sp = outfile;
|
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->quote = 1;
|
||||||
mp->ifsp = nv_getval(sh_scoped(shp,IFSNOD));
|
mp->ifsp = nv_getval(sh_scoped(shp,IFSNOD));
|
||||||
mp->ifs = ' ';
|
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;
|
int var=1,addsub=0,oldpat=mp->pattern,idnum=0,flag=0,d;
|
||||||
Stk_t *stkp = mp->shp->stk;
|
Stk_t *stkp = mp->shp->stk;
|
||||||
retry1:
|
retry1:
|
||||||
mp->zeros = 0;
|
|
||||||
idbuff[0] = 0;
|
idbuff[0] = 0;
|
||||||
idbuff[1] = 0;
|
idbuff[1] = 0;
|
||||||
c = fcmbget(&LEN);
|
c = fcmbget(&LEN);
|
||||||
|
@ -1466,9 +1461,6 @@ retry1:
|
||||||
else
|
else
|
||||||
v = nv_getval(np);
|
v = nv_getval(np);
|
||||||
mp->atmode = (v && mp->quoted && mode=='@');
|
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))
|
if(savptr==stakptr(0))
|
||||||
stkseek(stkp,offset);
|
stkseek(stkp,offset);
|
||||||
|
@ -1621,7 +1613,6 @@ retry1:
|
||||||
int split = mp->split;
|
int split = mp->split;
|
||||||
int quoted = mp->quoted;
|
int quoted = mp->quoted;
|
||||||
int arith = mp->arith;
|
int arith = mp->arith;
|
||||||
int zeros = mp->zeros;
|
|
||||||
int assign = mp->assign;
|
int assign = mp->assign;
|
||||||
if(newops)
|
if(newops)
|
||||||
{
|
{
|
||||||
|
@ -1638,7 +1629,7 @@ retry1:
|
||||||
mp->split = 0;
|
mp->split = 0;
|
||||||
mp->quoted = 0;
|
mp->quoted = 0;
|
||||||
mp->assign &= ~1;
|
mp->assign &= ~1;
|
||||||
mp->arith = mp->zeros = 0;
|
mp->arith = 0;
|
||||||
newquote = 0;
|
newquote = 0;
|
||||||
}
|
}
|
||||||
else if(c=='?' || c=='=')
|
else if(c=='?' || c=='=')
|
||||||
|
@ -1650,7 +1641,6 @@ retry1:
|
||||||
mp->split = split;
|
mp->split = split;
|
||||||
mp->quoted = quoted;
|
mp->quoted = quoted;
|
||||||
mp->arith = arith;
|
mp->arith = arith;
|
||||||
mp->zeros = zeros;
|
|
||||||
mp->assign = assign;
|
mp->assign = assign;
|
||||||
/* add null byte */
|
/* add null byte */
|
||||||
sfputc(stkp,0);
|
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);
|
t = sh_dolparen((Lex_t*)mp->shp->lex_context);
|
||||||
if(t && t->tre.tretyp==TARITH)
|
if(t && t->tre.tretyp==TARITH)
|
||||||
{
|
{
|
||||||
mp->shp->inarith = 1;
|
|
||||||
fcsave(&save);
|
fcsave(&save);
|
||||||
if(t->ar.arcomp)
|
if(t->ar.arcomp)
|
||||||
num = arith_exec(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);
|
num = sh_arith(mp->shp,t->ar.arexpr->argval);
|
||||||
else
|
else
|
||||||
num = sh_arith(mp->shp,sh_mactrim(mp->shp,t->ar.arexpr->argval,3));
|
num = sh_arith(mp->shp,sh_mactrim(mp->shp,t->ar.arexpr->argval,3));
|
||||||
mp->shp->inarith = 0;
|
|
||||||
out_offset:
|
out_offset:
|
||||||
stkset(stkp,savptr,savtop);
|
stkset(stkp,savptr,savtop);
|
||||||
*mp = savemac;
|
*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;
|
Stk_t *stkp=mp->shp->stk;
|
||||||
int oldpat = mp->pattern;
|
int oldpat = mp->pattern;
|
||||||
nopat = (mp->quote||(mp->assign==1)||mp->arith);
|
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)
|
if(mp->sp)
|
||||||
sfwrite(mp->sp,str,size);
|
sfwrite(mp->sp,str,size);
|
||||||
else if(mp->pattern>=2 || (mp->pattern && nopat) || mp->assign==3)
|
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)
|
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);
|
r = sh_arith(shp,str);
|
||||||
}
|
|
||||||
return(r);
|
return(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,14 +105,7 @@ Sfdouble_t nv_getn(Namval_t *np, register Namfun_t *nfp)
|
||||||
else
|
else
|
||||||
str = nv_getv(np,fp?fp:nfp);
|
str = nv_getv(np,fp?fp:nfp);
|
||||||
if(str && *str)
|
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);
|
d = sh_arith(shp,str);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return(d);
|
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 */
|
struct rand *rp; /* current $RANDOM discipline function data */
|
||||||
unsigned int save_rand_seed; /* parent shell $RANDOM seed */
|
unsigned int save_rand_seed; /* parent shell $RANDOM seed */
|
||||||
int save_rand_last; /* last random number from $RANDOM in parent shell */
|
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));
|
memset((char*)sp, 0, sizeof(*sp));
|
||||||
sfsync(shp->outpool);
|
sfsync(shp->outpool);
|
||||||
sh_sigcheck(shp);
|
sh_sigcheck(shp);
|
||||||
|
@ -592,9 +591,6 @@ Sfio_t *sh_subshell(Shell_t *shp,Shnode_t *t, volatile int flags, int comsub)
|
||||||
{
|
{
|
||||||
if(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 */
|
/* disable job control */
|
||||||
shp->spid = 0;
|
shp->spid = 0;
|
||||||
sp->jobcontrol = job.jobcontrol;
|
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);
|
sh_close(sp->tmpfd);
|
||||||
}
|
}
|
||||||
shp->fdstatus[1] = sp->fdstatus;
|
shp->fdstatus[1] = sp->fdstatus;
|
||||||
/* restore POSIX arithmetic context flag */
|
|
||||||
shp->inarith = save_inarith;
|
|
||||||
}
|
}
|
||||||
if(!shp->subshare)
|
if(!shp->subshare)
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,6 +21,9 @@
|
||||||
|
|
||||||
. "${SHTESTS_COMMON:-${0%/*}/_common}"
|
. "${SHTESTS_COMMON:-${0%/*}/_common}"
|
||||||
|
|
||||||
|
integer hasposix=0
|
||||||
|
(set -o posix) 2>/dev/null && ((hasposix++)) # not using [[ -o ?posix ]] as it's broken on 93v-
|
||||||
|
|
||||||
trap '' FPE # NOTE: osf.alpha requires this (no ieee math)
|
trap '' FPE # NOTE: osf.alpha requires this (no ieee math)
|
||||||
|
|
||||||
integer x=1 y=2 z=3
|
integer x=1 y=2 z=3
|
||||||
|
@ -346,8 +349,14 @@ do (( ipx = ip % 256 ))
|
||||||
done
|
done
|
||||||
unset x
|
unset x
|
||||||
x=010
|
x=010
|
||||||
(( x == 10 )) || err_exit 'leading zeros in x treated as octal arithmetic with $((x))'
|
(( x == 10 )) || err_exit 'leading zeros in x treated as octal arithmetic with ((x))'
|
||||||
(( $x == 8 )) || err_exit 'leading zeros not treated as octal arithmetic with $x'
|
(( $x == 10 )) || err_exit 'leading zeros in x treated as octal arithmetic with (($x))'
|
||||||
|
if ((hasposix))
|
||||||
|
then set --posix
|
||||||
|
((x == 8)) || err_exit 'posix: leading zeros in x not treated as octal arithmetic with ((x))'
|
||||||
|
(($x == 8)) || err_exit 'posix: leading zeros in x not treated as octal arithmetic with (($x))'
|
||||||
|
set --noposix
|
||||||
|
fi
|
||||||
unset x
|
unset x
|
||||||
typeset -Z x=010
|
typeset -Z x=010
|
||||||
(( x == 10 )) || err_exit 'leading zeros not ignored for arithmetic'
|
(( x == 10 )) || err_exit 'leading zeros not ignored for arithmetic'
|
||||||
|
@ -728,15 +737,26 @@ unset A
|
||||||
unset r x
|
unset r x
|
||||||
integer x
|
integer x
|
||||||
r=020
|
r=020
|
||||||
(($r == 16)) || err_exit 'leading 0 not treated as octal inside ((...))'
|
(($r == 20)) || err_exit 'leading 0 treated as octal inside ((...))'
|
||||||
x=$(($r))
|
x=$(($r))
|
||||||
(( x == 16 )) || err_exit 'leading 0 not treated as octal inside $((...))'
|
((x == 20)) || err_exit 'leading 0 treated as octal inside $((...))'
|
||||||
x=$r
|
x=$r
|
||||||
((x == 20 )) || err_exit 'leading 0 should not be treated as octal outside ((...))'
|
((x == 20)) || err_exit 'leading 0 treated as octal outside ((...))'
|
||||||
print -- -020 | read x
|
print -- -020 | read x
|
||||||
((x == -20)) || err_exit 'numbers with leading -0 should not be treated as octal outside ((...))'
|
((x == -20)) || err_exit 'numbers with leading -0 treated as octal outside ((...))'
|
||||||
print -- -8#20 | read x
|
print -- -8#20 | read x
|
||||||
((x == -16)) || err_exit 'numbers with leading -8# should be treated as octal'
|
((x == -16)) || err_exit 'numbers with leading -8# should be treated as octal'
|
||||||
|
if ((hasposix))
|
||||||
|
then set --posix
|
||||||
|
(($r == 16)) || err_exit 'posix: leading 0 not treated as octal inside ((...))'
|
||||||
|
x=$(($r))
|
||||||
|
(( x == 16 )) || err_exit 'posix: leading 0 not treated as octal inside $((...))'
|
||||||
|
x=$r
|
||||||
|
((x == 16)) || err_exit 'posix: leading 0 not as octal outside ((...))'
|
||||||
|
print -- -020 | read x
|
||||||
|
((x == -16)) || err_exit 'posix: numbers with leading -0 should be treated as octal outside ((...))'
|
||||||
|
set --noposix
|
||||||
|
fi
|
||||||
|
|
||||||
unset x
|
unset x
|
||||||
x=0x1
|
x=0x1
|
||||||
|
@ -750,8 +770,13 @@ let "$x==10" || err_exit 'arithmetic with $x where $x is 010 should be decimal i
|
||||||
(( 9.$x == 9.01 )) || err_exit 'arithmetic with 9.$x where x=010 should be 9.01'
|
(( 9.$x == 9.01 )) || err_exit 'arithmetic with 9.$x where x=010 should be 9.01'
|
||||||
(( 9$x == 9010 )) || err_exit 'arithmetic with 9$x where x=010 should be 9010'
|
(( 9$x == 9010 )) || err_exit 'arithmetic with 9$x where x=010 should be 9010'
|
||||||
x010=99
|
x010=99
|
||||||
((x$x == 99 )) || err_exit 'arithtmetic with x$x where x=010 should be $x010'
|
((x$x == 99 )) || err_exit 'arithmetic with x$x where x=010 should be $x010'
|
||||||
(( 3+$x == 11 )) || err_exit '3+$x where x=010 should be 11 in ((...))'
|
(( 3+$x == 13 )) || err_exit '3+$x where x=010 should be 13 in ((...))'
|
||||||
|
if ((hasposix))
|
||||||
|
then set --posix
|
||||||
|
(( 3+$x == 11 )) || err_exit 'posix: 3+$x where x=010 should be 11 in ((...))'
|
||||||
|
set --noposix
|
||||||
|
fi
|
||||||
let "(3+$x)==13" || err_exit 'let should not recognize leading 0 as octal'
|
let "(3+$x)==13" || err_exit 'let should not recognize leading 0 as octal'
|
||||||
unset x
|
unset x
|
||||||
typeset -RZ3 x=10
|
typeset -RZ3 x=10
|
||||||
|
@ -878,8 +903,9 @@ unset got
|
||||||
|
|
||||||
# ======
|
# ======
|
||||||
# https://github.com/ksh93/ksh/issues/326
|
# https://github.com/ksh93/ksh/issues/326
|
||||||
for m in u d i o x X
|
((hasposix)) && for m in u d i o x X
|
||||||
do
|
do
|
||||||
|
set --posix
|
||||||
case $m in
|
case $m in
|
||||||
o) exp="10;21;32;" ;;
|
o) exp="10;21;32;" ;;
|
||||||
x) exp="8;11;1a;" ;;
|
x) exp="8;11;1a;" ;;
|
||||||
|
@ -887,22 +913,16 @@ do
|
||||||
*) exp="8;17;26;" ;;
|
*) exp="8;17;26;" ;;
|
||||||
esac
|
esac
|
||||||
got=${ printf "%$m;" 010 021 032; }
|
got=${ printf "%$m;" 010 021 032; }
|
||||||
[[ $got == "$exp" ]] || err_exit "printf %$m does not recognize octal arguments" \
|
[[ $got == "$exp" ]] || err_exit "posix: printf %$m does not recognize octal arguments" \
|
||||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
set --noposix
|
||||||
done
|
done
|
||||||
|
|
||||||
# https://github.com/ksh93/ksh/issues/326#issuecomment-917707463
|
|
||||||
exp=18
|
|
||||||
got=$(( $(integer x; x=010; echo $x) + 010 ))
|
|
||||||
# ^^^ decimal ^^^ octal
|
|
||||||
[[ $got == "$exp" ]] || err_exit 'Integer with leading zero incorrectly interpreted as octal in non-POSIX arith context' \
|
|
||||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
|
||||||
|
|
||||||
# ======
|
# ======
|
||||||
# BUG_ARITHNAN: In ksh <= 93u+m 2021-11-15 and zsh 5.6 - 5.8, the case-insensitive
|
# BUG_ARITHNAN: In ksh <= 93u+m 2021-11-15 and zsh 5.6 - 5.8, the case-insensitive
|
||||||
# floating point constants Inf and NaN are recognised in arithmetic evaluation,
|
# floating point constants Inf and NaN are recognised in arithmetic evaluation,
|
||||||
# overriding any variables with the names Inf, NaN, INF, nan, etc.
|
# overriding any variables with the names Inf, NaN, INF, nan, etc.
|
||||||
if (set --posix) 2>/dev/null
|
if ((hasposix))
|
||||||
then set --posix
|
then set --posix
|
||||||
Inf=42 NaN=13
|
Inf=42 NaN=13
|
||||||
inf=421 nan=137
|
inf=421 nan=137
|
||||||
|
@ -920,5 +940,12 @@ then set --posix
|
||||||
set --noposix
|
set --noposix
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ======
|
||||||
|
# https://github.com/ksh93/ksh/issues/334#issuecomment-968603087
|
||||||
|
exp=21
|
||||||
|
got=$(typeset -Z x=0x15; { echo $((x)); } 2>&1)
|
||||||
|
[[ $got == "$exp" ]] || err_exit "typeset -Z corrupts hexadecimal number in arithmetic context" \
|
||||||
|
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
|
||||||
# ======
|
# ======
|
||||||
exit $((Errors<125?Errors:125))
|
exit $((Errors<125?Errors:125))
|
||||||
|
|
Loading…
Reference in a new issue