NetBSD-Bugs archive

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

bin/54112: /bin/sh "$@" still broken: when field splitting this time

>Number:         54112
>Category:       bin
>Synopsis:       /bin/sh "$@" still broken: when field splitting this time
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    bin-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Apr 10 06:50:00 +0000 2019
>Originator:     Robert Elz
>Release:        NetBSD 8.99.30 (any version at all)
System: NetBSD 8.99.30 NetBSD 8.99.30 (1.1-20190114) #9: Mon Jan 14 13:29:08 ICT 2019 amd64
Architecture: x86_64
Machine: amd64
		argc() { echo "$#"; }
		set -- ''

		argc ${0+"$@" }
	should say "1" but says "0" instead.

	This variant:
		argc ${0+"$@"}
	does give 1 (as it should).

	As above.

	Yes, this is an absurd usage, but still valid, and it should work.

	This test case came from a bug-bash bug report, which reported
	a problem with expansion of ${0+"$@" "$@"} so I tested it with
	our /bin/sh.

	Sigh.    (I suspect the bash issue is/was something quite different).

	The problem is that "$@" produces a "" string (as it should) but
	that is represented as the C string "" (ie: nothing).   When that
	is abutted to the space, we get " ", which we then field split,
	which eliminates the space, leaving nothing at all.

	When the space is not there, the result is fully quoted, we have
	no field splitting regions, so no field splitting actually happens,
	and the null string remained untouched.

	In all of these the only relevance of the use of $0 is that we
	know $0 is set, so the text after the + is expanded to produce
	the result -- any set variable would do.   Similar things happened
	in the case of ${unset-"$@" } of course.

	Coming very soon.   This PR is mostly just so something can be
	referenced in the CVS commit message, rather than including all
	this noise there, and can also be referenced in the new ATF
	tests that will also be added to look for this case.

	Most of this has been fixed and waiting to get processed for a
	month now, but the fix originally broke
		set --; argc "${0+$@}"
	producing 1 instead of 0 (which POSIX as it is right now requires).

	That POSIX is to be changed to make this unspecified (as most
	other shells produce 1 for this case) is irrelevant here, this
	expansion was fixed a while ago in this shell, and the fix should
	be retained (0 is the logically sensible value).   The previous fix
	for this was one of the ugliest of the old gross hacks, now being
	removed.   Good riddance!   The newer solution isn't exactly a marvel
	of astonishing beauty either, unfortunately.

	Note that this change does alter the way we interpret:

		set -- ; X=;  argc ${0+"$X$@"}

	Previuously we produced 1, now we produce 0.   This one has been an
	unspecified case in POSIX for a while now, different shells handle it
	differently ... except for this there is no obvious correct answer,
	either way can be argued to be better, so I believe this change to be

	The rule now is (for this unspecified case -- so no-one should
	rely upon it) that when we have a double quoted string (any subset
	of a word), and $@ appears inside the double quotes, and in a context
	where "$@" makes sense, if after that string has been expanded all we
	have left is the surrounding "" then the whole string (that is, the
	remaining pair of quotes) is simply deleted.   "$@" is the obvious
	simplest example of this (here producing nothing is required, and
	you can rely upon that).  The unspecified case is when there was
	something else inside the same "" pair as the $@ which also expanded
	to nothing.  "$@$@" is another simple example.  That one we used to
	produce nothing for (weirdly different from the $X$@ case) whereas
	some other shells produce "" (1 arg) for that.   With the new rule,
	we will continue producing 0 (nothing) for the double $@ example,
	and now also fpr the $X$@ example (or $@$X etc).

	Note: all of this applies only in contexts were the magic "$@"
	rules apply, everwhere else we treat $@ the same as $* (different
	rules).  That's actually mostly another unspecified case, so you
	should not rely upon $@ being the same as $* - just use $* and not
	$@ when appropriate.

Home | Main Index | Thread Index | Old Index