NetBSD/bin
christos 01f35fcceb PR/48843: Jarmo Jaakkola: dot commands mess up scope nesting tracking
Evaluation of commands goes completely haywire if a file containing
a break/continue/return command outside its "intended" scope is sourced
using a dot command inside its "intended" scope.  The main symptom is
not exiting from the sourced file when supposed to, leading to evaluation
of commands that were not supposed to be evaluated.  A secondary symptom
is that these extra commands are not evaluated correctly, as some of them
are skipped.  Some examples are listed in the How-To-Repeat section.

According to the POSIX standard, this is how it should work:
    dot:
        The shell shall execute commands from the file in the current
        environment.
    break:
        The break utility shall exit from the smallest enclosing for, while,
        or until loop, [...]
    continue:
        The continue utility shall return to the top of the smallest
        enclosing for, while, or until loop, [...]
    return:
        The return utility shall cause the shell to stop executing
        the current function or dot script.  If the shell is not currently
        executing a function or dot script, the results are unspecified.

It is clear that return should return from a sourced file, which
it does not do.  Whether break and continue should work from the sourced
file might be debatable.  Because the dot command says "in the current
environment", I'd say yes.  In any case, it should not fail in weird
ways like it does now!

The problems occur with return (a) and break/continue (b) because:
    1)  dotcmd() does not record the function nesting level prior to
        sourcing the file nor does it touch the loopnest variable,
        leading to either
    2   a) returncmd() being unable to detect that it should not set
           evalskip to SKIPFUNC but SKIPFILE, or
        b) breakcmd() setting evalskip to SKIPCONT or SKIPBREAK,
        leading to
    3)  cmdloop() not detecting that it should skip the rest of
        the file, due to only checking for SKIPFILE.
The result is that cmdloop() keeps executing lines from the file
whilst evalskip is set, which is the main symptom.  Because
evalskip is checked in multiple places in eval.c, the secondary
symptom appears.
>How-To-Repeat:
Run the following script:

    printf "break\necho break1; echo break2" >break
    printf "continue\necho continue1; echo continue2" >continue
    printf "return\necho return1; echo return2" >return

    while true; do . ./break; done

    for i in 1 2; do . ./continue; done

    func() {
        . ./return
    }
    func

No output should be produced, but instead this is the result:
    break1
    continue1
    continue1
    return1

The main symptom is evident from the unexpected output and the secondary
one from the fact that there are no lines with '2' in them.
>Fix:
Here is patch to src/bin/sh to fix the above problems.  It keeps
track of the function nesting level at the beginning of a dot command
to enable the return command to work properly.

I also changed the undefined-by-standard functionality of the return
command when it's not in a dot command or function from (indirectly)
exiting the shell to being silently ignored.  This was done because
the previous way has at least one bug: the shell exits without asking
for confirmation when there are stopped jobs.

Because I read the standard to mean that break and continue should have
an effect outside the sourced file, that's how I implemented it.  For what
it's worth, this also seems to be what bash does.  Also laziness, because
this way required no changes to loopnesting tracking.  If this is not
wanted, it might make sense to move the nesting tracking to the inputfile
stack.

The patch also does some clean-up to reduce the amount of global
variables by moving the dotcmd() and the find_dot_file() functions from
main.c to eval.c and making in_function() a proper function.
2014-05-31 14:42:18 +00:00
..
cat more complete fix for Coverity issue 976653 2013-12-08 08:32:13 +00:00
chio Use Lk. 2012-03-17 11:09:29 +00:00
chmod In all man pages that say: 2013-12-17 09:54:08 +00:00
cp In all man pages that say: 2013-12-17 09:54:08 +00:00
csh CID 1060854: Wrong sizeof argument (SIZEOF_MISMATCH) 2013-08-06 05:42:43 +00:00
date - fix timed(8) references; 2012-04-06 11:36:56 +00:00
dd PR/48057: psi: skip and msgfmt cannot be used together. Looks like a pasto; 2013-07-17 12:55:48 +00:00
df use strspct. 2012-01-07 18:45:13 +00:00
domainname Use __dead 2011-08-29 14:51:17 +00:00
echo Check ferror(stdout) and exit(1) if there's been a problem. 2008-09-18 05:42:08 +00:00
ed PR/48692: Henning Petersen: Missplaced paranthesis in bin/ed gbl.c and main.c 2014-03-31 12:55:46 +00:00
expr Improvements: wording, punctuation, macro usage. 2012-08-12 17:27:04 +00:00
hostname Remove options added in 1.18, commitid: UhxHPgtT2Pzeg4Yw due to some 2014-02-13 12:00:29 +00:00
kill Fix whitespace nits. Suggested by Bug Hunting. 2012-03-22 07:58:16 +00:00
ksh Fixed unused warnings. 2013-10-18 19:53:34 +00:00
ln Fix whitespace nits. Suggested by Bug Hunting. 2012-03-22 07:58:16 +00:00
ls PR bin/48798: fix format for ino_t. 2014-05-10 09:39:18 +00:00
mkdir Fix whitespace nits. Suggested by Bug Hunting. 2012-03-22 07:58:16 +00:00
mt Adjust for mandoc output. 2012-03-19 10:48:20 +00:00
mv Fix whitespace nits. Suggested by Bug Hunting. 2012-03-22 07:58:16 +00:00
pax Stop hissing. 2013-11-14 07:45:05 +00:00
ps Don't bother using variables whose value is never changed from the 2014-04-20 23:31:40 +00:00
pwd Use __dead 2011-08-29 14:51:17 +00:00
rcmd Sort options in SYNOPSIS, sort option descriptions, remove some extra 2011-05-31 11:31:10 +00:00
rcp allocate a separate buffer in each call to sink(). 2012-05-07 15:22:54 +00:00
rm Sort sections. 2013-04-26 19:34:34 +00:00
rmdir static + __dead 2011-08-29 14:47:47 +00:00
sh PR/48843: Jarmo Jaakkola: dot commands mess up scope nesting tracking 2014-05-31 14:42:18 +00:00
sleep Use __dead 2011-08-29 14:51:17 +00:00
stty - print the line discipline using the new ioctl 2013-09-12 19:47:23 +00:00
sync
test test(1): Compare timestamp in nsec scale in -nt/-ot. 2013-05-04 06:26:44 +00:00
Makefile
Makefile.inc Default to WARNS=5 2012-03-21 05:47:53 +00:00