Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
arithmetic: Fix even more octal leading zero mess (re: 5da8eb3)
We still weren't done. <sigh> Bug 1: leading-zero octal numbers with invalid digits 8 or 9 in them are reinterpreted as decimal, which is flagrantly incorrect behaviour. This MUST throw an error because it is a fatal user error in the script. This bug has always been in ksh, but became mostly hidden since the referenced commit, now triggered only in the 'let' builtin with --letoctal on, or globally with --posix on. The following example makes it easy to see how badly wrong this is: $ set --posix $ echo $((0125)) $((0126)) $((0127)) $((0128)) $((0129)) 85 86 87 128 129 Absolutely every other shell (provided leading-zero octal is activated) throws an error at 0128, as they should. Bug 2: in arithmetic expansions, the '0x' prefix for hexadecimal numbers, and ksh's n# prefix for base n (e.g., 16# for hex nums), incorrectly accept leading zeros. # on 93u+m after the referenced commit (arith error on 93u+) $ echo $((00x1A)) $((016#1A)) 26 26 # on 93u+ as well as 93u+m $ let "x=00x1A" "y=016#1A"; echo $x $y 26 26 Expected: arithmetic syntax error in both cases. Note: The --posix option (on 93u+m), or the --letoctal option for the let builtin (on 93u+ and 93u+m), disables this bug so that the expected error is thrown. This means the bug is caused by incorrect disabling of initial zeros as an octal prefix. All initial zeros followed by a digit are simply discarded unconditionally. src/cmd/ksh93/sh/arith.c: arith(): case LOOKUP: - Delete incorrect unconditional skipping of initial zeros. - Delete check for '8' and '9' digits for reparsing as a decimal number upon error from libast's strtonll() function. - Instead, always reparse as decimal if lastbase is 8 (which means strtonll() detected an octal number, correct or otherwise), AND the number string starts with 0, AND the --letoctal or --posix option (as applicable) is active. The above *should* have cleanly fixed both bug 1 and bug 2, but a mystifying problem remains. Bug 1 is left, but *only* for the digit immediately following the leading zero: $ set --posix $ echo $((09)) 9 $ echo $((019)) ksh: 019: arithmetic syntax error $ echo $((0119)) ksh: 0119: arithmetic syntax error The only way in which this could possibly make sense is if libast's strtonll() has a bug as well. So, we have to dig into that, too. In src/lib/libast/string/strtonll.c, strtonll() is implemented by defining a few macros and then including strtoi.h, which has the common code for the strto* functions. So the bug must be there. Sure enough, when searching for 'base = 8;' to find where octal is set, the problem quickly becomes apparent. The following code is executed when 'base' (which is 'lastbase' in ksh) is initially 0: 267: else if (S2I_valid(s) && *s == '0' && S2I_valid(s + 1)) 268: { 269: if ((c = *(s + 1)) == 'x' || c == 'X') // '0x': hex [...] 274: else if (c >= '0' && c <= '7') // <<<=== ah ha!!! 275: { 276: s++; 277: base = 8; 278: } 279: } That's right, the base 8 is only activated if the first digit following the leading 0 is a valid octal digit. Otherwise, 'base' stays 0 and thus defaults to 10 on lines 281-282, causing the bug. src/lib/libast/string/strtoi.h: - Delete incorrect check for initial octal digit. When 'base' is initially 0, it is now always set to 8 when there is a leading zero in the number string (unless followed by 'x' or 'X'). This fixes the rest of bug 1. And now, hopefully this is finally the end of the octal leading-zero mess.
- Loading branch information