text: /*
   * Copyright © 2013 Canonical Limited
   *
   * 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 of the licence, 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")
    },
    { "list-apps",    N_("List applications"),
                      N_("List the installed D-Bus activatable applications (by .desktop files)")
    },
    { "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)
      {
        gint 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)
      {
        gint maxwidth;
        gint 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
      {
        gint maxwidth;
        gint 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));
  
    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 (&params, 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 (&params);
            g_error_free (error);
            g_free (context);
            return 1;
          }
  
        g_variant_builder_add (&params, "v", parameter);
        g_variant_unref (parameter);
  
        if (args[3])
          {
            g_printerr (_("actions accept a maximum of one parameter\n"));
            g_variant_builder_clear (&params);
            return 1;
          }
      }
  
    return app_call (args[0], "ActivateAction", g_variant_new ("(sav@a{sv})", name, &params, 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 (_("unrecognised command: %s\n\n"), argv[1]);
  
    return app_help (FALSE, NULL);
  }
  