mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-11-04 01:58:54 +01:00 
			
		
		
		
	All uses of g_variant_builder_init() in gio are safe to translate to the new g_variant_builder_init_static() alternative as the type will outlive the call to g_variant_builder_end() (or is already static in nature).
		
			
				
	
	
		
			477 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			477 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright © 2013 Canonical Limited
 | 
						|
 *
 | 
						|
 * SPDX-License-Identifier: LGPL-2.1-or-later
 | 
						|
 *
 | 
						|
 * This library is free software; you can redistribute it and/or
 | 
						|
 * modify it under the terms of the GNU Lesser General Public
 | 
						|
 * License as published by the Free Software Foundation; either
 | 
						|
 * version 2.1 of the License, or (at your option) any later version.
 | 
						|
 *
 | 
						|
 * This library is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
 * Lesser General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU Lesser General Public
 | 
						|
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 | 
						|
 *
 | 
						|
 * Author: Ryan Lortie <desrt@desrt.ca>
 | 
						|
 */
 | 
						|
 | 
						|
#include "config.h"
 | 
						|
 | 
						|
#include <gio/gdesktopappinfo.h>
 | 
						|
 | 
						|
#include <glib/gi18n.h>
 | 
						|
#include <gio/gio.h>
 | 
						|
 | 
						|
#include <string.h>
 | 
						|
#include <locale.h>
 | 
						|
 | 
						|
struct help_topic
 | 
						|
{
 | 
						|
  const gchar *command;
 | 
						|
  const gchar *summary;
 | 
						|
  const gchar *description;
 | 
						|
  const gchar *synopsis;
 | 
						|
};
 | 
						|
 | 
						|
struct help_substvar
 | 
						|
{
 | 
						|
  const gchar *var;
 | 
						|
  const gchar *description;
 | 
						|
};
 | 
						|
 | 
						|
