Here is another patch related to "$@" and "$*". It makes their behaviour
in combination with 'set -u' (set -o nounset) POSIX-compliant and
consistent with other current shells.

As of 2009, POSIX mandates that the special parameters "$@" and "$*" be
exempt from 'set -u' checking. The reason is that it should be possible
to pass on an empty set of positional parameters even when expansions of
unset variables are disallowed. ksh currently does not do this, making
'set -u' less useful.

I've also attached a simple regression test. I didn't know what existing
.t file that would fit in, so I made a new one.

Thanks,

- M.

References:

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_25_03
"When the shell tries to expand an unset parameter other than the '@'
and '*' special parameters, it shall write a message to standard error
and shall not execute the command containing the expansion [...]"

#tag_18_25_18
"Historically, some shells applied the -u option to all parameters
including $@ and $*. The standard developers felt that this was a
misfeature since it is normal and common for $@ and $* to be used in
shell scripts regardless of whether they were passed any arguments.
Treating these uses as an error when no arguments are passed reduces the
value of -u for its intended purpose of finding spelling mistakes in
variable names and uses of unset positional parameters."
Index: bin/ksh/eval.c
===================================================================
RCS file: /cvs/src/bin/ksh/eval.c,v
retrieving revision 1.49
diff -u -p -r1.49 eval.c
--- bin/ksh/eval.c	30 Dec 2015 09:07:00 -0000	1.49
+++ bin/ksh/eval.c	4 Mar 2016 04:44:31 -0000
@@ -707,6 +707,7 @@ varsub(Expand *xp, char *sp, char *word,
 	int slen;
 	char *p;
 	struct tbl *vp;
+	int zero_ok = 0;
 
 	if (sp[0] == '\0')	/* Bad variable name */
 		return -1;
@@ -715,8 +716,6 @@ varsub(Expand *xp, char *sp, char *word,
 
 	/* ${#var}, string length or array size */
 	if (sp[0] == '#' && (c = sp[1]) != '\0') {
-		int zero_ok = 0;
-
 		/* Can't have any modifiers for ${#...} */
 		if (*word != CSUBST)
 			return -1;
@@ -789,6 +788,7 @@ varsub(Expand *xp, char *sp, char *word,
 			xp->split = c == '@'; /* $@ */
 			state = XARG;
 		}
+		zero_ok = 1;	/* exempt "$@" and "$*" from 'set -u' */
 	} else {
 		if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') {
 			XPtrV wv;
@@ -835,7 +835,7 @@ varsub(Expand *xp, char *sp, char *word,
 	    (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */
 	    c == '=' || c == '-' || c == '?' : c == '+'))
 		state = XBASE;	/* expand word instead of variable value */
-	if (Flag(FNOUNSET) && xp->str == null &&
+	if (Flag(FNOUNSET) && xp->str == null && !zero_ok &&
 	    (ctype(c, C_SUBOP2) || (state != XBASE && c != '+')))
 		errorf("%s: parameter not set", sp);
 	return state;
name: shellopt-u-1
description:
        Check that "$@" and "$*" are exempt from 'set -u' (nounset)
stdin:
        set -u
        : "$@$*$1"
expected-exit: e == 1
expected-stderr-pattern:
        /: 1: parameter not set$/
---

Reply via email to