+/* Command line option handling {{{1 */
+
+static void
+free_option_entry (gpointer data)
+{
+ GOptionEntry *entry = data;
+
+ switch (entry->arg)
+ {
+ case G_OPTION_ARG_STRING:
+ case G_OPTION_ARG_FILENAME:
+ g_free (*(gchar **) entry->arg_data);
+ break;
+
+ case G_OPTION_ARG_STRING_ARRAY:
+ case G_OPTION_ARG_FILENAME_ARRAY:
+ g_strfreev (*(gchar ***) entry->arg_data);
+ break;
+
+ default:
+ /* most things require no free... */
+ break;
+ }
+
+ /* ...except for the space that we allocated for it ourselves */
+ g_free (entry->arg_data);
+
+ g_slice_free (GOptionEntry, entry);
+}
+
+static void
+g_application_pack_option_entries (GApplication *application,
+ GVariantDict *dict)
+{
+ GHashTableIter iter;
+ gpointer item;
+
+ g_hash_table_iter_init (&iter, application->priv->packed_options);
+ while (g_hash_table_iter_next (&iter, NULL, &item))
+ {
+ GOptionEntry *entry = item;
+ GVariant *value = NULL;
+
+ switch (entry->arg)
+ {
+ case G_OPTION_ARG_NONE:
+ if (*(gboolean *) entry->arg_data != 2)
+ value = g_variant_new_boolean (*(gboolean *) entry->arg_data);
+ break;
+
+ case G_OPTION_ARG_STRING:
+ if (*(gchar **) entry->arg_data)
+ value = g_variant_new_string (*(gchar **) entry->arg_data);
+ break;
+
+ case G_OPTION_ARG_INT:
+ if (*(gint32 *) entry->arg_data)
+ value = g_variant_new_int32 (*(gint32 *) entry->arg_data);
+ break;
+
+ case G_OPTION_ARG_FILENAME:
+ if (*(gchar **) entry->arg_data)
+ value = g_variant_new_bytestring (*(gchar **) entry->arg_data);
+ break;
+
+ case G_OPTION_ARG_STRING_ARRAY:
+ if (*(gchar ***) entry->arg_data)
+ value = g_variant_new_strv (*(const gchar ***) entry->arg_data, -1);
+ break;
+
+ case G_OPTION_ARG_FILENAME_ARRAY:
+ if (*(gchar ***) entry->arg_data)
+ value = g_variant_new_bytestring_array (*(const gchar ***) entry->arg_data, -1);
+ break;
+
+ case G_OPTION_ARG_DOUBLE:
+ if (*(gdouble *) entry->arg_data)
+ value = g_variant_new_double (*(gdouble *) entry->arg_data);
+ break;
+
+ case G_OPTION_ARG_INT64:
+ if (*(gint64 *) entry->arg_data)
+ value = g_variant_new_int64 (*(gint64 *) entry->arg_data);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (value)
+ g_variant_dict_insert_value (dict, entry->long_name, value);
+ }
+}
+
+static GVariantDict *
+g_application_parse_command_line (GApplication *application,
+ gchar ***arguments,
+ GError **error)
+{
+ gboolean become_service = FALSE;
+ GVariantDict *dict = NULL;
+ GOptionContext *context;
+
+ /* Due to the memory management of GOptionGroup we can only parse
+ * options once. That's because once you add a group to the
+ * GOptionContext there is no way to get it back again. This is fine:
+ * local_command_line() should never get invoked more than once
+ * anyway. Add a sanity check just to be sure.
+ */
+ g_return_val_if_fail (!application->priv->options_parsed, NULL);
+
+ context = g_option_context_new (NULL);
+
+ /* Add the main option group, if it exists */
+ if (application->priv->main_options)
+ {
+ /* This consumes the main_options */
+ g_option_context_set_main_group (context, application->priv->main_options);
+ application->priv->main_options = NULL;
+ }
+
+ /* Add any other option groups if they exist. Adding them to the
+ * context will consume them, so we free the list as we go...
+ */
+ while (application->priv->option_groups)
+ {
+ g_option_context_add_group (context, application->priv->option_groups->data);
+ application->priv->option_groups = g_slist_delete_link (application->priv->option_groups,
+ application->priv->option_groups);
+ }
+
+ /* If the application has not registered local options and it has
+ * G_APPLICATION_HANDLES_COMMAND_LINE then we have to assume that
+ * their primary instance commandline handler may want to deal with
+ * the arguments. We must therefore ignore them.
+ */
+ if (application->priv->main_options == NULL && (application->priv->flags & G_APPLICATION_HANDLES_COMMAND_LINE))
+ g_option_context_set_ignore_unknown_options (context, TRUE);
+
+ /* In the case that we are not explicitly marked as a service or a
+ * launcher then we want to add the "--gapplication-service" option to
+ * allow the process to be made into a service.
+ */
+ if ((application->priv->flags & (G_APPLICATION_IS_SERVICE | G_APPLICATION_IS_LAUNCHER)) == 0)
+ {
+ GOptionGroup *option_group;
+ GOptionEntry entries[] = {
+ { "gapplication-service", '\0', 0, G_OPTION_ARG_NONE, &become_service,
+ N_("Enter GApplication service mode (use from D-Bus service files)") },
+ { NULL }
+ };
+
+ option_group = g_option_group_new ("gapplication",
+ _("GApplication options"), _("Show GApplication options"),
+ NULL, NULL);
+ g_option_group_set_translation_domain (option_group, GETTEXT_PACKAGE);
+ g_option_group_add_entries (option_group, entries);
+
+ g_option_context_add_group (context, option_group);
+ }
+
+ /* Now we parse... */
+ if (!g_option_context_parse_strv (context, arguments, error))
+ goto out;
+
+ /* Check for --gapplication-service */
+ if (become_service)
+ application->priv->flags |= G_APPLICATION_IS_SERVICE;
+
+ dict = g_variant_dict_new (NULL);
+ if (application->priv->packed_options)
+ {
+ g_application_pack_option_entries (application, dict);
+ g_hash_table_unref (application->priv->packed_options);
+ application->priv->packed_options = NULL;
+ }
+
+out:
+ /* Make sure we don't run again */
+ application->priv->options_parsed = TRUE;
+
+ g_option_context_free (context);
+
+ return dict;
+}
+
+static void
+add_packed_option (GApplication *application,
+ GOptionEntry *entry)
+{
+ switch (entry->arg)
+ {
+ case G_OPTION_ARG_NONE:
+ entry->arg_data = g_new (gboolean, 1);
+ *(gboolean *) entry->arg_data = 2;
+ break;
+
+ case G_OPTION_ARG_INT:
+ entry->arg_data = g_new0 (gint, 1);
+ break;
+
+ case G_OPTION_ARG_STRING:
+ case G_OPTION_ARG_FILENAME:
+ case G_OPTION_ARG_STRING_ARRAY:
+ case G_OPTION_ARG_FILENAME_ARRAY:
+ entry->arg_data = g_new0 (gpointer, 1);
+ break;
+
+ case G_OPTION_ARG_INT64:
+ entry->arg_data = g_new0 (gint64, 1);
+ break;
+
+ case G_OPTION_ARG_DOUBLE:
+ entry->arg_data = g_new0 (gdouble, 1);
+ break;
+
+ default:
+ g_return_if_reached ();
+ }
+
+ if (!application->priv->packed_options)
+ application->priv->packed_options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_option_entry);
+
+ g_hash_table_insert (application->priv->packed_options,
+ g_strdup (entry->long_name),
+ g_slice_dup (GOptionEntry, entry));
+}
+
+/**
+ * g_application_add_main_option_entries:
+ * @application: a #GApplication
+ * @entries: (array zero-terminated=1) (element-type GOptionEntry) a
+ * %NULL-terminated list of #GOptionEntrys
+ *
+ * Adds main option entries to be handled by @application.
+ *
+ * This function is comparable to g_option_context_add_main_entries().
+ *
+ * After the commandline arguments are parsed, the
+ * #GApplication::handle-local-options signal will be emitted. At this
+ * point, the application can inspect the values pointed to by @arg_data
+ * in the given #GOptionEntrys.
+ *
+ * Unlike #GOptionContext, #GApplication supports giving a %NULL
+ * @arg_data for a non-callback #GOptionEntry. This results in the
+ * argument in question being packed into a #GVariantDict which is also
+ * passed to #GApplication::handle-local-options, where it can be
+ * inspected and modified. If %G_APPLICATION_HANDLES_COMMAND_LINE is
+ * set, then the resulting dictionary is sent to the primary instance,
+ * where g_application_command_line_get_options_dict() will return it.
+ * This "packing" is done according to the type of the argument --
+ * booleans for normal flags, strings for strings, bytestrings for
+ * filenames, etc. The packing only occurs if the flag is given (ie: we
+ * do not pack a "false" #GVariant in the case that a flag is missing).
+ *
+ * In general, it is recommended that all commandline arguments are
+ * parsed locally. The options dictionary should then be used to
+ * transmit the result of the parsing to the primary instance, where
+ * g_variant_dict_lookup() can be used. For local options, it is
+ * possible to either use @arg_data in the usual way, or to consult (and
+ * potentially remove) the option from the options dictionary.
+ *
+ * This function is new in GLib 2.40. Before then, the only real choice
+ * was to send all of the commandline arguments (options and all) to the
+ * primary instance for handling. #GApplication ignored them completely
+ * on the local side. Calling this function "opts in" to the new
+ * behaviour, and in particular, means that unrecognised options will be
+ * treated as errors. Unrecognised options have never been ignored when
+ * %G_APPLICATION_HANDLES_COMMAND_LINE is unset.
+ *
+ * If #GApplication::handle-local-options needs to see the list of
+ * filenames, then the use of %G_OPTION_REMAINING is recommended. If
+ * @arg_data is %NULL then %G_OPTION_REMAINING can be used as a key into
+ * the options dictionary. If you do use %G_OPTION_REMAINING then you
+ * need to handle these arguments for yourself because once they are
+ * consumed, they will no longer be visible to the default handling
+ * (which treats them as filenames to be opened).
+ *
+ * Since: 2.40
+ */
+void
+g_application_add_main_option_entries (GApplication *application,
+ const GOptionEntry *entries)
+{
+ gint i;
+
+ g_return_if_fail (G_IS_APPLICATION (application));
+ g_return_if_fail (entries != NULL);
+
+ if (!application->priv->main_options)
+ application->priv->main_options = g_option_group_new (NULL, NULL, NULL, NULL, NULL);
+
+ for (i = 0; entries[i].long_name; i++)
+ {
+ GOptionEntry my_entries[2] = { { NULL }, { NULL } };
+ my_entries[0] = entries[i];
+
+ if (!my_entries[0].arg_data)
+ add_packed_option (application, &my_entries[0]);
+
+ g_option_group_add_entries (application->priv->main_options, my_entries);
+ }
+}
+
+/**
+ * g_application_add_main_option:
+ * @application: the #GApplication
+ * @long_name: the long name of an option used to specify it in a commandline
+ * @short_name: the short name of an option
+ * @flags: flags from #GOptionFlags
+ * @arg: the type of the option, as a #GOptionArg
+ * @description: the description for the option in `--help` output
+ * @arg_description: (nullable): the placeholder to use for the extra argument
+ * parsed by the option in `--help` output
+ *
+ * Add an option to be handled by @application.
+ *
+ * Calling this function is the equivalent of calling
+ * g_application_add_main_option_entries() with a single #GOptionEntry
+ * that has its arg_data member set to %NULL.
+ *
+ * The parsed arguments will be packed into a #GVariantDict which
+ * is passed to #GApplication::handle-local-options. If
+ * %G_APPLICATION_HANDLES_COMMAND_LINE is set, then it will also
+ * be sent to the primary instance. See
+ * g_application_add_main_option_entries() for more details.
+ *
+ * See #GOptionEntry for more documentation of the arguments.
+ *
+ * Since: 2.42
+ **/
+void
+g_application_add_main_option (GApplication *application,
+ const char *long_name,
+ char short_name,
+ GOptionFlags flags,
+ GOptionArg arg,
+ const char *description,
+ const char *arg_description)
+{
+ gchar *dup_string;
+ GOptionEntry my_entry[2] = {
+ { NULL, short_name, flags, arg, NULL, NULL, NULL },
+ { NULL }
+ };
+
+ g_return_if_fail (G_IS_APPLICATION (application));
+ g_return_if_fail (long_name != NULL);
+ g_return_if_fail (description != NULL);
+
+ my_entry[0].long_name = dup_string = g_strdup (long_name);
+ application->priv->option_strings = g_slist_prepend (application->priv->option_strings, dup_string);
+
+ my_entry[0].description = dup_string = g_strdup (description);
+ application->priv->option_strings = g_slist_prepend (application->priv->option_strings, dup_string);
+
+ my_entry[0].arg_description = dup_string = g_strdup (arg_description);
+ application->priv->option_strings = g_slist_prepend (application->priv->option_strings, dup_string);
+
+ g_application_add_main_option_entries (application, my_entry);
+}
+
+/**
+ * g_application_add_option_group:
+ * @application: the #GApplication
+ * @group: a #GOptionGroup
+ *
+ * Adds a #GOptionGroup to the commandline handling of @application.
+ *
+ * This function is comparable to g_option_context_add_group().
+ *
+ * Unlike g_application_add_main_option_entries(), this function does
+ * not deal with %NULL @arg_data and never transmits options to the
+ * primary instance.
+ *
+ * The reason for that is because, by the time the options arrive at the
+ * primary instance, it is typically too late to do anything with them.
+ * Taking the GTK option group as an example: GTK will already have been
+ * initialised by the time the #GApplication::command-line handler runs.
+ * In the case that this is not the first-running instance of the
+ * application, the existing instance may already have been running for
+ * a very long time.
+ *
+ * This means that the options from #GOptionGroup are only really usable
+ * in the case that the instance of the application being run is the
+ * first instance. Passing options like `--display=` or `--gdk-debug=`
+ * on future runs will have no effect on the existing primary instance.
+ *
+ * Calling this function will cause the options in the supplied option
+ * group to be parsed, but it does not cause you to be "opted in" to the
+ * new functionality whereby unrecognised options are rejected even if
+ * %G_APPLICATION_HANDLES_COMMAND_LINE was given.
+ *
+ * Since: 2.40
+ **/
+void
+g_application_add_option_group (GApplication *application,
+ GOptionGroup *group)
+{
+ g_return_if_fail (G_IS_APPLICATION (application));
+ g_return_if_fail (group != NULL);
+
+ application->priv->option_groups = g_slist_prepend (application->priv->option_groups, group);
+}
+