/* * 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 . * * Author: Ryan Lortie */ #include "config.h" #include #include #include #include #include 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 (&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 (¶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 (&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); }