[glib] GOption: add strict posix mode



commit ae52ab3d1170a393b0b734e9b9b37c3f107a7c03
Author: Ryan Lortie <desrt desrt ca>
Date:   Mon Jan 27 15:42:23 2014 +0000

    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

 glib/goption.c              |   72 +++++++++++++++++++++++++++++++++++++++++++
 glib/goption.h              |    6 +++
 glib/tests/option-context.c |   66 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 144 insertions(+), 0 deletions(-)
---
diff --git a/glib/goption.c b/glib/goption.c
index 6438281..019f54f 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 ab12a27..205a484 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 8cf77a6..0ca29ca 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);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]