1
0
Fork 0
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:
Martijn Dekker 2021-11-17 03:24:18 +00:00
parent 257eea612a
commit c734568b02
14 changed files with 86 additions and 93 deletions

18
NEWS
View file

@ -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

View file

@ -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.
____________________________________________________________________________ ____________________________________________________________________________

View file

@ -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);

View file

@ -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*[]);

View file

@ -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 */ \

View file

@ -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. */

View file

@ -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

View file

@ -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)
{ {

View file

@ -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));
} }

View file

@ -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)

View file

@ -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);
} }

View file

@ -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);
} }

View file

@ -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)
{ {

View file

@ -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))