2005-07-12 Matthias Clasen <mclasen@redhat.com>
+ * glib/goption.h (G_OPTION_FLAG_NOALIAS):
+ * glib/goption.c: Add and implement a new flag
+ to turn off the automatic <groupname>- prefixing
+ for conflict resolution of long option names. (#171840,
+ Adam McLaurin)
+
+ All optional callback arguments (#308886, Pawel
+ Sliwowski)
+
+ * glib/goption.h (G_OPTION_FLAG_OPTIONAL_ARG):
+ * glib/goption.c: Add and implement a new flag
+ to indicate that a callback *optionally* takes another
+ argument.
+
+ * tests/option-test.c: Add tests for optional arguments.
+
+2005-07-12 Matthias Clasen <mclasen@redhat.com>
+
* glib/gthread.c (g_static_rec_mutex_lock_full): Don't lock
if depth is zero. (#310148, Wim Taymans)
2005-07-12 Matthias Clasen <mclasen@redhat.com>
+ * glib/goption.h (G_OPTION_FLAG_NOALIAS):
+ * glib/goption.c: Add and implement a new flag
+ to turn off the automatic <groupname>- prefixing
+ for conflict resolution of long option names. (#171840,
+ Adam McLaurin)
+
+ All optional callback arguments (#308886, Pawel
+ Sliwowski)
+
+ * glib/goption.h (G_OPTION_FLAG_OPTIONAL_ARG):
+ * glib/goption.c: Add and implement a new flag
+ to indicate that a callback *optionally* takes another
+ argument.
+
+ * tests/option-test.c: Add tests for optional arguments.
+
+2005-07-12 Matthias Clasen <mclasen@redhat.com>
+
* glib/gthread.c (g_static_rec_mutex_lock_full): Don't lock
if depth is zero. (#310148, Wim Taymans)
2005-07-12 Matthias Clasen <mclasen@redhat.com>
+ * glib/goption.h (G_OPTION_FLAG_NOALIAS):
+ * glib/goption.c: Add and implement a new flag
+ to turn off the automatic <groupname>- prefixing
+ for conflict resolution of long option names. (#171840,
+ Adam McLaurin)
+
+ All optional callback arguments (#308886, Pawel
+ Sliwowski)
+
+ * glib/goption.h (G_OPTION_FLAG_OPTIONAL_ARG):
+ * glib/goption.c: Add and implement a new flag
+ to indicate that a callback *optionally* takes another
+ argument.
+
+ * tests/option-test.c: Add tests for optional arguments.
+
+2005-07-12 Matthias Clasen <mclasen@redhat.com>
+
* glib/gthread.c (g_static_rec_mutex_lock_full): Don't lock
if depth is zero. (#310148, Wim Taymans)
2005-07-12 Matthias Clasen <mclasen@redhat.com>
+ * glib/goption.h (G_OPTION_FLAG_NOALIAS):
+ * glib/goption.c: Add and implement a new flag
+ to turn off the automatic <groupname>- prefixing
+ for conflict resolution of long option names. (#171840,
+ Adam McLaurin)
+
+ All optional callback arguments (#308886, Pawel
+ Sliwowski)
+
+ * glib/goption.h (G_OPTION_FLAG_OPTIONAL_ARG):
+ * glib/goption.c: Add and implement a new flag
+ to indicate that a callback *optionally* takes another
+ argument.
+
+ * tests/option-test.c: Add tests for optional arguments.
+
+2005-07-12 Matthias Clasen <mclasen@redhat.com>
+
* glib/gthread.c (g_static_rec_mutex_lock_full): Don't lock
if depth is zero. (#310148, Wim Taymans)
@G_OPTION_FLAG_FILENAME: For options of the %G_OPTION_ARG_CALLBACK
kind, this flag indicates that the argument should be passed to the
callback in the GLib filename encoding rather than UTF-8. Since 2.8
+@G_OPTION_FLAG_OPTIONAL_ARG: For options of the %G_OPTION_ARG_CALLBACK
+ kind, this flag indicates that the argument supply is optional. If no argument
+ is given then data of %GOptionParseFunc will be set to NULL. Since 2.8
+@G_OPTION_FLAG_NOALIAS: This flag turns off the automatic conflict resolution
+ which prefixes long option names with <literal>groupname-</literal> if
+ there is a conflict. This option should only be used in situations where
+ aliasing is necessary to model some legacy commandline interface. It is
+ not safe to use this option, unless all option groups are under your
+ direct control. Since 2.8.
<!-- ##### MACRO G_OPTION_REMAINING ##### -->
<para>
((entry)->arg == G_OPTION_ARG_CALLBACK && \
((entry)->flags & G_OPTION_FLAG_NO_ARG)))
+#define OPTIONAL_ARG(entry) ((entry)->arg == G_OPTION_ARG_CALLBACK && \
+ (entry)->flags & G_OPTION_FLAG_OPTIONAL_ARG)
+
typedef struct
{
GOptionArg arg_type;
for (i = 0; i < group->n_entries; i++)
{
entry = &group->entries[i];
- if (g_hash_table_lookup (shadow_map, entry->long_name))
+ if (g_hash_table_lookup (shadow_map, entry->long_name) &&
+ !(entry->flags && G_OPTION_FLAG_NOALIAS))
entry->long_name = g_strdup_printf ("%s-%s", group->name, entry->long_name);
else
g_hash_table_insert (shadow_map, (gpointer)entry->long_name, entry);
- if (seen[(guchar)entry->short_name])
+ if (seen[(guchar)entry->short_name] &&
+ !(entry->flags && G_OPTION_FLAG_NOALIAS))
entry->short_name = 0;
else
seen[(guchar)entry->short_name] = TRUE;
case G_OPTION_ARG_STRING:
{
gchar *data;
-
+
data = g_locale_to_utf8 (value, -1, NULL, NULL, error);
if (!data)
case G_OPTION_ARG_STRING_ARRAY:
{
gchar *data;
-
+
data = g_locale_to_utf8 (value, -1, NULL, NULL, error);
if (!data)
{
gchar *data;
gboolean retval;
-
- if (entry->flags & G_OPTION_FLAG_NO_ARG)
+
+ if (!value && entry->flags & G_OPTION_FLAG_OPTIONAL_ARG)
+ data = NULL;
+ else if (entry->flags & G_OPTION_FLAG_NO_ARG)
data = NULL;
else if (entry->flags & G_OPTION_FLAG_FILENAME)
{
#ifdef G_OS_WIN32
- data = g_locale_to_utf8 (value, -1, NULL, NULL, error);
+ data = g_locale_to_utf8 (value, -1, NULL, NULL, error);
#else
data = g_strdup (value);
#endif
else
data = g_locale_to_utf8 (value, -1, NULL, NULL, error);
- if (!(entry->flags & G_OPTION_FLAG_NO_ARG) && !data)
+ if (!(entry->flags & (G_OPTION_FLAG_NO_ARG|G_OPTION_FLAG_OPTIONAL_ARG)) &&
+ !data)
return FALSE;
retval = (* (GOptionArgFunc) entry->arg_data) (option_name, data, group->user_data, error);
{
if (arg == group->entries[j].short_name)
{
- if (NO_ARG (&group->entries[j]))
- {
- gchar *option_name;
+ gchar *option_name;
+ gchar *value = NULL;
+
+ option_name = g_strdup_printf ("-%c", group->entries[j].short_name);
- option_name = g_strdup_printf ("-%c", group->entries[j].short_name);
- parse_arg (context, group, &group->entries[j],
- NULL, option_name, error);
- g_free (option_name);
-
- *parsed = TRUE;
- }
+ if (NO_ARG (&group->entries[j]))
+ value = NULL;
else
{
- gchar *value = NULL;
- gchar *option_name;
-
if (*new_index > index)
{
g_warning ("FIXME: figure out the correct error here");
if (index < *argc - 1)
{
- value = (*argv)[index + 1];
- add_pending_null (context, &((*argv)[index + 1]), NULL);
- *new_index = index + 1;
+ if (!OPTIONAL_ARG (&group->entries[j]))
+ {
+ value = (*argv)[index + 1];
+ add_pending_null (context, &((*argv)[index + 1]), NULL);
+ *new_index = index+1;
+ }
+ else
+ {
+ if ((*argv)[index + 1][0] == '-')
+ value = NULL;
+ else
+ {
+ value = (*argv)[index + 1];
+ add_pending_null (context, &((*argv)[index + 1]), NULL);
+ *new_index = index + 1;
+ }
+ }
}
+ else if (index >= *argc - 1 && OPTIONAL_ARG (&group->entries[j]))
+ value = NULL;
else
{
g_set_error (error,
g_free (option_name);
return FALSE;
}
-
- if (!parse_arg (context, group, &group->entries[j], value, option_name, error))
- {
- g_free (option_name);
- return FALSE;
- }
+ }
+ if (!parse_arg (context, group, &group->entries[j],
+ value, option_name, error))
+ {
g_free (option_name);
- *parsed = TRUE;
+ return FALSE;
}
+
+ g_free (option_name);
+ *parsed = TRUE;
}
}
GOptionGroup *group,
gint *index,
gchar *arg,
+ gboolean aliased,
gint *argc,
gchar ***argv,
GError **error,
if (*index >= *argc)
return TRUE;
+ if (aliased && (group->entries[j].flags & G_OPTION_FLAG_NOALIAS))
+ continue;
+
if (NO_ARG (&group->entries[j]) &&
strcmp (arg, group->entries[j].long_name) == 0)
{
if (arg[len] == '=')
value = arg + len + 1;
- else if (*index < *argc - 1)
+ else if (*index < *argc - 1)
{
- value = (*argv)[*index + 1];
- add_pending_null (context, &((*argv)[*index + 1]), NULL);
- (*index)++;
+ if (!(group->entries[j].flags & G_OPTION_FLAG_OPTIONAL_ARG))
+ {
+ value = (*argv)[*index + 1];
+ add_pending_null (context, &((*argv)[*index + 1]), NULL);
+ (*index)++;
+ }
+ else
+ {
+ if ((*argv)[*index + 1][0] == '-')
+ {
+ gboolean retval;
+ retval = parse_arg (context, group, &group->entries[j],
+ NULL, option_name, error);
+ *parsed = TRUE;
+ g_free (option_name);
+ return retval;
+ }
+ else
+ {
+ value = (*argv)[*index + 1];
+ add_pending_null (context, &((*argv)[*index + 1]), NULL);
+ (*index)++;
+ }
+ }
+ }
+ else if (*index >= *argc - 1 &&
+ group->entries[j].flags & G_OPTION_FLAG_OPTIONAL_ARG)
+ {
+ gboolean retval;
+ retval = parse_arg (context, group, &group->entries[j],
+ NULL, option_name, error);
+ *parsed = TRUE;
+ g_free (option_name);
+ return retval;
}
else
{
return FALSE;
}
- if (!parse_arg (context, group, &group->entries[j], value, option_name, error))
+ if (!parse_arg (context, group, &group->entries[j],
+ value, option_name, error))
{
g_free (option_name);
return FALSE;
g_free (option_name);
*parsed = TRUE;
- }
+ }
}
}
if (context->main_group &&
!parse_long_option (context, context->main_group, &i, arg,
- argc, argv, error, &parsed))
+ FALSE, argc, argv, error, &parsed))
goto fail;
if (parsed)
{
GOptionGroup *group = list->data;
- if (!parse_long_option (context, group, &i, arg,
- argc, argv, error, &parsed))
+ if (!parse_long_option (context, group, &i, arg,
+ FALSE, argc, argv, error, &parsed))
goto fail;
if (parsed)
if (strncmp (group->name, arg, dash - arg) == 0)
{
if (!parse_long_option (context, group, &i, dash + 1,
- argc, argv, error, &parsed))
+ TRUE, argc, argv, error, &parsed))
goto fail;
if (parsed)
typedef enum
{
- G_OPTION_FLAG_HIDDEN = 1 << 0,
- G_OPTION_FLAG_IN_MAIN = 1 << 1,
- G_OPTION_FLAG_REVERSE = 1 << 2,
- G_OPTION_FLAG_NO_ARG = 1 << 3,
- G_OPTION_FLAG_FILENAME = 1 << 4
+ G_OPTION_FLAG_HIDDEN = 1 << 0,
+ G_OPTION_FLAG_IN_MAIN = 1 << 1,
+ G_OPTION_FLAG_REVERSE = 1 << 2,
+ G_OPTION_FLAG_NO_ARG = 1 << 3,
+ G_OPTION_FLAG_FILENAME = 1 << 4,
+ G_OPTION_FLAG_OPTIONAL_ARG = 1 << 5,
+ G_OPTION_FLAG_NOALIAS = 1 << 6
} GOptionFlags;
typedef enum
gchar *callback_test1_string;
gboolean callback_test2_int;
+gchar *callback_test_optional_string;
+gboolean callback_test_optional_boolean;
+
gchar **array_test1_array;
gboolean ignore_test1_boolean;
g_option_context_free (context);
}
+static gboolean
+callback_parse_optional (const gchar *option_name, const gchar *value,
+ gpointer data, GError **error)
+{
+ callback_test_optional_boolean = TRUE;
+ if (value)
+ callback_test_optional_string = g_strdup (value);
+ else
+ callback_test_optional_string = NULL;
+ return TRUE;
+}
+
+void
+callback_test_optional_1 (void)
+{
+ GOptionContext *context;
+ gboolean retval;
+ GError *error = NULL;
+ gchar **argv;
+ int argc;
+ GOptionEntry entries [] =
+ { { "test", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
+ callback_parse_optional, NULL, NULL },
+ { NULL } };
+
+ context = g_option_context_new (NULL);
+ g_option_context_add_main_entries (context, entries, NULL);
+
+ /* Now try parsing */
+ argv = split_string ("program --test foo.txt", &argc);
+
+ retval = g_option_context_parse (context, &argc, &argv, &error);
+ g_assert (retval);
+
+ g_assert (strcmp (callback_test_optional_string, "foo.txt") == 0);
+
+ g_assert (callback_test_optional_boolean);
+
+ g_free (callback_test_optional_string);
+
+ g_strfreev (argv);
+ g_option_context_free (context);
+}
+
+void
+callback_test_optional_2 (void)
+{
+ GOptionContext *context;
+ gboolean retval;
+ GError *error = NULL;
+ gchar **argv;
+ int argc;
+ GOptionEntry entries [] =
+ { { "test", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
+ callback_parse_optional, NULL, NULL },
+ { NULL } };
+
+ context = g_option_context_new (NULL);
+ g_option_context_add_main_entries (context, entries, NULL);
+
+ /* Now try parsing */
+ argv = split_string ("program --test", &argc);
+
+ retval = g_option_context_parse (context, &argc, &argv, &error);
+ g_assert (retval);
+
+ g_assert (callback_test_optional_string == NULL);
+
+ g_assert (callback_test_optional_boolean);
+
+ g_free (callback_test_optional_string);
+
+ g_strfreev (argv);
+ g_option_context_free (context);
+}
+
+void
+callback_test_optional_3 (void)
+{
+ GOptionContext *context;
+ gboolean retval;
+ GError *error = NULL;
+ gchar **argv;
+ int argc;
+ GOptionEntry entries [] =
+ { { "test", 't', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
+ callback_parse_optional, NULL, NULL },
+ { NULL } };
+
+ context = g_option_context_new (NULL);
+ g_option_context_add_main_entries (context, entries, NULL);
+
+ /* Now try parsing */
+ argv = split_string ("program -t foo.txt", &argc);
+
+ retval = g_option_context_parse (context, &argc, &argv, &error);
+ g_assert (retval);
+
+ g_assert (strcmp (callback_test_optional_string, "foo.txt") == 0);
+
+ g_assert (callback_test_optional_boolean);
+
+ g_free (callback_test_optional_string);
+
+ g_strfreev (argv);
+ g_option_context_free (context);
+}
+
+
+void
+callback_test_optional_4 (void)
+{
+ GOptionContext *context;
+ gboolean retval;
+ GError *error = NULL;
+ gchar **argv;
+ int argc;
+ GOptionEntry entries [] =
+ { { "test", 't', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
+ callback_parse_optional, NULL, NULL },
+ { NULL } };
+
+ context = g_option_context_new (NULL);
+ g_option_context_add_main_entries (context, entries, NULL);
+
+ /* Now try parsing */
+ argv = split_string ("program -t", &argc);
+
+ retval = g_option_context_parse (context, &argc, &argv, &error);
+ g_assert (retval);
+
+ g_assert (callback_test_optional_string == NULL);
+
+ g_assert (callback_test_optional_boolean);
+
+ g_free (callback_test_optional_string);
+
+ g_strfreev (argv);
+ g_option_context_free (context);
+}
+
+void
+callback_test_optional_5 (void)
+{
+ GOptionContext *context;
+ gboolean dummy;
+ gboolean retval;
+ GError *error = NULL;
+ gchar **argv;
+ int argc;
+ GOptionEntry entries [] =
+ { { "dummy", 'd', 0, G_OPTION_ARG_NONE, &dummy, NULL },
+ { "test", 't', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
+ callback_parse_optional, NULL, NULL },
+ { NULL } };
+
+ context = g_option_context_new (NULL);
+ g_option_context_add_main_entries (context, entries, NULL);
+
+ /* Now try parsing */
+ argv = split_string ("program --test --dummy", &argc);
+
+ retval = g_option_context_parse (context, &argc, &argv, &error);
+ g_assert (retval);
+
+ g_assert (callback_test_optional_string == NULL);
+
+ g_assert (callback_test_optional_boolean);
+
+ g_free (callback_test_optional_string);
+
+ g_strfreev (argv);
+ g_option_context_free (context);
+}
+
+void
+callback_test_optional_6 (void)
+{
+ GOptionContext *context;
+ gboolean dummy;
+ gboolean retval;
+ GError *error = NULL;
+ gchar **argv;
+ int argc;
+ GOptionEntry entries [] =
+ { { "dummy", 'd', 0, G_OPTION_ARG_NONE, &dummy, NULL },
+ { "test", 't', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
+ callback_parse_optional, NULL, NULL },
+ { NULL } };
+
+ context = g_option_context_new (NULL);
+ g_option_context_add_main_entries (context, entries, NULL);
+
+ /* Now try parsing */
+ argv = split_string ("program -t -d", &argc);
+
+ retval = g_option_context_parse (context, &argc, &argv, &error);
+ g_assert (retval);
+
+ g_assert (callback_test_optional_string == NULL);
+
+ g_assert (callback_test_optional_boolean);
+
+ g_free (callback_test_optional_string);
+
+ g_strfreev (argv);
+ g_option_context_free (context);
+}
+
+void
+callback_test_optional_7 (void)
+{
+ GOptionContext *context;
+ gboolean dummy;
+ gboolean retval;
+ GError *error = NULL;
+ gchar **argv;
+ int argc;
+ GOptionEntry entries [] =
+ { { "dummy", 'd', 0, G_OPTION_ARG_NONE, &dummy, NULL },
+ { "test", 't', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
+ callback_parse_optional, NULL, NULL },
+ { NULL } };
+
+ context = g_option_context_new (NULL);
+ g_option_context_add_main_entries (context, entries, NULL);
+
+ /* Now try parsing */
+ argv = split_string ("program -td", &argc);
+
+ retval = g_option_context_parse (context, &argc, &argv, &error);
+ g_assert (retval);
+
+ g_assert (callback_test_optional_string == NULL);
+
+ g_assert (callback_test_optional_boolean);
+
+ g_free (callback_test_optional_string);
+
+ g_strfreev (argv);
+ g_option_context_free (context);
+}
+
+void
+callback_test_optional_8 (void)
+{
+ GOptionContext *context;
+ gboolean dummy;
+ gboolean retval;
+ GError *error = NULL;
+ gchar **argv;
+ int argc;
+ GOptionEntry entries [] =
+ { { "dummy", 'd', 0, G_OPTION_ARG_NONE, &dummy, NULL },
+ { "test", 't', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
+ callback_parse_optional, NULL, NULL },
+ { NULL } };
+
+ context = g_option_context_new (NULL);
+ g_option_context_add_main_entries (context, entries, NULL);
+
+ /* Now try parsing */
+ argv = split_string ("program -dt foo.txt", &argc);
+
+ retval = g_option_context_parse (context, &argc, &argv, &error);
+ g_assert (retval);
+
+ g_assert (callback_test_optional_string);
+
+ g_assert (callback_test_optional_boolean);
+
+ g_free (callback_test_optional_string);
+
+ g_strfreev (argv);
+ g_option_context_free (context);
+}
+
void
ignore_test1 (void)
{
callback_test1 ();
callback_test2 ();
+ /* Test optional arg flag for callback */
+ callback_test_optional_1 ();
+ callback_test_optional_2 ();
+ callback_test_optional_3 ();
+ callback_test_optional_4 ();
+ callback_test_optional_5 ();
+ callback_test_optional_6 ();
+ callback_test_optional_7 ();
+ callback_test_optional_8 ();
+
/* Test ignoring options */
ignore_test1 ();
ignore_test2 ();