| /* | |
| * 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 (&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); | |
| } |