+ g_free (info->name);
+ g_free (info->generic_name);
+ g_free (info->fullname);
+ g_free (info->comment);
+ g_free (info->icon_name);
+ if (info->icon)
+ g_object_unref (info->icon);
+ g_strfreev (info->keywords);
+ g_strfreev (info->only_show_in);
+ g_strfreev (info->not_show_in);
+ g_free (info->try_exec);
+ g_free (info->exec);
+ g_free (info->binary);
+ g_free (info->path);
+ g_free (info->categories);
+ g_free (info->startup_wm_class);
+ g_strfreev (info->mime_types);
+ g_free (info->app_id);
+ g_strfreev (info->actions);
+
+ G_OBJECT_CLASS (g_desktop_app_info_parent_class)->finalize (object);
+}
+
+static void
+g_desktop_app_info_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GDesktopAppInfo *self = G_DESKTOP_APP_INFO (object);
+
+ switch (prop_id)
+ {
+ case PROP_FILENAME:
+ self->filename = g_value_dup_string (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+g_desktop_app_info_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GDesktopAppInfo *self = G_DESKTOP_APP_INFO (object);
+
+ switch (prop_id)
+ {
+ case PROP_FILENAME:
+ g_value_set_string (value, self->filename);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+g_desktop_app_info_class_init (GDesktopAppInfoClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->get_property = g_desktop_app_info_get_property;
+ gobject_class->set_property = g_desktop_app_info_set_property;
+ gobject_class->finalize = g_desktop_app_info_finalize;
+
+ /**
+ * GDesktopAppInfo:filename:
+ *
+ * The origin filename of this #GDesktopAppInfo
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_FILENAME,
+ g_param_spec_string ("filename", "Filename", "", NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+g_desktop_app_info_init (GDesktopAppInfo *local)
+{
+}
+
+/* Construction... {{{2 */
+
+/*< internal >
+ * binary_from_exec:
+ * @exec: an exec line
+ *
+ * Returns the first word in an exec line (ie: the binary name).
+ *
+ * If @exec is " progname --foo %F" then returns "progname".
+ */
+static char *
+binary_from_exec (const char *exec)
+{
+ const char *p, *start;
+
+ p = exec;
+ while (*p == ' ')
+ p++;
+ start = p;
+ while (*p != ' ' && *p != 0)
+ p++;
+
+ return g_strndup (start, p - start);
+}
+
+static gboolean
+g_desktop_app_info_load_from_keyfile (GDesktopAppInfo *info,
+ GKeyFile *key_file)
+{
+ char *start_group;
+ char *type;
+ char *try_exec;
+ char *exec;
+ gboolean bus_activatable;
+
+ start_group = g_key_file_get_start_group (key_file);
+ if (start_group == NULL || strcmp (start_group, G_KEY_FILE_DESKTOP_GROUP) != 0)
+ {
+ g_free (start_group);
+ return FALSE;
+ }
+ g_free (start_group);
+
+ type = g_key_file_get_string (key_file,
+ G_KEY_FILE_DESKTOP_GROUP,
+ G_KEY_FILE_DESKTOP_KEY_TYPE,
+ NULL);
+ if (type == NULL || strcmp (type, G_KEY_FILE_DESKTOP_TYPE_APPLICATION) != 0)
+ {
+ g_free (type);
+ return FALSE;
+ }
+ g_free (type);
+
+ try_exec = g_key_file_get_string (key_file,
+ G_KEY_FILE_DESKTOP_GROUP,
+ G_KEY_FILE_DESKTOP_KEY_TRY_EXEC,
+ NULL);
+ if (try_exec && try_exec[0] != '\0')
+ {
+ char *t;
+ t = g_find_program_in_path (try_exec);
+ if (t == NULL)
+ {
+ g_free (try_exec);
+ return FALSE;
+ }
+ g_free (t);
+ }
+
+ exec = g_key_file_get_string (key_file,
+ G_KEY_FILE_DESKTOP_GROUP,
+ G_KEY_FILE_DESKTOP_KEY_EXEC,
+ NULL);
+ if (exec && exec[0] != '\0')
+ {
+ gint argc;
+ char **argv;
+ if (!g_shell_parse_argv (exec, &argc, &argv, NULL))
+ {
+ g_free (exec);
+ g_free (try_exec);
+ return FALSE;
+ }
+ else
+ {
+ char *t;
+ t = g_find_program_in_path (argv[0]);
+ g_strfreev (argv);
+
+ if (t == NULL)
+ {
+ g_free (exec);
+ g_free (try_exec);
+ return FALSE;
+ }
+ g_free (t);
+ }
+ }
+
+ info->name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL, NULL);
+ info->generic_name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, GENERIC_NAME_KEY, NULL, NULL);
+ info->fullname = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, FULL_NAME_KEY, NULL, NULL);
+ info->keywords = g_key_file_get_locale_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, KEYWORDS_KEY, NULL, NULL, NULL);
+ info->comment = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_COMMENT, NULL, NULL);
+ info->nodisplay = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, NULL) != FALSE;
+ info->icon_name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, NULL, NULL);
+ info->only_show_in = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN, NULL, NULL);
+ info->not_show_in = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN, NULL, NULL);
+ info->try_exec = try_exec;
+ info->exec = exec;
+ info->path = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_PATH, NULL);
+ info->terminal = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TERMINAL, NULL) != FALSE;
+ info->startup_notify = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY, NULL) != FALSE;
+ info->no_fuse = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GIO-NoFuse", NULL) != FALSE;
+ info->hidden = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_HIDDEN, NULL) != FALSE;
+ info->categories = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_CATEGORIES, NULL);
+ info->startup_wm_class = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, STARTUP_WM_CLASS_KEY, NULL);
+ info->mime_types = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_MIME_TYPE, NULL, NULL);
+ bus_activatable = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_DBUS_ACTIVATABLE, NULL);
+ info->actions = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ACTIONS, NULL, NULL);
+
+ /* Remove the special-case: no Actions= key just means 0 extra actions */
+ if (info->actions == NULL)
+ info->actions = g_new0 (gchar *, 0 + 1);
+
+ info->icon = NULL;
+ if (info->icon_name)
+ {
+ if (g_path_is_absolute (info->icon_name))
+ {
+ GFile *file;
+
+ file = g_file_new_for_path (info->icon_name);
+ info->icon = g_file_icon_new (file);
+ g_object_unref (file);
+ }
+ else
+ {
+ char *p;
+
+ /* Work around a common mistake in desktop files */
+ if ((p = strrchr (info->icon_name, '.')) != NULL &&
+ (strcmp (p, ".png") == 0 ||
+ strcmp (p, ".xpm") == 0 ||
+ strcmp (p, ".svg") == 0))
+ *p = 0;
+
+ info->icon = g_themed_icon_new (info->icon_name);
+ }
+ }
+
+ if (info->exec)
+ info->binary = binary_from_exec (info->exec);
+
+ if (info->path && info->path[0] == '\0')
+ {
+ g_free (info->path);
+ info->path = NULL;
+ }
+
+ /* Can only be DBusActivatable if we know the filename, which means
+ * that this won't work for the load-from-keyfile case.
+ */
+ if (bus_activatable && info->filename)
+ {
+ gchar *basename;
+ gchar *last_dot;
+
+ basename = g_path_get_basename (info->filename);
+ last_dot = strrchr (basename, '.');
+
+ if (last_dot && g_str_equal (last_dot, ".desktop"))
+ {
+ *last_dot = '\0';
+
+ if (g_dbus_is_interface_name (basename))
+ info->app_id = g_strdup (basename);
+ }
+
+ g_free (basename);
+ }
+
+ info->keyfile = g_key_file_ref (key_file);
+
+ return TRUE;
+}
+
+static gboolean
+g_desktop_app_info_load_file (GDesktopAppInfo *self)
+{
+ GKeyFile *key_file;
+ gboolean retval = FALSE;
+
+ g_return_val_if_fail (self->filename != NULL, FALSE);
+
+ self->desktop_id = g_path_get_basename (self->filename);
+
+ key_file = g_key_file_new ();
+
+ if (g_key_file_load_from_file (key_file, self->filename, G_KEY_FILE_NONE, NULL))
+ retval = g_desktop_app_info_load_from_keyfile (self, key_file);
+
+ g_key_file_unref (key_file);
+ return retval;
+}
+
+/**
+ * g_desktop_app_info_new_from_keyfile:
+ * @key_file: an opened #GKeyFile
+ *
+ * Creates a new #GDesktopAppInfo.
+ *
+ * Returns: a new #GDesktopAppInfo or %NULL on error.
+ *
+ * Since: 2.18
+ **/
+GDesktopAppInfo *
+g_desktop_app_info_new_from_keyfile (GKeyFile *key_file)
+{
+ GDesktopAppInfo *info;
+
+ info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
+ info->filename = NULL;
+ if (!g_desktop_app_info_load_from_keyfile (info, key_file))
+ {
+ g_object_unref (info);
+ return NULL;
+ }
+ return info;
+}
+
+/**
+ * g_desktop_app_info_new_from_filename:
+ * @filename: the path of a desktop file, in the GLib filename encoding
+ *
+ * Creates a new #GDesktopAppInfo.
+ *
+ * Returns: a new #GDesktopAppInfo or %NULL on error.
+ **/
+GDesktopAppInfo *
+g_desktop_app_info_new_from_filename (const char *filename)
+{
+ GDesktopAppInfo *info = NULL;
+
+ info = g_object_new (G_TYPE_DESKTOP_APP_INFO, "filename", filename, NULL);
+ if (!g_desktop_app_info_load_file (info))
+ {
+ g_object_unref (info);
+ return NULL;
+ }
+ return info;
+}
+
+/**
+ * g_desktop_app_info_new:
+ * @desktop_id: the desktop file id
+ *
+ * Creates a new #GDesktopAppInfo based on a desktop file id.
+ *
+ * A desktop file id is the basename of the desktop file, including the
+ * .desktop extension. GIO is looking for a desktop file with this name
+ * in the `applications` subdirectories of the XDG
+ * data directories (i.e. the directories specified in the `XDG_DATA_HOME`
+ * and `XDG_DATA_DIRS` environment variables). GIO also supports the
+ * prefix-to-subdirectory mapping that is described in the
+ * [Menu Spec](http://standards.freedesktop.org/menu-spec/latest/)
+ * (i.e. a desktop id of kde-foo.desktop will match
+ * `/usr/share/applications/kde/foo.desktop`).
+ *
+ * Returns: a new #GDesktopAppInfo, or %NULL if no desktop file with that id
+ */
+GDesktopAppInfo *
+g_desktop_app_info_new (const char *desktop_id)
+{
+ GDesktopAppInfo *appinfo = NULL;
+ guint i;
+
+ desktop_file_dirs_lock ();
+
+ for (i = 0; i < n_desktop_file_dirs; i++)
+ {
+ appinfo = desktop_file_dir_get_app (&desktop_file_dirs[i], desktop_id);
+
+ if (appinfo)
+ break;
+ }
+
+ desktop_file_dirs_unlock ();
+
+ if (appinfo == NULL)
+ return NULL;
+
+ g_free (appinfo->desktop_id);
+ appinfo->desktop_id = g_strdup (desktop_id);
+
+ if (g_desktop_app_info_get_is_hidden (appinfo))
+ {
+ g_object_unref (appinfo);
+ appinfo = NULL;
+ }
+
+ return appinfo;
+}
+
+static GAppInfo *
+g_desktop_app_info_dup (GAppInfo *appinfo)
+{
+ GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+ GDesktopAppInfo *new_info;
+
+ new_info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
+
+ new_info->filename = g_strdup (info->filename);
+ new_info->desktop_id = g_strdup (info->desktop_id);
+
+ if (info->keyfile)
+ new_info->keyfile = g_key_file_ref (info->keyfile);
+
+ new_info->name = g_strdup (info->name);
+ new_info->generic_name = g_strdup (info->generic_name);
+ new_info->fullname = g_strdup (info->fullname);
+ new_info->keywords = g_strdupv (info->keywords);
+ new_info->comment = g_strdup (info->comment);
+ new_info->nodisplay = info->nodisplay;
+ new_info->icon_name = g_strdup (info->icon_name);
+ if (info->icon)
+ new_info->icon = g_object_ref (info->icon);
+ new_info->only_show_in = g_strdupv (info->only_show_in);
+ new_info->not_show_in = g_strdupv (info->not_show_in);
+ new_info->try_exec = g_strdup (info->try_exec);
+ new_info->exec = g_strdup (info->exec);
+ new_info->binary = g_strdup (info->binary);
+ new_info->path = g_strdup (info->path);
+ new_info->app_id = g_strdup (info->app_id);
+ new_info->hidden = info->hidden;
+ new_info->terminal = info->terminal;
+ new_info->startup_notify = info->startup_notify;
+
+ return G_APP_INFO (new_info);
+}
+
+/* GAppInfo interface implementation functions {{{2 */
+
+static gboolean
+g_desktop_app_info_equal (GAppInfo *appinfo1,
+ GAppInfo *appinfo2)
+{
+ GDesktopAppInfo *info1 = G_DESKTOP_APP_INFO (appinfo1);
+ GDesktopAppInfo *info2 = G_DESKTOP_APP_INFO (appinfo2);
+
+ if (info1->desktop_id == NULL ||
+ info2->desktop_id == NULL)
+ return info1 == info2;
+
+ return strcmp (info1->desktop_id, info2->desktop_id) == 0;
+}
+
+static const char *
+g_desktop_app_info_get_id (GAppInfo *appinfo)
+{
+ GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+
+ return info->desktop_id;
+}
+
+static const char *
+g_desktop_app_info_get_name (GAppInfo *appinfo)
+{
+ GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+
+ if (info->name == NULL)
+ return _("Unnamed");
+ return info->name;
+}
+
+static const char *
+g_desktop_app_info_get_display_name (GAppInfo *appinfo)
+{
+ GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+
+ if (info->fullname == NULL)
+ return g_desktop_app_info_get_name (appinfo);
+ return info->fullname;
+}
+
+/**
+ * g_desktop_app_info_get_is_hidden:
+ * @info: a #GDesktopAppInfo.
+ *
+ * A desktop file is hidden if the Hidden key in it is
+ * set to True.
+ *
+ * Returns: %TRUE if hidden, %FALSE otherwise.
+ **/
+gboolean
+g_desktop_app_info_get_is_hidden (GDesktopAppInfo *info)
+{
+ return info->hidden;
+}
+
+/**
+ * g_desktop_app_info_get_filename:
+ * @info: a #GDesktopAppInfo
+ *
+ * When @info was created from a known filename, return it. In some
+ * situations such as the #GDesktopAppInfo returned from
+ * g_desktop_app_info_new_from_keyfile(), this function will return %NULL.
+ *
+ * Returns: The full path to the file for @info, or %NULL if not known.
+ * Since: 2.24
+ */
+const char *
+g_desktop_app_info_get_filename (GDesktopAppInfo *info)
+{
+ return info->filename;
+}
+
+static const char *
+g_desktop_app_info_get_description (GAppInfo *appinfo)
+{
+ GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+
+ return info->comment;
+}
+
+static const char *
+g_desktop_app_info_get_executable (GAppInfo *appinfo)
+{
+ GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+
+ return info->binary;
+}
+
+static const char *
+g_desktop_app_info_get_commandline (GAppInfo *appinfo)
+{
+ GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+
+ return info->exec;
+}
+
+static GIcon *
+g_desktop_app_info_get_icon (GAppInfo *appinfo)
+{
+ GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+
+ return info->icon;
+}
+
+/**
+ * g_desktop_app_info_get_categories:
+ * @info: a #GDesktopAppInfo
+ *
+ * Gets the categories from the desktop file.
+ *
+ * Returns: The unparsed Categories key from the desktop file;
+ * i.e. no attempt is made to split it by ';' or validate it.
+ */
+const char *
+g_desktop_app_info_get_categories (GDesktopAppInfo *info)
+{
+ return info->categories;
+}
+
+/**
+ * g_desktop_app_info_get_keywords:
+ * @info: a #GDesktopAppInfo
+ *
+ * Gets the keywords from the desktop file.
+ *
+ * Returns: (transfer none): The value of the Keywords key
+ *
+ * Since: 2.32
+ */
+const char * const *
+g_desktop_app_info_get_keywords (GDesktopAppInfo *info)
+{
+ return (const char * const *)info->keywords;
+}
+
+/**
+ * g_desktop_app_info_get_generic_name:
+ * @info: a #GDesktopAppInfo
+ *
+ * Gets the generic name from the destkop file.
+ *
+ * Returns: The value of the GenericName key
+ */
+const char *
+g_desktop_app_info_get_generic_name (GDesktopAppInfo *info)
+{
+ return info->generic_name;
+}
+
+/**
+ * g_desktop_app_info_get_nodisplay:
+ * @info: a #GDesktopAppInfo
+ *
+ * Gets the value of the NoDisplay key, which helps determine if the
+ * application info should be shown in menus. See
+ * #G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY and g_app_info_should_show().
+ *
+ * Returns: The value of the NoDisplay key
+ *
+ * Since: 2.30
+ */
+gboolean
+g_desktop_app_info_get_nodisplay (GDesktopAppInfo *info)
+{
+ return info->nodisplay;
+}
+
+/**
+ * g_desktop_app_info_get_show_in:
+ * @info: a #GDesktopAppInfo
+ * @desktop_env: (nullable): a string specifying a desktop name
+ *
+ * Checks if the application info should be shown in menus that list available
+ * applications for a specific name of the desktop, based on the
+ * `OnlyShowIn` and `NotShowIn` keys.
+ *
+ * @desktop_env should typically be given as %NULL, in which case the
+ * `XDG_CURRENT_DESKTOP` environment variable is consulted. If you want
+ * to override the default mechanism then you may specify @desktop_env,
+ * but this is not recommended.
+ *
+ * Note that g_app_info_should_show() for @info will include this check (with
+ * %NULL for @desktop_env) as well as additional checks.
+ *
+ * Returns: %TRUE if the @info should be shown in @desktop_env according to the
+ * `OnlyShowIn` and `NotShowIn` keys, %FALSE
+ * otherwise.
+ *
+ * Since: 2.30
+ */
+gboolean
+g_desktop_app_info_get_show_in (GDesktopAppInfo *info,
+ const gchar *desktop_env)
+{
+ const gchar *specified_envs[] = { desktop_env, NULL };
+ const gchar * const *envs;
+ gint i;
+
+ g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), FALSE);
+
+ if (desktop_env)
+ envs = specified_envs;
+ else
+ envs = get_current_desktops (NULL);
+
+ for (i = 0; envs[i]; i++)
+ {
+ gint j;
+
+ if (info->only_show_in)
+ for (j = 0; info->only_show_in[j]; j++)
+ if (g_str_equal (info->only_show_in[j], envs[i]))
+ return TRUE;
+
+ if (info->not_show_in)
+ for (j = 0; info->not_show_in[j]; j++)
+ if (g_str_equal (info->not_show_in[j], envs[i]))
+ return FALSE;
+ }
+
+ return info->only_show_in == NULL;
+}
+
+/* Launching... {{{2 */
+
+static char *
+expand_macro_single (char macro, char *uri)
+{
+ GFile *file;
+ char *result = NULL;
+ char *path = NULL;
+ char *name;
+
+ file = g_file_new_for_uri (uri);
+
+ switch (macro)
+ {
+ case 'u':
+ case 'U':
+ result = g_shell_quote (uri);
+ break;
+ case 'f':
+ case 'F':
+ path = g_file_get_path (file);
+ if (path)
+ result = g_shell_quote (path);
+ break;
+ case 'd':
+ case 'D':
+ path = g_file_get_path (file);
+ if (path)
+ {
+ name = g_path_get_dirname (path);
+ result = g_shell_quote (name);
+ g_free (name);
+ }
+ break;
+ case 'n':
+ case 'N':
+ path = g_file_get_path (file);
+ if (path)
+ {
+ name = g_path_get_basename (path);
+ result = g_shell_quote (name);
+ g_free (name);
+ }
+ break;
+ }
+
+ g_object_unref (file);
+ g_free (path);
+
+ return result;
+}
+
+static void
+expand_macro (char macro,
+ GString *exec,
+ GDesktopAppInfo *info,
+ GList **uri_list)
+{
+ GList *uris = *uri_list;
+ char *expanded;
+ gboolean force_file_uri;
+ char force_file_uri_macro;
+ char *uri;
+
+ g_return_if_fail (exec != NULL);
+
+ /* On %u and %U, pass POSIX file path pointing to the URI via
+ * the FUSE mount in ~/.gvfs. Note that if the FUSE daemon isn't
+ * running or the URI doesn't have a POSIX file path via FUSE
+ * we'll just pass the URI.
+ */
+ force_file_uri_macro = macro;
+ force_file_uri = FALSE;
+ if (!info->no_fuse)
+ {
+ switch (macro)
+ {
+ case 'u':
+ force_file_uri_macro = 'f';
+ force_file_uri = TRUE;
+ break;
+ case 'U':
+ force_file_uri_macro = 'F';
+ force_file_uri = TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ switch (macro)
+ {
+ case 'u':
+ case 'f':
+ case 'd':
+ case 'n':
+ if (uris)
+ {
+ uri = uris->data;
+ if (!force_file_uri ||
+ /* Pass URI if it contains an anchor */
+ strchr (uri, '#') != NULL)
+ {
+ expanded = expand_macro_single (macro, uri);
+ }
+ else
+ {
+ expanded = expand_macro_single (force_file_uri_macro, uri);
+ if (expanded == NULL)
+ expanded = expand_macro_single (macro, uri);
+ }
+
+ if (expanded)
+ {
+ g_string_append (exec, expanded);
+ g_free (expanded);
+ }
+ uris = uris->next;
+ }
+
+ break;
+
+ case 'U':
+ case 'F':
+ case 'D':
+ case 'N':
+ while (uris)
+ {
+ uri = uris->data;
+
+ if (!force_file_uri ||
+ /* Pass URI if it contains an anchor */
+ strchr (uri, '#') != NULL)
+ {
+ expanded = expand_macro_single (macro, uri);
+ }
+ else
+ {
+ expanded = expand_macro_single (force_file_uri_macro, uri);
+ if (expanded == NULL)
+ expanded = expand_macro_single (macro, uri);
+ }
+
+ if (expanded)
+ {
+ g_string_append (exec, expanded);
+ g_free (expanded);
+ }
+
+ uris = uris->next;
+
+ if (uris != NULL && expanded)
+ g_string_append_c (exec, ' ');
+ }
+
+ break;
+
+ case 'i':
+ if (info->icon_name)
+ {
+ g_string_append (exec, "--icon ");
+ expanded = g_shell_quote (info->icon_name);
+ g_string_append (exec, expanded);
+ g_free (expanded);
+ }
+ break;
+
+ case 'c':
+ if (info->name)
+ {
+ expanded = g_shell_quote (info->name);
+ g_string_append (exec, expanded);
+ g_free (expanded);
+ }
+ break;
+
+ case 'k':
+ if (info->filename)
+ {
+ expanded = g_shell_quote (info->filename);
+ g_string_append (exec, expanded);
+ g_free (expanded);
+ }
+ break;
+
+ case 'm': /* deprecated */
+ break;
+
+ case '%':
+ g_string_append_c (exec, '%');
+ break;
+ }
+
+ *uri_list = uris;
+}
+
+static gboolean
+expand_application_parameters (GDesktopAppInfo *info,
+ const gchar *exec_line,
+ GList **uris,
+ int *argc,
+ char ***argv,
+ GError **error)
+{
+ GList *uri_list = *uris;
+ const char *p = exec_line;
+ GString *expanded_exec;
+ gboolean res;
+
+ if (exec_line == NULL)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Desktop file didn't specify Exec field"));
+ return FALSE;
+ }
+
+ expanded_exec = g_string_new (NULL);
+
+ while (*p)
+ {
+ if (p[0] == '%' && p[1] != '\0')
+ {
+ expand_macro (p[1], expanded_exec, info, uris);
+ p++;
+ }
+ else
+ g_string_append_c (expanded_exec, *p);
+
+ p++;
+ }
+
+ /* No file substitutions */
+ if (uri_list == *uris && uri_list != NULL)
+ {
+ /* If there is no macro default to %f. This is also what KDE does */
+ g_string_append_c (expanded_exec, ' ');
+ expand_macro ('f', expanded_exec, info, uris);
+ }
+
+ res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
+ g_string_free (expanded_exec, TRUE);
+ return res;
+}
+
+static gboolean
+prepend_terminal_to_vector (int *argc,
+ char ***argv)
+{
+#ifndef G_OS_WIN32
+ char **real_argv;
+ int real_argc;
+ int i, j;
+ char **term_argv = NULL;
+ int term_argc = 0;
+ char *check;
+ char **the_argv;
+
+ g_return_val_if_fail (argc != NULL, FALSE);
+ g_return_val_if_fail (argv != NULL, FALSE);
+
+ /* sanity */
+ if(*argv == NULL)
+ *argc = 0;
+
+ the_argv = *argv;
+
+ /* compute size if not given */
+ if (*argc < 0)
+ {
+ for (i = 0; the_argv[i] != NULL; i++)
+ ;
+ *argc = i;
+ }
+
+ term_argc = 2;
+ term_argv = g_new0 (char *, 3);
+
+ check = g_find_program_in_path ("gnome-terminal");
+ if (check != NULL)
+ {
+ term_argv[0] = check;
+ /* Note that gnome-terminal takes -x and
+ * as -e in gnome-terminal is broken we use that. */
+ term_argv[1] = g_strdup ("-x");
+ }
+ else
+ {
+ if (check == NULL)
+ check = g_find_program_in_path ("nxterm");
+ if (check == NULL)
+ check = g_find_program_in_path ("color-xterm");
+ if (check == NULL)
+ check = g_find_program_in_path ("rxvt");
+ if (check == NULL)
+ check = g_find_program_in_path ("xterm");
+ if (check == NULL)
+ check = g_find_program_in_path ("dtterm");
+ if (check == NULL)
+ {
+ check = g_strdup ("xterm");
+ g_warning ("couldn't find a terminal, falling back to xterm");
+ }
+ term_argv[0] = check;
+ term_argv[1] = g_strdup ("-e");
+ }
+
+ real_argc = term_argc + *argc;
+ real_argv = g_new (char *, real_argc + 1);
+
+ for (i = 0; i < term_argc; i++)
+ real_argv[i] = term_argv[i];
+
+ for (j = 0; j < *argc; j++, i++)
+ real_argv[i] = (char *)the_argv[j];
+
+ real_argv[i] = NULL;
+
+ g_free (*argv);
+ *argv = real_argv;
+ *argc = real_argc;
+
+ /* we use g_free here as we sucked all the inner strings
+ * out from it into real_argv */
+ g_free (term_argv);
+ return TRUE;
+#else
+ return FALSE;
+#endif /* G_OS_WIN32 */
+}
+
+static GList *
+create_files_for_uris (GList *uris)
+{
+ GList *res;
+ GList *iter;
+
+ res = NULL;
+
+ for (iter = uris; iter; iter = iter->next)
+ {
+ GFile *file = g_file_new_for_uri ((char *)iter->data);
+ res = g_list_prepend (res, file);
+ }
+
+ return g_list_reverse (res);
+}
+
+typedef struct
+{
+ GSpawnChildSetupFunc user_setup;
+ gpointer user_setup_data;
+
+ char *pid_envvar;
+} ChildSetupData;
+
+static void
+child_setup (gpointer user_data)
+{
+ ChildSetupData *data = user_data;
+
+ if (data->pid_envvar)
+ {
+ pid_t pid = getpid ();
+ char buf[20];
+ int i;
+
+ /* Write the pid into the space already reserved for it in the
+ * environment array. We can't use sprintf because it might
+ * malloc, so we do it by hand. It's simplest to write the pid
+ * out backwards first, then copy it over.