Skip to content

Commit

Permalink
Fix "$@$@" corner case
Browse files Browse the repository at this point in the history
At <https://austingroupbugs.net/view.php?id=1852>, Steffen Nurpmeso
(@sdaoden) writes:
>    twox() { one "$@$@"; }
[...]
> shells will give different results for the argument-less call to
> twox(). (Here on my box bash(1) is the only one which expands
> "$@$@" to nothing.)

Geoff Clare responds:
> The standard clearly requires "$@$@" to generate zero fields if
> there are no positional parameters. The quoted text "if the
> expansion is embedded within a word which contains one or more
> other parts that expand to a quoted null string, ..." does not
> apply because there are no other parts that expand to a quoted
> null string. (The second $@ is not a candidate for being such a
> part because "the expansion of '@' shall generate zero fields".)

Simple reproducer:

$ set --  # ensure zero PPs
$ set -- "$@$@"
$ echo $#
1

Expected output: 0

ksh has further inconsistent behaviour: e.g., for zero positional
parameters, ''"$@" correctly yields one empty field, ''"$@$@"
yields zero, and ''"$@$@$@" and on yield one again, but
''''"$@$@$@" yields zero -- etc.

Analysis: In copyto() in macro.c:705, the mp->quoted variable is
increased by one for each shell quote encountered in a word. In
varsub() in macro.c:1888, this variable is decreased by two if a
quoted $@ is encountered with zero positional parameters. For a
simple "$@" which has two quotes, this results in a decrease of two
and an mp->quoted value of 0 once both quotes have been parsed.
Effectively, both quotes have "disappeared", which allows empty
removal to take effect: correct behaviour. But if another $@ exists
within the same set of quotes, that decrease by two is done again,
causing incorrect behaviour.

src/cmd/ksh93/sh/macro.c:
- Add new 'atspecial' member to Mac_t struct.
- varsub(): When applying the quote count reduction for quoted "$@"
  with zero PPs, remember the current mp->quoted count in
  mp->atspecial, and do not apply the reduction again unless
  mp->quoted has changed from mp->atspecial. This fixes the bug.
  • Loading branch information
McDutchie committed Aug 20, 2024
1 parent a71f1fd commit a827b15
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 5 deletions.
5 changes: 5 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ This documents significant changes in the dev branch of ksh 93u+m.
For full details, see the git log at: https://github.com/ksh93/ksh
Uppercase BUG_* IDs are shell bug IDs as used by the Modernish shell library.

2024-08-19:

- Fixed a corner-case bug: for an empty set of positional parameters,
"$@$@" would generate one empty field instead of zero fields like "$@".

2024-07-31:

- Fixed a bug where printf %T, after having printed the time in UTC once
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/ksh93/include/version.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

#define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */
#define SH_RELEASE_SVER "1.1.0-alpha" /* semantic version number: https://semver.org */
#define SH_RELEASE_DATE "2024-08-01" /* must be in this format for $((.sh.version)) */
#define SH_RELEASE_DATE "2024-08-19" /* must be in this format for $((.sh.version)) */
#define SH_RELEASE_CPYR "(c) 2020-2024 Contributors to ksh " SH_RELEASE_FORK

/* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */
Expand Down
7 changes: 4 additions & 3 deletions src/cmd/ksh93/sh/macro.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ typedef struct _mac_
char *ifsp; /* pointer to IFS value */
int fields; /* number of fields */
short quoted; /* set when word has quotes */
short atspecial; /* quoted count at which special-casing for "$@" with no PPs has been applied */
unsigned char ifs; /* first byte of IFS */
char atmode; /* when processing $@ */
char quote; /* set within double quoted contexts */
Expand Down Expand Up @@ -1882,9 +1883,9 @@ static int varsub(Mac_t *mp)
if(v || c=='/' && offset>=0)
stkseek(stkp,offset);
}
/* check for quoted @ */
if(mode=='@' && mp->quote && !v && c!='-')
mp->quoted-=2;
/* discount the quotes around $@ if there are zero PPs, so that empty "$@" generates zero fields */
if(mode=='@' && mp->quote && !v && c!='-' && mp->atspecial != mp->quoted)
mp->quoted -= 2, mp->atspecial = mp->quoted;
retry2:
if(v && (!nulflg || *v ) && c!='+')
{
Expand Down
31 changes: 30 additions & 1 deletion src/cmd/ksh93/tests/quoting.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# #
# This software is part of the ast package #
# Copyright (c) 1982-2011 AT&T Intellectual Property #
# Copyright (c) 2020-2022 Contributors to ksh 93u+m #
# Copyright (c) 2020-2024 Contributors to ksh 93u+m #
# and is licensed under the #
# Eclipse Public License, Version 2.0 #
# #
Expand Down Expand Up @@ -369,4 +369,33 @@ case x in
$x) err_exit "case \$x='$x' should not match x";;
esac

# ======
# https://austingroupbugs.net/view.php?id=1852
# https://mail.gnu.org/archive/html/bug-bash/2024-08/msg00132.html
unset e E q i
for e in 3 '"$@"' 5 '"$@$@"' 7 '"$@$@$@"' 9 '"$@$@$@$@"' 11 '"$@$@$@$@$@"' 13 '"$@$@$@$@$@$@"' \
5 '"$@""$@"' 7 '"$@""$@""$@"' 9 '"$@""$@""$@""$@"' 11 '"$@""$@""$@""$@""$@"' 13 '"$@""$@""$@""$@""$@""$@"'
do [[ $e == [0-9]* ]] && i=$e && continue
set -- # set zero PPs
eval "set -- $e"
(($# == 0)) || err_exit "$e does not yield zero fields for zero positional parameters (got $#)"
set -- one two three
eval "set -- $e"
(($# == i)) || err_exit "$e does not yield $i fields for 3 positional parameters (got $#)"
for q in "''" '""'
do for q in "$q" "$q$q" "$q$q$q" "$q$q$q$q" "$q$q$q$q$q" "$q$q$q$q$q$q"
do for E in "$q$e" "$e$q" "$q$e$q"
do set -- # set zero PPs
eval "set -- $E"
(($# == 1)) || err_exit "$E does not yield one field for zero positional parameters (got $#)"
set -- one two three
eval "set -- $E"
(($# == i)) || err_exit "$E does not yield $i fields for 3 positional parameters (got $#)"
done
done
done
((i+=2))
done

# ======
exit $((Errors<125?Errors:125))

0 comments on commit a827b15

Please sign in to comment.