static const struct help_topic topics[] = {
 | 
						|
  { "help",         N_("Print help"),
 | 
						|
                    N_("Print help"),
 | 
						|
                    N_("[COMMAND]")
 | 
						|
  },
 | 
						|
  { "version",      N_("Print version"),
 | 
						|
                    N_("Print version information and exit"),
 | 
						|
                    NULL
 | 
						|
  },
 | 
						|
  { "list-apps",    N_("List applications"),
 | 
						|
                    N_("List the installed D-Bus activatable applications (by .desktop files)"),
 | 
						|
                    NULL
 | 
						|
  },
 | 
						|
  { "launch",       N_("Launch an application"),
 | 
						|
                    N_("Launch the application (with optional files to open)"),
 | 
						|
                    N_("APPID [FILE…]")
 | 
						|
  },
 | 
						|
  { "action",       N_("Activate an action"),
 | 
						|
                    N_("Invoke an action on the application"),
 | 
						|
                    N_("APPID ACTION [PARAMETER]")
 | 
						|
  },
 | 
						|
  { "list-actions", N_("List available actions"),
 | 
						|
                    N_("List static actions for an application (from .desktop file)"),
 | 
						|
                    N_("APPID")
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
static const struct help_substvar substvars[] = {
 | 
						|
  { N_("COMMAND"),   N_("The command to print detailed help for")                             },
 | 
						|
  { N_("APPID"),     N_("Application identifier in D-Bus format (eg: org.example.viewer)")    },
 | 
						|
  { N_("FILE"),      N_("Optional relative or absolute filenames, or URIs to open")           },
 | 
						|
  { N_("ACTION"),    N_("The action name to invoke")                                          },
 | 
						|
  { N_("PARAMETER"), N_("Optional parameter to the action invocation, in GVariant format")    }
 | 
						|
};
 | 
						|
 | 
						|
static int
 | 
						|
app_help (gboolean     requested,
 | 
						|
          const gchar *command)
 | 
						|
{
 | 
						|
  const struct help_topic *topic = NULL;
 | 
						|
  GString *string;
 | 
						|
 | 
						|
  string = g_string_new (NULL);
 | 
						|
 | 
						|
  if (command)
 | 
						|
    {
 | 
						|
      gsize i;
 | 
						|
 | 
						|
      for (i = 0; i < G_N_ELEMENTS (topics); i++)
 | 
						|
        if (g_str_equal (topics[i].command, command))
 | 
						|
          topic = &topics[i];
 | 
						|
 | 
						|
      if (!topic)
 | 
						|
        {
 | 
						|
          g_string_printf (string, _("Unknown command %s\n\n"), command);
 | 
						|
          requested = FALSE;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
  g_string_append (string, _("Usage:\n"));
 | 
						|
 | 
						|
  if (topic)
 | 
						|
    {
 | 
						|
      guint maxwidth;
 | 
						|
      gsize i;
 | 
						|
 | 
						|
      g_string_append_printf (string, "\n  %s %s %s\n\n", "gapplication",
 | 
						|
                              topic->command, topic->synopsis ? _(topic->synopsis) : "");
 | 
						|
      g_string_append_printf (string, "%s\n\n", _(topic->description));
 | 
						|
 | 
						|
      if (topic->synopsis)
 | 
						|
        {
 | 
						|
          g_string_append (string, _("Arguments:\n"));
 | 
						|
 | 
						|
          maxwidth = 0;
 | 
						|
          for (i = 0; i < G_N_ELEMENTS (substvars); i++)
 | 
						|
            if (strstr (topic->synopsis, substvars[i].var))
 | 
						|
              maxwidth = MAX(maxwidth, strlen (_(substvars[i].var)));
 | 
						|
 | 
						|
          for (i = 0; i < G_N_ELEMENTS (substvars); i++)
 | 
						|
            if (strstr (topic->synopsis, substvars[i].var))
 | 
						|
              g_string_append_printf (string, "  %-*.*s   %s\n", maxwidth, maxwidth,
 | 
						|
                                      _(substvars[i].var), _(substvars[i].description));
 | 
						|
          g_string_append (string, "\n");
 | 
						|
        }
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      guint maxwidth;
 | 
						|
      gsize i;
 | 
						|
 | 
						|
      g_string_append_printf (string, "\n  %s %s %s\n\n", "gapplication", _("COMMAND"), _("[ARGS…]"));
 | 
						|
      g_string_append_printf (string, _("Commands:\n"));
 | 
						|
 | 
						|
      maxwidth = 0;
 | 
						|
      for (i = 0; i < G_N_ELEMENTS (topics); i++)
 | 
						|
        maxwidth = MAX(maxwidth, strlen (topics[i].command));
 | 
						|
 | 
						|
      for (i = 0; i < G_N_ELEMENTS (topics); i++)
 | 
						|
        g_string_append_printf (string, "  %-*.*s   %s\n", maxwidth, maxwidth,
 | 
						|
                                topics[i].command, _(topics[i].summary));
 | 
						|
 | 
						|
      g_string_append (string, "\n");
 | 
						|
      /* Translators: do not translate 'help', but please translate 'COMMAND'. */
 | 
						|
      g_string_append_printf (string, _("Use “%s help COMMAND” to get detailed help.\n\n"), "gapplication");
 | 
						|
    }
 | 
						|
 | 
						|
  if (requested)
 | 
						|
    g_print ("%s", string->str);
 | 
						|
  else
 | 
						|
    g_printerr ("%s\n", string->str);
 | 
						|
 | 
						|
  g_string_free (string, TRUE);
 | 
						|
 | 
						|
  return requested ? 0 : 1;
 | 
						|
}
 | 
						|
 | 
						|
static gboolean
 | 
						|
app_check_name (gchar       **args,
 | 
						|
                const gchar  *command)
 | 
						|
{
 | 
						|
  if (args[0] == NULL)
 | 
						|
    {
 | 
						|
      g_printerr (_("%s command requires an application id to directly follow\n\n"), command);
 | 
						|
      return FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
  if (!g_dbus_is_name (args[0]))
 | 
						|
    {
 | 
						|
      g_printerr (_("invalid application id: “%s”\n"), args[0]);
 | 
						|
      return FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
app_no_args (const gchar *command)
 | 
						|
{
 | 
						|
  /* Translators: %s is replaced with a command name like 'list-actions' */
 | 
						|
  g_printerr (_("“%s” takes no arguments\n\n"), command);
 | 
						|
  return app_help (FALSE, command);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
app_version (gchar **args)
 | 
						|
{
 | 
						|
  if (g_strv_length (args))
 | 
						|
    return app_no_args ("version");
 | 
						|
 | 
						|
  g_print (PACKAGE_VERSION "\n");
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
app_list (gchar **args)
 | 
						|
{
 | 
						|
  GList *apps;
 | 
						|
 | 
						|
  if (g_strv_length (args))
 | 
						|
    return app_no_args ("list");
 | 
						|
 | 
						|
  apps = g_app_info_get_all ();
 | 
						|
 | 
						|
  while (apps)
 | 
						|
    {
 | 
						|
      GDesktopAppInfo *info = apps->data;
 | 
						|
 | 
						|
      if (G_IS_DESKTOP_APP_INFO (info))
 | 
						|
        if (g_desktop_app_info_get_boolean (info, "DBusActivatable"))
 | 
						|
          {
 | 
						|
            const gchar *filename;
 | 
						|
 | 
						|
            filename = g_app_info_get_id (G_APP_INFO (info));
 | 
						|
            if (g_str_has_suffix (filename, ".desktop"))
 | 
						|
              {
 | 
						|
                gchar *id;
 | 
						|
 | 
						|
                id = g_strndup (filename, strlen (filename) - 8);
 | 
						|
                g_print ("%s\n", id);
 | 
						|
                g_free (id);
 | 
						|
              }
 | 
						|
          }
 | 
						|
 | 
						|
      apps = g_list_delete_link (apps, apps);
 | 
						|
      g_object_unref (info);
 | 
						|
    }
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
static gchar *
 | 
						|
app_path_for_id (const gchar *app_id)
 | 
						|
{
 | 
						|
  gchar *path;
 | 
						|
  gint i;
 | 
						|
 | 
						|
  path = g_strconcat ("/", app_id, NULL);
 | 
						|
  for (i = 0; path[i]; i++)
 | 
						|
    {
 | 
						|
      if (path[i] == '.')
 | 
						|
        path[i] = '/';
 | 
						|
      if (path[i] == '-')
 | 
						|
        path[i] = '_';
 | 
						|
    }
 | 
						|
 | 
						|
  return path;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
app_call (const gchar *app_id,
 | 
						|
          const gchar *method_name,
 | 
						|
          GVariant    *parameters)
 | 
						|
{
 | 
						|
  GDBusConnection *session;
 | 
						|
  GError *error = NULL;
 | 
						|
  gchar *object_path;
 | 
						|
  GVariant *result;
 | 
						|
 | 
						|
 | 
						|
  session = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
 | 
						|
  if (!session)
 | 
						|
    {
 | 
						|
      g_variant_unref (g_variant_ref_sink (parameters));
 | 
						|
      g_printerr (_("unable to connect to D-Bus: %s\n"), error->message);
 | 
						|
      g_error_free (error);
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
 | 
						|
  object_path = app_path_for_id (app_id);
 | 
						|
 | 
						|
  result = g_dbus_connection_call_sync (session, app_id, object_path, "org.freedesktop.Application",
 | 
						|
                                        method_name, parameters, G_VARIANT_TYPE_UNIT,
 | 
						|
                                        G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
 | 
						|
 | 
						|
  g_free (object_path);
 | 
						|
 | 
						|
  if (result)
 | 
						|
    {
 | 
						|
      g_variant_unref (result);
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      g_printerr (_("error sending %s message to application: %s\n"), method_name, error->message);
 | 
						|
      g_error_free (error);
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static GVariant *
 | 
						|
app_get_platform_data (void)
 | 
						|
{
 | 
						|
  GVariantBuilder builder;
 | 
						|
  const gchar *startup_id;
 | 
						|
 | 
						|
  g_variant_builder_init_static (&builder, G_VARIANT_TYPE_VARDICT);
 | 
						|
 | 
						|
  if ((startup_id = g_getenv ("DESKTOP_STARTUP_ID")))
 | 
						|
    g_variant_builder_add (&builder, "{sv}", "desktop-startup-id", g_variant_new_string (startup_id));
 | 
						|
 | 
						|
  if ((startup_id = g_getenv ("XDG_ACTIVATION_TOKEN")))
 | 
						|
    g_variant_builder_add (&builder, "{sv}", "activation-token", g_variant_new_string (startup_id));
 | 
						|
 | 
						|
  return g_variant_builder_end (&builder);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
app_action (gchar **args)
 | 
						|
{
 | 
						|
  GVariantBuilder params;
 | 
						|
  const gchar *name;
 | 
						|
 | 
						|
  if (!app_check_name (args, "action"))
 | 
						|
    return 1;
 | 
						|
 | 
						|
  if (args[1] == NULL)
 | 
						|
    {
 | 
						|
      g_printerr (_("action name must be given after application id\n"));
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
 | 
						|
  name = args[1];
 | 
						|
 | 
						|
  if (!g_action_name_is_valid (name))
 | 
						|
    {
 | 
						|
      g_printerr (_("invalid action name: “%s”\n"
 | 
						|
                    "action names must consist of only alphanumerics, “-” and “.”\n"), name);
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
 | 
						|
  g_variant_builder_init_static (¶ms, G_VARIANT_TYPE ("av"));
 | 
						|
 | 
						|
  if (args[2])
 | 
						|
    {
 | 
						|
      GError *error = NULL;
 | 
						|
      GVariant *parameter;
 | 
						|
 | 
						|
      parameter = g_variant_parse (NULL, args[2], NULL, NULL, &error);
 | 
						|
 | 
						|
      if (!parameter)
 | 
						|
        {
 | 
						|
          gchar *context;
 | 
						|
 | 
						|
          context = g_variant_parse_error_print_context (error, args[2]);
 | 
						|
          g_printerr (_("error parsing action parameter: %s\n"), context);
 | 
						|
          g_variant_builder_clear (¶ms);
 | 
						|
          g_error_free (error);
 | 
						|
          g_free (context);
 | 
						|
          return 1;
 | 
						|
        }
 | 
						|
 | 
						|
      g_variant_builder_add (¶ms, "v", parameter);
 | 
						|
      g_variant_unref (parameter);
 | 
						|
 | 
						|
      if (args[3])
 | 
						|
        {
 | 
						|
          g_printerr (_("actions accept a maximum of one parameter\n"));
 | 
						|
          g_variant_builder_clear (¶ms);
 | 
						|
          return 1;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
  return app_call (args[0], "ActivateAction", g_variant_new ("(sav@a{sv})", name, ¶ms, app_get_platform_data ()));
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
app_activate (const gchar *app_id)
 | 
						|
{
 | 
						|
  return app_call (app_id, "Activate", g_variant_new ("(@a{sv})", app_get_platform_data ()));
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
app_launch (gchar **args)
 | 
						|
{
 | 
						|
  GVariantBuilder files;
 | 
						|
  gint i;
 | 
						|
 | 
						|
  if (!app_check_name (args, "launch"))
 | 
						|
    return 1;
 | 
						|
 | 
						|
  if (args[1] == NULL)
 | 
						|
    return app_activate (args[0]);
 | 
						|
 | 
						|
  g_variant_builder_init_static (&files, G_VARIANT_TYPE_STRING_ARRAY);
 | 
						|
 | 
						|
  for (i = 1; args[i]; i++)
 | 
						|
    {
 | 
						|
      GFile *file;
 | 
						|
 | 
						|
      /* "This operation never fails" */
 | 
						|
      file = g_file_new_for_commandline_arg (args[i]);
 | 
						|
      g_variant_builder_add_value (&files, g_variant_new_take_string (g_file_get_uri (file)));
 | 
						|
      g_object_unref (file);
 | 
						|
    }
 | 
						|
 | 
						|
  return app_call (args[0], "Open", g_variant_new ("(as@a{sv})", &files, app_get_platform_data ()));
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
app_list_actions (gchar **args)
 | 
						|
{
 | 
						|
  const gchar * const *actions;
 | 
						|
  GDesktopAppInfo *app_info;
 | 
						|
  gchar *filename;
 | 
						|
  gint i;
 | 
						|
 | 
						|
  if (!app_check_name (args, "list-actions"))
 | 
						|
    return 1;
 | 
						|
 | 
						|
  if (args[1])
 | 
						|
    {
 | 
						|
      g_printerr (_("list-actions command takes only the application id"));
 | 
						|
      app_help (FALSE, "list-actions");
 | 
						|
    }
 | 
						|
 | 
						|
  filename = g_strconcat (args[0], ".desktop", NULL);
 | 
						|
  app_info = g_desktop_app_info_new (filename);
 | 
						|
  g_free (filename);
 | 
						|
 | 
						|
  if (app_info == NULL)
 | 
						|
    {
 | 
						|
      g_printerr (_("unable to find desktop file for application %s\n"), args[0]);
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
 | 
						|
  actions = g_desktop_app_info_list_actions (app_info);
 | 
						|
 | 
						|
  for (i = 0; actions[i]; i++)
 | 
						|
    g_print ("%s\n", actions[i]);
 | 
						|
 | 
						|
  g_object_unref (app_info);
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
main (int argc, char **argv)
 | 
						|
{
 | 
						|
  setlocale (LC_ALL, "");
 | 
						|
  textdomain (GETTEXT_PACKAGE);
 | 
						|
  bindtextdomain (GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
 | 
						|
#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
 | 
						|
  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
 | 
						|
#endif
 | 
						|
 | 
						|
  if (argc < 2)
 | 
						|
    return app_help (TRUE, NULL);
 | 
						|
 | 
						|
  if (g_str_equal (argv[1], "help"))
 | 
						|
    return app_help (TRUE, argv[2]);
 | 
						|
 | 
						|
  if (g_str_equal (argv[1], "version"))
 | 
						|
    return app_version (argv + 2);
 | 
						|
 | 
						|
  if (g_str_equal (argv[1], "list-apps"))
 | 
						|
    return app_list (argv + 2);
 | 
						|
 | 
						|
  if (g_str_equal (argv[1], "launch"))
 | 
						|
    return app_launch (argv + 2);
 | 
						|
 | 
						|
  if (g_str_equal (argv[1], "action"))
 | 
						|
    return app_action (argv + 2);
 | 
						|
 | 
						|
  if (g_str_equal (argv[1], "list-actions"))
 | 
						|
    return app_list_actions (argv + 2);
 | 
						|
 | 
						|
  g_printerr (_("unrecognized command: %s\n\n"), argv[1]);
 | 
						|
 | 
						|
  return app_help (FALSE, NULL);
 | 
						|
}
 |