Re: Negative LINENO possible with indirection

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

 



On 17/09/2018 23:46, Max Rees wrote:
Hello,

While running the shunit2 test suite[1][2] against dash, the following
problem presented itself: when multiple layers of indirection are used,
$LINENO yields a negative number. It appears that the root of the
problem is that lineno is decremented in multiple places in src/eval.c
with no lower bound, but I wasn't sure how to fix that. A minimal test
case follows below.

Hi,

A more minimal test case is

  :
  :
  f() { eval echo \$LINENO; }
  f

When this script is parsed, the function definition gets an associated line number 3, and the eval command invocation also gets an associated line number 3. POSIX says

LINENO
    Set by the shell to a decimal number representing the current sequential line number (numbered starting with 1) within a script or function before it executes each command. If the user unsets or resets LINENO, the variable may lose its special meaning for the life of the shell. If the shell is not currently executing a script or function, the value of LINENO is unspecified. This volume of POSIX.1-2017 specifies the effects of the variable only for systems supporting the User Portability Utilities option.

I have interpreted this as saying that when a script defines a function, inside that function, lines are numbered starting from 1 again. So when the eval command is executed, LINENO should be set to 1. This is achieved by subtracting the function definition's line number (less one).

This is also how zsh has interpreted the spec. However, most other shells agree with bash, which says:

x.  The expansion of $LINENO inside a shell function is only relative to the
    function start if the shell is interactive -- if the shell is running a
    script, $LINENO expands to the line number in the script.  This is as
    POSIX-2001 requires.

Continuing with the original interpretation:

When the eval command executes, the echo command is parsed in isolation. It gets an associated line number 1. But it is executed in the context of the function f, so f's associated line number is taken off again. The result is 1 - (3 - 1) == -1.

There are multiple possible approaches to change this behaviour, depending on the intended behaviour.

1: Function definitions could be ignored for LINENO purposes if the function definitions are in a script. Since that takes out the subtraction, it would at least get rid of the negative numbers. 2: Function definitions' commands could be ignored for LINENO purposes. This preserves the outer-most command's line number. Since that also takes out the subtraction, it would at least get rid of the negative numbers. 3: eval's commands could be executed as if they are not in the context of a function. This would mean that eval 'echo $LINENO' always prints 1. 4: eval's commands could be executed as if they are in the context of a function with a faked associated line number, so that echo $LINENO and eval 'echo $LINENO' always print the same number, but eval 'echo $LINENO
echo $LINENO' prints two different line numbers.
5: The commands eval sees after parsing could get a fixed associated line number, so that eval 'echo $LINENO
echo $LINENO' always prints '0 0' or '1 1'.
6: The commands eval sees after parsing could get eval's associated line number, so that echo $LINENO and eval 'echo $LINENO' always print the same number, and eval 'echo $LINENO echo $LINENO' prints the same line number twice. As long as the user never modifies LINENO, this can also be equivalently stated as: the commands eval sees after parsing could avoid setting LINENO.

Testing the behaviour of various shells shows, taking dash as the baseline:

bash  | 1 4
bosh  | 2 5
ksh   | 1 3
mksh  | 1 6
pdksh | 1 5
posh  | 1 5
yash  | 1 3
zsh   | 6

Personally, I am in favour of 1. It is more sensible behaviour and I only implemented it the way it is now because of what POSIX said. If a different, equally reasonable interpretation can get a more desirable outcome, I would suggest going with that interpretation. It involves a bit more than just taking out the subtractions: it needs a bit of thinking to figure out how to ensure LINENO does get set properly in functions in interactive shells.

As for the rest, unless there are strong reasons to go after a particular result, since POSIX says the results are unspecified, I would suggest going with whatever is the simplest to implement.

Cheers,
Harald van Dijk

Please CC me on replies - I'm not subscribed.

Max



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

  Powered by Linux