A few questions about escaping in desktop files

Simon McVittie smcv at collabora.com
Tue Aug 23 14:30:03 UTC 2022


On Mon, 22 Aug 2022 at 21:40:58 +0200, meator wrote:
> Let the shell do the unquoting?! To quote (pun intended) the standard:
> 
> > Implementations must undo quoting before expanding field codes and before
> > passing the argument to the executable program.
> 
> Isn't this forbidden? I guess the universal as-if rule mandates that if it
> won't change the behavior than it's possible, but this is strange.

That wording seems strange when compared with how GLib implements it,
but again, I didn't write this spec; all I can tell you is what one
of the implementations that have been in daily use for many years is
doing. Perhaps GLib is relying on the "as-if" rule, or perhaps the spec
doesn't match reality, or perhaps the spec author meant something that
is not how we're interpreting it.

> Why is [GLib] quoting the evaluated field code? The filenames and URLs are
> pretty unambiguous and could be copied verbatim into the argument without
> fear of something misinterpreting it. If I understand it correctly, it's
> going to be unquoted by g_shell_parse_argv() you mentioned.

Because GLib is substituting the field codes before doing shell-style
parsing, it would be wrong for it not to quote the expansion of the field
codes. If it didn't quote the expansion of the field codes, then the
shell-style parsing would give it the wrong answer, because not every
character in a shell-style string represents itself: some are metacharacters
with a special meaning, like quotes and spaces.

For instance, imagine you have a filename containing spaces, double quotes,
backticks or other awkward characters (this is valid, Unix filenames can
contain anything except '/' and '\x00'). If you have a .desktop file with

    Exec=/usr/bin/gvim %f

then you need to end up with the equivalent of this argv for gvim:

    char **argv = {"/usr/bin/gvim", "/path/to/Simon's \"awkward\" filename", NULL}

(You said you're using C++, so I'm using C/C++ string literal syntax.)

There are basically two ways you could achieve that. The one GLib has
chosen is to replace %f with a shell-quoted version of the path:

    /usr/bin/gvim '/path/to/Simon'"'"'s "awkward" filename'

(or anything that would be parsed by a shell as being equivalent to that,
there are lots of choices), and then let g_shell_parse_argv() parse that
to get the desired argv, which it passes to execve() or equivalent.

A variation on this approach, which I think Qt/KDE uses, is to replace
%f with a similarly shell-quoted version of the path, but then pass the
whole thing to a shell and let the shell produce the argv, equivalent to
this C/C++ syntax:

    char **argv = {"sh", "-c", "exec /usr/bin/gvim \"/path/to/Simon's \\\"awkward\\\" filename\"", NULL}

The other way you could potentially achieve this would be to do the
shell-parsing first, to get:

    {"/usr/bin/gvim", "%f", NULL}

and then replace %f in-place, this time without shell-style quoting:

    {"/usr/bin/gvim", "/path/to/Simon's \"awkward\" filename", NULL}

The spec forbids field codes inside a quoted argument, which if I understand
correctly makes either of these strategies viable.

> I'll have to somehow configure
> Gnome and KDE on my system or find their specific program that does this but
> this will work.

For the GNOME side, all you'd really need is the GLib library, not
any GUIs. GDesktopAppInfo objects are responsible for handling .desktop
files.

For Qt/KDE, I don't know which specific code is the equivalent of
GDesktopAppInfo.

    smcv


More information about the xdg mailing list