Re: [PATCH] fix "Illegal number" on FreeBSD & macOS for x=; echo $((x))

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 3/5/18 1:32 AM, Martijn Dekker wrote:
dash compiled on Mac OS X (macOS) and FreeBSD manifests the following bug:

$ dash -c 'x=; echo $((x))'
dash: 1: Illegal number:

This error is printed instead of the expected default "0" that should be
substituted for an empty variable in an arithmetic expression.

The bug does not occur on Linux, NetBSD, OpenBSD or Solaris.

There is no reason why dash should behave differently on FreeBSD vs. Linux, so I agree with your patch, but isn't this non-standard, isn't either behaviour allowed?

dash on Linux also accepts 'x=\ ; echo $((x))' to print 0, and your patch also lets that work on FreeBSD.

I traced the problem to the fact that strtoimax(3) on macOS and FreeBSD
returns EINVAL in errno for an empty string -- unlike those other
systems, which return 0 in errno for an empty string.

POSIX says of strtoimax(3):
http://pubs.opengroup.org/onlinepubs/9699919799/functions/strtoimax.html
| These functions may fail if:
|
| [EINVAL]
|     No conversion could be performed.

It seems reasonable to consider that "no conversion could be performed"
if the string to convert is empty. Returning EINVAL if no conversion
could be performed is optional ("may fail"). So it seems to me that both
the FreeBSD/macOS behaviour and that of the other systems is POSIX
compliant.

The following patch should eliminate the bug on FreeBSD, macOS and any
other POSIX system which may act the same, without affecting behaviour
on other systems.

- M.

diff --git a/src/mystring.c b/src/mystring.c
index 0106bd2..c7df41f 100644
--- a/src/mystring.c
+++ b/src/mystring.c
@@ -125,7 +125,7 @@ intmax_t atomax(const char *s, int base)
  	errno = 0;
  	r = strtoimax(s, &p, base);

-	if (errno != 0)
+	if (errno != 0 && !(errno == EINVAL && p == s))

This looks good, it does the job, but it can be simplified a bit:

If errno == EINVAL, then p == s is already known.

If errno != 0 && p == s, then errno == EINVAL is already known.

This allows simplifying to either of the following:

  if (errno != 0 && errno != EINVAL)
  if (errno != 0 && p != s)

But perhaps

  if (errno == ERANGE)

is all that's needed here.

Cheers,
Harald van Dijk

  		badnum(s);

  	/*
--
To unsubscribe from this list: send the line "unsubscribe dash" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [LARTC]     [Bugtraq]     [Yosemite Forum]     [Photo]

  Powered by Linux