Re: Arithmetic evaluation of negative numbers with base prefix

2019-06-17 Thread Jeremy Townshend
Dear Chet

Many thanks for your impressively swift response.  It is enlightening to see
how these expressions are parsed.

For the record, whilst I can now see how they are parsed, it feels
particularly unsatisfactory that the following two expressions yield the same
result when the variable i happens to have unwittingly been decremented below
zero (by bash arithmetic evaluation by the way - not by the output of some
external command):

  echo $((3-10#${i})) 
  echo $((3+10#${i})) 

As you indicate, this is caused by 10# being parsed as zero.  That silent
assumption of zero effectively then also silently nullifies/swallows the
preceding operator.

Ilkka Virta's email helpfully pointed me to a somewhat related debate that
occurred about 11 months ago.  I agree with your comment in this debate:

  "There would be a good case for rejecting the '10#' because it's missing
  the value."

It is this silently proceeding with a plausible (but undesirable) output in
such cases which is especially concerning.

In the meantime it would seem cautionary to advise against the pitfall of
using base# prefixed to variables (contrary to
mywiki.wooledge.org/ArithmeticExpression) unless you can be confident that
they will never be decremented below zero.

At the very least it would be helpful if the manual reflected that 10#
followed by anything other than a digit ([0-9a-zA-Z@_]) is parsed as zero, and
rlarified more completely the constraints of "number" for "n" in the "base#"
paragraph.  

I cannot find anywhere else in the manual where the word "number", "numeric
value" or "integer" excludes values less than zero without explicitly stating
so.  On the other hand phrases like "[if] ...  number/numeric values less than
zero", "if ...  [not] a number greater than [or equal to] zero" are used
repeatedly.  In those cases "number" clearly doesn't exclude those less than
zero.


Jeremy Townshend




history -n seems does not work

2019-06-17 Thread Eugen Konkov
Hello Bug-bash,

detailed explanation [here](https://unix.stackexchange.com/a/525406/129967)

-- 
Best regards,
Eugen Konkov




Re: Arithmetic evaluation of negative numbers with base prefix

2019-06-17 Thread Greg Wooledge
On Mon, Jun 17, 2019 at 02:30:27PM +0100, Jeremy Townshend wrote:
> In the meantime it would seem cautionary to advise against the pitfall of
> using base# prefixed to variables (contrary to
> mywiki.wooledge.org/ArithmeticExpression) unless you can be confident that
> they will never be decremented below zero.

Fair point.  I've updated 
and .



Re: Arithmetic evaluation of negative numbers with base prefix

2019-06-17 Thread Ilkka Virta

On 17.6. 18:47, Greg Wooledge wrote:

On Mon, Jun 17, 2019 at 02:30:27PM +0100, Jeremy Townshend wrote:

In the meantime it would seem cautionary to advise against the pitfall of
using base# prefixed to variables (contrary to
mywiki.wooledge.org/ArithmeticExpression) unless you can be confident that
they will never be decremented below zero.


Fair point.  I've updated 
and .


Good!

I still wish this could be fixed to do the useful thing without any 
workarounds, given it's what ksh and zsh do, and since this is the 
second time it comes up on the list, it appears to be surprising to 
users, too.


The # prefix is already an extension of the C numeric constant 
syntax, so extending it further to include an optional sign wouldn't 
seem in inappropriate.



I took a look last night and made some sort of a patch. It seems to 
work, though I'm not sure if I've missed any corner cases. Apart from 
the digitless '10#', the behaviour matches ksh and zsh, I made it an 
error, they apparently allow it.


  $ cat test.sh
  echo $(( 10 * 10#-123 ))  # -1230
  echo $(( 10 * 10#-008 ))  #   -80
  echo $(( 10 * 10#1+23 ))  #10*1 + 23 = 33
  echo $(( 10# ))   #  error

  $ ./bash test.sh
  -1230
  -80
  33
  test.sh: line 5: 10#: no digits in number (error token is "10#")

  $ ksh test.sh
  -1230
  -80
  33
  0


--
Ilkka Virta / itvi...@iki.fi
--- expr.c.orig 2018-12-17 16:32:21.0 +0200
+++ expr.c  2019-06-18 08:30:31.110851666 +0300
@@ -1386,8 +1386,12 @@ readtok ()
 }
   else if (DIGIT(c))
 {
-  while (ISALNUM (c) || c == '#' || c == '@' || c == '_')
-   c = *cp++;
+  unsigned char prevc = c;
+  while (ISALNUM (c) || c == '#' || c == '@' || c == '_' || ((c == '+' || 
c == '-') && prevc == '#')) 
+{
+  prevc = c;
+  c = *cp++;
+}
 
   c = *--cp;
   *cp = '\0';
@@ -1531,6 +1535,8 @@ strlong (num)
   register char *s;
   register unsigned char c;
   int base, foundbase;
+  int digits = 0;
+  char sign = 0;
   intmax_t val;
 
   s = num;
@@ -1569,8 +1575,16 @@ strlong (num)
 
  base = val;
  val = 0;
+ digits = 0;
  foundbase++;
}
+  else if (c == '-' || c == '+')
+{
+  if (digits > 0 || sign != 0)
+evalerror (_("invalid number"));
+  
+  sign = c;
+}
   else if (ISALNUM(c) || (c == '_') || (c == '@'))
{
  if (DIGIT(c))
@@ -1588,11 +1602,18 @@ strlong (num)
evalerror (_("value too great for base"));
 
  val = (val * base) + c;
+ digits++;
}
   else
break;
 }
 
+  if (sign == '-')
+val *= -1;
+  
+  if (digits == 0)
+evalerror (_("no digits in number"));
+
   return (val);
 }