From 58dd4814b26199aad33afbba7abac2bb6b462682 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 12 Jul 2005 18:56:25 +0000 Subject: [PATCH] Add and implement a new flag to turn off the automatic - 2005-07-12 Matthias Clasen * glib/goption.h (G_OPTION_FLAG_NOALIAS): * glib/goption.c: Add and implement a new flag to turn off the automatic - 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. --- ChangeLog | 18 ++ ChangeLog.pre-2-10 | 18 ++ ChangeLog.pre-2-12 | 18 ++ ChangeLog.pre-2-8 | 18 ++ docs/reference/glib/tmpl/option.sgml | 9 + glib/goption.c | 140 +++++++++---- glib/goption.h | 12 +- tests/option-test.c | 290 +++++++++++++++++++++++++++ 8 files changed, 475 insertions(+), 48 deletions(-) diff --git a/ChangeLog b/ChangeLog index abb3bfef2..59b77ae5e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2005-07-12 Matthias Clasen + + * glib/goption.h (G_OPTION_FLAG_NOALIAS): + * glib/goption.c: Add and implement a new flag + to turn off the automatic - 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 * glib/gthread.c (g_static_rec_mutex_lock_full): Don't lock diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index abb3bfef2..59b77ae5e 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,21 @@ +2005-07-12 Matthias Clasen + + * glib/goption.h (G_OPTION_FLAG_NOALIAS): + * glib/goption.c: Add and implement a new flag + to turn off the automatic - 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 * glib/gthread.c (g_static_rec_mutex_lock_full): Don't lock diff --git a/ChangeLog.pre-2-12 b/ChangeLog.pre-2-12 index abb3bfef2..59b77ae5e 100644 --- a/ChangeLog.pre-2-12 +++ b/ChangeLog.pre-2-12 @@ -1,3 +1,21 @@ +2005-07-12 Matthias Clasen + + * glib/goption.h (G_OPTION_FLAG_NOALIAS): + * glib/goption.c: Add and implement a new flag + to turn off the automatic - 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 * glib/gthread.c (g_static_rec_mutex_lock_full): Don't lock diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index abb3bfef2..59b77ae5e 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,21 @@ +2005-07-12 Matthias Clasen + + * glib/goption.h (G_OPTION_FLAG_NOALIAS): + * glib/goption.c: Add and implement a new flag + to turn off the automatic - 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 * glib/gthread.c (g_static_rec_mutex_lock_full): Don't lock diff --git a/docs/reference/glib/tmpl/option.sgml b/docs/reference/glib/tmpl/option.sgml index a4fa8f81c..3014b3a58 100644 --- a/docs/reference/glib/tmpl/option.sgml +++ b/docs/reference/glib/tmpl/option.sgml @@ -275,6 +275,15 @@ Flags which modify individual options. @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 groupname- 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. diff --git a/glib/goption.c b/glib/goption.c index efa427172..b0536e229 100644 --- a/glib/goption.c +++ b/glib/goption.c @@ -37,6 +37,9 @@ ((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; @@ -494,12 +497,14 @@ print_help (GOptionContext *context, 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; @@ -728,7 +733,7 @@ parse_arg (GOptionContext *context, case G_OPTION_ARG_STRING: { gchar *data; - + data = g_locale_to_utf8 (value, -1, NULL, NULL, error); if (!data) @@ -747,7 +752,7 @@ parse_arg (GOptionContext *context, case G_OPTION_ARG_STRING_ARRAY: { gchar *data; - + data = g_locale_to_utf8 (value, -1, NULL, NULL, error); if (!data) @@ -853,13 +858,15 @@ parse_arg (GOptionContext *context, { 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 @@ -867,7 +874,8 @@ parse_arg (GOptionContext *context, 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); @@ -902,22 +910,15 @@ parse_short_option (GOptionContext *context, { 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"); @@ -929,10 +930,26 @@ parse_short_option (GOptionContext *context, 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, @@ -941,16 +958,17 @@ parse_short_option (GOptionContext *context, g_free (option_name); return FALSE; } - - 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 (!parse_arg (context, group, &group->entries[j], + value, option_name, error)) + { + g_free (option_name); + return FALSE; + } + + g_free (option_name); + *parsed = TRUE; } } @@ -962,6 +980,7 @@ parse_long_option (GOptionContext *context, GOptionGroup *group, gint *index, gchar *arg, + gboolean aliased, gint *argc, gchar ***argv, GError **error, @@ -974,6 +993,9 @@ parse_long_option (GOptionContext *context, 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) { @@ -1002,11 +1024,42 @@ parse_long_option (GOptionContext *context, 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 { @@ -1017,7 +1070,8 @@ parse_long_option (GOptionContext *context, 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; @@ -1025,7 +1079,7 @@ parse_long_option (GOptionContext *context, g_free (option_name); *parsed = TRUE; - } + } } } @@ -1268,7 +1322,7 @@ g_option_context_parse (GOptionContext *context, 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) @@ -1280,8 +1334,8 @@ g_option_context_parse (GOptionContext *context, { 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) @@ -1306,7 +1360,7 @@ g_option_context_parse (GOptionContext *context, 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) diff --git a/glib/goption.h b/glib/goption.h index dcd36e43b..4448fd0c3 100644 --- a/glib/goption.h +++ b/glib/goption.h @@ -32,11 +32,13 @@ typedef struct _GOptionEntry GOptionEntry; 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 diff --git a/tests/option-test.c b/tests/option-test.c index ffd1761e5..4699afd7d 100644 --- a/tests/option-test.c +++ b/tests/option-test.c @@ -12,6 +12,9 @@ gchar *arg_test3_filename; 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; @@ -399,6 +402,283 @@ callback_test2 (void) 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) { @@ -1016,6 +1296,16 @@ main (int argc, char **argv) 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 ();