summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Lortie <desrt@desrt.ca>2014-01-27 15:42:23 +0000
committerLars Uebernickel <lars.uebernickel@canonical.com>2014-10-15 23:37:45 +0200
commitae52ab3d1170a393b0b734e9b9b37c3f107a7c03 (patch)
tree51323e6babd77d7d640aaa72a70e60f7d2def7db
parentf2786908a8858ec9d063e8fae7e4b2d8d612b682 (diff)
downloadglib-ae52ab3d1170a393b0b734e9b9b37c3f107a7c03.tar.gz
GOption: add strict posix mode
Add a "posixly correct" mode to GOption to stop parsing arguments as soon as the first non-option argument is encountered. We determine the default value on the basis of duplicating the behaviour of the system getopt() implementation (which we directly check the behaviour of at runtime). On GNU systems this allows the user to modify our behaviour using POSIXLY_CORRECT. The user can change the value by g_option_context_set_strict_posix(), which might be useful for some usecases of GOptionContext (as mentioned in the doc string of this new function). https://bugzilla.gnome.org/show_bug.cgi?id=723160
-rw-r--r--glib/goption.c72
-rw-r--r--glib/goption.h6
-rw-r--r--glib/tests/option-context.c66
3 files changed, 144 insertions, 0 deletions
diff --git a/glib/goption.c b/glib/goption.c
index 643828120..019f54feb 100644
--- a/glib/goption.c
+++ b/glib/goption.c
@@ -183,6 +183,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
+#include <unistd.h>
#if defined __OpenBSD__
#include <unistd.h>
@@ -248,6 +249,7 @@ struct _GOptionContext
guint help_enabled : 1;
guint ignore_unknown : 1;
guint strv_mode : 1;
+ guint strict_posix : 1;
GOptionGroup *main_group;
@@ -358,6 +360,14 @@ g_option_context_new (const gchar *parameter_string)
context = g_new0 (GOptionContext, 1);
context->parameter_string = g_strdup (parameter_string);
+ {
+ const char *argv[] = { "./a", "a", "-a", NULL };
+ /* Check to see if getopt will parse the "-a" or not. If it finds
+ * no arguments then we are in strict POSIX mode.
+ */
+ optind = 1;
+ context->strict_posix = getopt (3, (char **) argv, "a") != 'a';
+ }
context->help_enabled = TRUE;
context->ignore_unknown = FALSE;
@@ -484,6 +494,65 @@ g_option_context_get_ignore_unknown_options (GOptionContext *context)
}
/**
+ * g_option_context_set_strict_posix:
+ * @context: a #GoptionContext
+ *
+ * Sets strict POSIX mode.
+ *
+ * In strict POSIX mode, the first non-argument parameter encountered
+ * (eg: filename) terminates argument processing. Remaining arguments
+ * are treated as non-options and are not attempted to be parsed.
+ *
+ * If strict POSIX mode is disabled then parsing is done in the GNU way
+ * where option arguments can be freely mixed with non-options.
+ *
+ * As an example, consider "ls foo -l". With GNU style parsing, this
+ * will list "foo" in long mode. In strict POSIX style, this will list
+ * the files named "foo" and "-l".
+ *
+ * The default is system-dependent. In particular, on some systems, it
+ * may be modified by the POSIXLY_CORRECT environment variable.
+ *
+ * It may be useful to force strict POSIX mode when creating "verb
+ * style" command line tools. For example, the "gsettings" command line
+ * tool supports the global option "--schemadir" as well as many
+ * subcommands ("get", "set", etc.) which each have their own set of
+ * arguments. Using strict POSIX mode will allow parsing the global
+ * options up to the verb name while leaving the remaining options to be
+ * parsed by the relevant subcommand (which can be determined by
+ * examining the verb name, which should be present in argv[1] after
+ * parsing).
+ *
+ * Since: 2.44
+ **/
+void
+g_option_context_set_strict_posix (GOptionContext *context,
+ gboolean strict_posix)
+{
+ g_return_if_fail (context != NULL);
+
+ context->strict_posix = strict_posix;
+}
+
+/**
+ * g_option_context_get_strict_posix:
+ * @context: a #GoptionContext
+ *
+ * Returns whether strict POSIX code is enabled.
+ *
+ * See g_option_context_set_strict_posix() for more information.
+ *
+ * Since: 2.44
+ **/
+gboolean
+g_option_context_get_strict_posix (GOptionContext *context)
+{
+ g_return_val_if_fail (context != NULL, FALSE);
+
+ return context->strict_posix;
+}
+
+/**
* g_option_context_add_group:
* @context: a #GOptionContext
* @group: the group to add
@@ -2060,6 +2129,9 @@ g_option_context_parse (GOptionContext *context,
}
else
{
+ if (context->strict_posix)
+ stop_parsing = TRUE;
+
/* Collect remaining args */
if (context->main_group &&
!parse_remaining_arg (context, context->main_group, &i,
diff --git a/glib/goption.h b/glib/goption.h
index ab12a2792..205a48413 100644
--- a/glib/goption.h
+++ b/glib/goption.h
@@ -310,6 +310,12 @@ void g_option_context_set_ignore_unknown_options (GOptionContext *context,
GLIB_AVAILABLE_IN_ALL
gboolean g_option_context_get_ignore_unknown_options (GOptionContext *context);
+GLIB_AVAILABLE_IN_2_44
+void g_option_context_set_strict_posix (GOptionContext *context,
+ gboolean strict_posix);
+GLIB_AVAILABLE_IN_2_44
+gboolean g_option_context_get_strict_posix (GOptionContext *context);
+
GLIB_AVAILABLE_IN_ALL
void g_option_context_add_main_entries (GOptionContext *context,
const GOptionEntry *entries,
diff --git a/glib/tests/option-context.c b/glib/tests/option-context.c
index 8cf77a6f0..0ca29ca3d 100644
--- a/glib/tests/option-context.c
+++ b/glib/tests/option-context.c
@@ -2300,6 +2300,71 @@ test_group_parse (void)
g_option_context_free (context);
}
+static gint
+option_context_parse_command_line (GOptionContext *context,
+ const gchar *command_line)
+{
+ gchar **argv;
+ guint argv_len, argv_new_len;
+ gboolean success;
+
+ argv = split_string (command_line, NULL);
+ argv_len = g_strv_length (argv);
+
+ success = g_option_context_parse_strv (context, &argv, NULL);
+ argv_new_len = g_strv_length (argv);
+
+ g_strfreev (argv);
+ return success ? argv_len - argv_new_len : -1;
+}
+
+static void
+test_strict_posix (void)
+{
+ GOptionContext *context;
+ gboolean foo;
+ gboolean bar;
+ GOptionEntry entries[] = {
+ { "foo", 'f', 0, G_OPTION_ARG_NONE, &foo, NULL, NULL },
+ { "bar", 'b', 0, G_OPTION_ARG_NONE, &bar, NULL, NULL },
+ { NULL }
+ };
+ gint n_parsed;
+
+ context = g_option_context_new (NULL);
+ g_option_context_add_main_entries (context, entries, NULL);
+
+ foo = bar = FALSE;
+ g_option_context_set_strict_posix (context, FALSE);
+ n_parsed = option_context_parse_command_line (context, "program --foo command --bar");
+ g_assert_cmpint (n_parsed, ==, 2);
+ g_assert (foo == TRUE);
+ g_assert (bar == TRUE);
+
+ foo = bar = FALSE;
+ g_option_context_set_strict_posix (context, TRUE);
+ n_parsed = option_context_parse_command_line (context, "program --foo command --bar");
+ g_assert_cmpint (n_parsed, ==, 1);
+ g_assert (foo == TRUE);
+ g_assert (bar == FALSE);
+
+ foo = bar = FALSE;
+ g_option_context_set_strict_posix (context, TRUE);
+ n_parsed = option_context_parse_command_line (context, "program --foo --bar command");
+ g_assert_cmpint (n_parsed, ==, 2);
+ g_assert (foo == TRUE);
+ g_assert (bar == TRUE);
+
+ foo = bar = FALSE;
+ g_option_context_set_strict_posix (context, TRUE);
+ n_parsed = option_context_parse_command_line (context, "program command --foo --bar");
+ g_assert_cmpint (n_parsed, ==, 0);
+ g_assert (foo == FALSE);
+ g_assert (bar == FALSE);
+
+ g_option_context_free (context);
+}
+
static void
flag_reverse_string (void)
{
@@ -2454,6 +2519,7 @@ main (int argc,
g_test_add_func ("/option/group/main", test_main_group);
g_test_add_func ("/option/group/error-hook", test_error_hook);
g_test_add_func ("/option/group/parse", test_group_parse);
+ g_test_add_func ("/option/strict-posix", test_strict_posix);
/* Test that restoration on failure works */
g_test_add_func ("/option/restoration/int", error_test1);