diff --git a/i3-dmenu-desktop b/i3-dmenu-desktop index e43d95aa..bb693350 100755 --- a/i3-dmenu-desktop +++ b/i3-dmenu-desktop @@ -413,7 +413,7 @@ my $exec = $app->{Exec}; my $location = $app->{_Location}; # Quote as described by “The Exec key”: -# https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html +# https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s07.html sub quote { my ($str) = @_; $str =~ s/("|`|\$|\\)/\\$1/g; @@ -425,6 +425,17 @@ $choice = quote($choice); $location = quote($location); $name = quote($name); +# https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s07.html: +# +# Note that the general escape rule for values of type string states that the +# backslash character can be escaped as ("\\") as well and that this escape rule +# is applied before the quoting rule. As such, to unambiguously represent a +# literal backslash character in a quoted argument in a desktop entry file +# requires the use of four successive backslash characters ("\\\\"). Likewise, a +# literal dollar sign in a quoted argument in a desktop entry file is +# unambiguously represented with ("\\$"). +$exec =~ s/\\\\/\\/g; + # Remove deprecated field codes, as the spec dictates. $exec =~ s/%[dDnNvm]//g; @@ -481,9 +492,10 @@ EOT # starts with a double quote ("), everything is parsed as-is until the next # double quote which is NOT preceded by a backslash (\). # - # Therefore, we escape all double quotes (") by replacing them with \" - $exec =~ s/\\"/\\\\\\"/g; - $exec =~ s/([^\\])"/$1\\"/g; + # Therefore, we escape all double quotes (") by replacing them with \". + # To not change the meaning of any double quote, backslashes need to be + # escaped as well. + $exec =~ s/(["\\])/\\$1/g; if (exists($app->{StartupNotify}) && !$app->{StartupNotify}) { $nosn = '--no-startup-id'; diff --git a/testcases/t/318-i3-dmenu-desktop.t b/testcases/t/318-i3-dmenu-desktop.t new file mode 100644 index 00000000..75d983ef --- /dev/null +++ b/testcases/t/318-i3-dmenu-desktop.t @@ -0,0 +1,125 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • https://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • https://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • https://build.i3wm.org/docs/ipc.html +# (or docs/ipc) +# +# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf +# (unless you are already familiar with Perl) +# +# Verifies that i3-dmenu-desktop correctly parses Exec= lines in .desktop files +# and sends the command to i3 for execution. +# Ticket: #5152, #5156 +# Bug still in: 4.21-17-g389d555d +use i3test; +use i3test::Util qw(slurp); +use File::Temp qw(tempfile tempdir); +use POSIX qw(mkfifo); +use JSON::XS qw(decode_json); + +my $desktopdir = tempdir(CLEANUP => 1); + +$ENV{XDG_DATA_DIRS} = "$desktopdir"; + +mkdir("$desktopdir/applications"); + +# Create an i3-msg executable that dumps command line flags to a FIFO +my $tmpdir = tempdir(CLEANUP => 1); + +$ENV{PATH} = "$tmpdir:" . $ENV{PATH}; + +mkfifo("$tmpdir/fifo", 0600) or BAIL_OUT "Could not create FIFO: $!"; + +open(my $i3msg_dump, '>', "$tmpdir/i3-msg"); +say $i3msg_dump <', "$tmpdir/fifo"); +say \$f encode_json(\\\@ARGV); +close(\$f); +EOT +close($i3msg_dump); +chmod 0755, "$tmpdir/i3-msg"; + +my $testcnt = 0; +sub verify_exec { + my ($execline, $want_arg) = @_; + + $testcnt++; + + open(my $desktop, '>', "$desktopdir/applications/desktop$testcnt.desktop"); + say $desktop <