bin/116559: make case statement of Bourne shell IEEE 1003.2-conformant

Eygene Ryabinkin rea-fbsd at codelabs.ru
Sat Sep 22 11:20:04 PDT 2007


>Number:         116559
>Category:       bin
>Synopsis:       make case statement of Bourne shell IEEE 1003.2-conformant
>Confidential:   no
>Severity:       critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sat Sep 22 18:20:02 GMT 2007
>Closed-Date:
>Last-Modified:
>Originator:     Eygene Ryabinkin
>Release:        FreeBSD 7.0-CURRENT i386
>Organization:
Code Labs
>Environment:

System: FreeBSD XXX 7.0-CURRENT FreeBSD 7.0-CURRENT #10: Wed Sep 12 16:16:49 MSD 2007 root at XXX:/usr/src/sys/i386/compile/XXX i386

>Description:

IEEE 1003.2 specifies that the return value of the 'case' construct
that did not matched any patterns shall be zero.  It is not the
case for the current FreeBSD /bin/sh.

>How-To-Repeat:

Run the test script that is embedded into the patch comments.

>Fix:

The following patch will correct and document the expected behaviour.

--- sh.patch begins here ---
According to the standard (http://opengroup.org/onlinepubs/000095399,
Shell and Utilities Volume (XCU), Case Conditional Construct), the
'case' statement shall return zero if no patterns were matched.

Currently, Bourne shell in FreeBSD does not change the exit code
on the 'case' construct that had not matched anything; it is
completely transparent in this regard.

The following test script shall not produce any messages with the
1003.2-conformant shells and its return code shall be zero:
-----
err () {
	echo "ERROR:    $@"
}

case_and_immediate () {
	false
	case Ultra in
	Super)
		false
		;;
	Hyper)
		true
		;;
	esac && echo ok
}

case_while_immediate () {
	false
	while case Ultra in Super) break ;; esac
	do
		echo ok
		break
	done
}

retval=0

for test in \
	case_and_immediate \
	case_while_immediate
do
	result=`$test`
	if test "$result" != ok; then
		err "$test" failed
		retval=$(($retval + 1))
	fi
done

exit $retval
-----

The real rationale for this change is that some utilities are
using constructs like
-----
while case "#?" in 0) break ;; esac
do
	case "$1" in
	--someflag)
		...
		;;
	...
	esac
done
-----
for the argument parsing.  The example I had before my eyes is
Git 1.5.3.2, script named git-commit.

Signed-off-by: Eygene Ryabinkin <rea-fbsd at codelabs.ru>
---
 eval.c |    1 +
 sh.1   |    4 ++++
 2 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/eval.c b/eval.c
index 9b41f63..6d24df2 100644
--- a/eval.c
+++ b/eval.c
@@ -367,6 +367,7 @@ evalcase(union node *n, int flags)
 	setstackmark(&smark);
 	arglist.lastp = &arglist.list;
 	oexitstatus = exitstatus;
+	exitstatus = 0;
 	expandarg(n->ncase.expr, &arglist, EXP_TILDE);
 	for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
 		for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
diff --git a/sh.1 b/sh.1
index 72aa5c5..41779fe 100644
--- a/sh.1
+++ b/sh.1
@@ -861,6 +861,10 @@ described later),
 separated by
 .Dq Li \&|
 characters.
+The exit code of the
+.Ic case
+command is the exit code of the last command executed in the list
+or zero if no patterns were matched.
 .Ss Grouping Commands Together
 Commands may be grouped by writing either
 .Bd -literal -offset indent
--- sh.patch ends here ---
>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list