1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00

Fix BUG_LOOPRET2 and related return/exit misbehaviour

The 'exit' and 'return' commands without an argument failed to pass
down the exit status of the last-run command when incorporated in a
block with redirection, &&/|| list, 'case' statement, or 'while',
'until' or 'for' loop.

src/cmd/ksh93/bltins/cflow.c:
- Use $?, which is sh.savexit a.k.a. shp->savexit, as the default
  exit status value if there is no argument, instead of
  shp->oldexit. This fixes the default exit status behaviour to
  match POSIX and other shells.

src/cmd/ksh93/include/defs.h,
src/cmd/ksh93/include/shell.h:
- Remove now-unused sh.oldexit (a.k.a. shp->oldexit) private struct
  member. It appeared to fulfill the same function as sh.savexit,
  but in a slightly broken way.
- Move the savexit/$? declaration from the _SH_PRIVATE part of the
  struct definition to the public API part. Since $? uses this,
  it's clearly a publicly exposed value already, and this is
  generally the one to use. (If anything, it's exitval that should
  have been private.) This declares savexit right next to exitval,
  rewriting the comments to clarify the difference between them.

src/cmd/ksh93/sh/fault.c,
src/cmd/ksh93/sh/subshell.c,
src/cmd/ksh93/sh/xec.c:
- Remove assignments to shp->oldexit.

src/cmd/ksh93/tests/basic.sh:
- Add thorough regression tests for the default exit status
  behaviour of 'return' and 'exit' in various lexical contexts.
- Verify that 'for' and 'case' without any command, as well as a
  lone redirection, still correctly reset the exit status to 0.

Fixes: #117
This commit is contained in:
Martijn Dekker 2020-09-09 20:02:20 +02:00
parent 400c107773
commit 092b90da81
10 changed files with 98 additions and 15 deletions

View file

@ -428,6 +428,88 @@ if env x-a=y >/dev/null 2>&1
then [[ $(env 'x-a=y' $SHELL -c 'env | grep x-a') == *x-a=y* ]] || err_exit 'invalid environment variables not preserved'
fi
foo() { return; }
false
foo && err_exit "'return' within function does not preserve exit status"
false
foo & wait "$!" && err_exit "'return' within function does not preserve exit status (bg job)"
foo() { false; return || :; }
foo && err_exit "'return ||' does not preserve exit status"
foo() { false; return && :; }
foo && err_exit "'return &&' does not preserve exit status"
foo() { false; while return; do true; done; }
foo && err_exit "'while return' does not preserve exit status"
foo() { false; while return; do true; done 2>&1; }
foo && err_exit "'while return' with redirection does not preserve exit status"
foo() { false; until return; do true; done; }
foo && err_exit "'until return' does not preserve exit status"
foo() { false; until return; do true; done 2>&1; }
foo && err_exit "'until return' with redirection does not preserve exit status"
foo() { false; for i in 1; do return; done; }
foo && err_exit "'return' within 'for' does not preserve exit status"
foo() { false; for i in 1; do return; done 2>&1; }
foo && err_exit "'return' within 'for' with redirection does not preserve exit status"
foo() { false; { return; } 2>&1; }
foo && err_exit "'return' within { block; } with redirection does not preserve exit status"
foo() ( exit )
false
foo && err_exit "'exit' within function does not preserve exit status"
false
foo & wait "$!" && err_exit "'exit' within function does not preserve exit status (bg job)"
foo() ( false; exit || : )
foo && err_exit "'exit ||' does not preserve exit status"
foo() ( false; exit && : )
foo && err_exit "'exit &&' does not preserve exit status"
foo() ( false; while exit; do true; done )
foo && err_exit "'while exit' does not preserve exit status"
foo() ( false; while exit; do true; done 2>&1 )
foo && err_exit "'while exit' with redirection does not preserve exit status"
foo() ( false; until exit; do true; done )
foo && err_exit "'until exit' does not preserve exit status"
foo() ( false; until exit; do true; done 2>&1 )
foo && err_exit "'until exit' with redirection does not preserve exit status"
foo() ( false; for i in 1; do exit; done )
foo && err_exit "'exit' within 'for' does not preserve exit status"
foo() ( false; for i in 1; do exit; done 2>&1 )
foo && err_exit "'exit' within 'for' with redirection does not preserve exit status"
foo() ( false; { exit; } 2>&1 )
foo && err_exit "'exit' within { block; } with redirection does not preserve exit status"
set --
false
for i in "$@"; do :; done || err_exit 'empty loop does not reset exit status ("$@")'
false
for i do :; done || err_exit 'empty loop does not reset exit status ("$@" shorthand)'
false
for i in; do :; done || err_exit "empty loop does not reset exit status (empty 'in' list)"
false
case 1 in 1) ;; esac || err_exit "empty case does not reset exit status"
false
case 1 in 2) echo whoa ;; esac || err_exit "non-matching case does not reset exit status"
false
2>&1 || err_exit "lone redirection does not reset exit status"
float s=SECONDS
for i in .1 .2
do print $i