diff --git a/ChangeLog b/ChangeLog index d191bb727..09f5489fc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,16 @@ 2004-10-29 Matthias Clasen + * tests/option-test.c: Add tests for the handling of + non-option arguments, "--" and G_OPTION_REMAINING. + + * glib/goption.[hc]: #define G_OPTION_REMAINING, which is + a special long option name, which can be used for an option + in the main group which collects the non-option arguments. + It must be of type G_OPTION_ARG_STRING_ARRAY or + G_OPTION_ARG_FILENAME_ARRAY. If the main group doesn't contain + an option whose name is G_OPTION_REMAINING, the non-option + arguments are left behind in argv as before. + * glib/goption.c: Add documentation. 2004-10-28 Matthias Clasen diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index d191bb727..09f5489fc 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,5 +1,16 @@ 2004-10-29 Matthias Clasen + * tests/option-test.c: Add tests for the handling of + non-option arguments, "--" and G_OPTION_REMAINING. + + * glib/goption.[hc]: #define G_OPTION_REMAINING, which is + a special long option name, which can be used for an option + in the main group which collects the non-option arguments. + It must be of type G_OPTION_ARG_STRING_ARRAY or + G_OPTION_ARG_FILENAME_ARRAY. If the main group doesn't contain + an option whose name is G_OPTION_REMAINING, the non-option + arguments are left behind in argv as before. + * glib/goption.c: Add documentation. 2004-10-28 Matthias Clasen diff --git a/ChangeLog.pre-2-12 b/ChangeLog.pre-2-12 index d191bb727..09f5489fc 100644 --- a/ChangeLog.pre-2-12 +++ b/ChangeLog.pre-2-12 @@ -1,5 +1,16 @@ 2004-10-29 Matthias Clasen + * tests/option-test.c: Add tests for the handling of + non-option arguments, "--" and G_OPTION_REMAINING. + + * glib/goption.[hc]: #define G_OPTION_REMAINING, which is + a special long option name, which can be used for an option + in the main group which collects the non-option arguments. + It must be of type G_OPTION_ARG_STRING_ARRAY or + G_OPTION_ARG_FILENAME_ARRAY. If the main group doesn't contain + an option whose name is G_OPTION_REMAINING, the non-option + arguments are left behind in argv as before. + * glib/goption.c: Add documentation. 2004-10-28 Matthias Clasen diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index d191bb727..09f5489fc 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,5 +1,16 @@ 2004-10-29 Matthias Clasen + * tests/option-test.c: Add tests for the handling of + non-option arguments, "--" and G_OPTION_REMAINING. + + * glib/goption.[hc]: #define G_OPTION_REMAINING, which is + a special long option name, which can be used for an option + in the main group which collects the non-option arguments. + It must be of type G_OPTION_ARG_STRING_ARRAY or + G_OPTION_ARG_FILENAME_ARRAY. If the main group doesn't contain + an option whose name is G_OPTION_REMAINING, the non-option + arguments are left behind in argv as before. + * glib/goption.c: Add documentation. 2004-10-28 Matthias Clasen diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index d191bb727..09f5489fc 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,5 +1,16 @@ 2004-10-29 Matthias Clasen + * tests/option-test.c: Add tests for the handling of + non-option arguments, "--" and G_OPTION_REMAINING. + + * glib/goption.[hc]: #define G_OPTION_REMAINING, which is + a special long option name, which can be used for an option + in the main group which collects the non-option arguments. + It must be of type G_OPTION_ARG_STRING_ARRAY or + G_OPTION_ARG_FILENAME_ARRAY. If the main group doesn't contain + an option whose name is G_OPTION_REMAINING, the non-option + arguments are left behind in argv as before. + * glib/goption.c: Add documentation. 2004-10-28 Matthias Clasen diff --git a/glib/goption.c b/glib/goption.c index 7210a2cad..931d5c89c 100644 --- a/glib/goption.c +++ b/glib/goption.c @@ -19,9 +19,11 @@ * Boston, MA 02111-1307, USA. */ -#include "goption.h" +#include "config.h" #include "galias.h" + +#include "goption.h" #include "glib.h" #include "gi18n.h" @@ -148,8 +150,9 @@ g_option_context_new (const gchar *parameter_string) * * Since: 2.6 */ -void g_option_context_free (GOptionContext *context) { -g_return_if_fail (context != NULL); +void g_option_context_free (GOptionContext *context) +{ + g_return_if_fail (context != NULL); g_list_foreach (context->groups, (GFunc)g_option_group_free, NULL); g_list_free (context->groups); @@ -404,6 +407,9 @@ print_help (GOptionContext *context, /* Then we go through the entries */ for (i = 0; i < group->n_entries; i++) { + if (group->entries[i].flags & G_OPTION_FLAG_HIDDEN) + continue; + len = g_utf8_strlen (group->entries[i].long_name, -1); if (group->entries[i].short_name) @@ -862,6 +868,40 @@ parse_long_option (GOptionContext *context, return TRUE; } +static gboolean +parse_remaining_arg (GOptionContext *context, + GOptionGroup *group, + gint *index, + gint *argc, + gchar ***argv, + GError **error, + gboolean *parsed) +{ + gint j; + + for (j = 0; j < group->n_entries; j++) + { + if (*index >= *argc) + return TRUE; + + if (group->entries[j].long_name[0]) + continue; + + g_return_val_if_fail (group->entries[j].arg == G_OPTION_ARG_STRING_ARRAY || + group->entries[j].arg == G_OPTION_ARG_FILENAME_ARRAY, FALSE); + + add_pending_null (context, &((*argv)[*index]), NULL); + + if (!parse_arg (context, group, &group->entries[j], (*argv)[*index], "", error)) + return FALSE; + + *parsed = TRUE; + return TRUE; + } + + return TRUE; +} + static void free_changes_list (GOptionContext *context, gboolean revert) @@ -1004,12 +1044,14 @@ g_option_context_parse (GOptionContext *context, if (argc && argv) { + gboolean stop_parsing = FALSE; + for (i = 1; i < *argc; i++) { gchar *arg; gboolean parsed = FALSE; - if ((*argv)[i][0] == '-') + if ((*argv)[i][0] == '-' && !stop_parsing) { if ((*argv)[i][1] == '-') { @@ -1021,7 +1063,8 @@ g_option_context_parse (GOptionContext *context, if (*arg == 0) { add_pending_null (context, &((*argv)[i]), NULL); - break; + stop_parsing = TRUE; + continue; } /* Handle help options */ @@ -1167,6 +1210,15 @@ g_option_context_parse (GOptionContext *context, goto fail; } } + else + { + /* Collect remaining args */ + if (context->main_group && + !parse_remaining_arg (context, context->main_group, &i, + argc, argv, error, &parsed)) + goto fail; + + } } /* Call post-parse hooks */ @@ -1204,7 +1256,10 @@ g_option_context_parse (GOptionContext *context, { k -= i; for (j = i + k; j < *argc; j++) - (*argv)[j-k] = (*argv)[j]; + { + (*argv)[j-k] = (*argv)[j]; + (*argv)[j] = NULL; + } *argc -= k; } } @@ -1415,7 +1470,7 @@ g_option_group_set_translate_func (GOptionGroup *group, group->translate_func = func; group->translate_data = data; - group->translate_notify = notify; + group->translate_notify = destroy_notify; } static gchar * diff --git a/glib/goption.h b/glib/goption.h index f1ffd0fca..1b62d7c2d 100644 --- a/glib/goption.h +++ b/glib/goption.h @@ -87,6 +87,8 @@ struct _GOptionEntry const gchar *arg_description; }; +#define G_OPTION_REMAINING "" + GOptionContext *g_option_context_new (const gchar *parameter_string); void g_option_context_free (GOptionContext *context); void g_option_context_set_help_enabled (GOptionContext *context, @@ -132,9 +134,6 @@ void g_option_group_set_translation_domain (GOptionGroup *group, const gchar *domain); - - - G_END_DECLS #endif /* __G_OPTION_H__ */ diff --git a/tests/option-test.c b/tests/option-test.c index daf75916a..38f8266ec 100644 --- a/tests/option-test.c +++ b/tests/option-test.c @@ -524,6 +524,184 @@ empty_test3 (void) g_option_context_free (context); } +/* check that non-option arguments are left in argv by default */ +void +rest_test1 (void) +{ + GOptionContext *context; + gboolean retval; + GError *error = NULL; + gchar **argv; + int argc; + GOptionEntry entries [] = { + { "test", 0, 0, G_OPTION_ARG_NONE, &ignore_test1_boolean, 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 foo --test bar", &argc); + + retval = g_option_context_parse (context, &argc, &argv, &error); + g_assert (retval); + + /* Check array */ + g_assert (ignore_test1_boolean); + g_assert (strcmp (argv[0], "program") == 0); + g_assert (strcmp (argv[1], "foo") == 0); + g_assert (strcmp (argv[2], "bar") == 0); + g_assert (argv[3] == NULL); + + g_strfreev (argv); + g_option_context_free (context); +} + +/* check that -- works */ +void +rest_test2 (void) +{ + GOptionContext *context; + gboolean retval; + GError *error = NULL; + gchar **argv; + int argc; + GOptionEntry entries [] = { + { "test", 0, 0, G_OPTION_ARG_NONE, &ignore_test1_boolean, 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 foo --test -- -bar", &argc); + + retval = g_option_context_parse (context, &argc, &argv, &error); + g_assert (retval); + + /* Check array */ + g_assert (ignore_test1_boolean); + g_assert (strcmp (argv[0], "program") == 0); + g_assert (strcmp (argv[1], "foo") == 0); + g_assert (strcmp (argv[2], "-bar") == 0); + g_assert (argv[3] == NULL); + + g_strfreev (argv); + g_option_context_free (context); +} + +/* check that G_OPTION_REMAINING collects non-option arguments */ +void +rest_test3 (void) +{ + GOptionContext *context; + gboolean retval; + GError *error = NULL; + gchar **argv; + int argc; + GOptionEntry entries [] = { + { "test", 0, 0, G_OPTION_ARG_NONE, &ignore_test1_boolean, NULL, NULL }, + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &array_test1_array, 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 foo --test bar", &argc); + + retval = g_option_context_parse (context, &argc, &argv, &error); + g_assert (retval); + + /* Check array */ + g_assert (ignore_test1_boolean); + g_assert (strcmp (array_test1_array[0], "foo") == 0); + g_assert (strcmp (array_test1_array[1], "bar") == 0); + g_assert (array_test1_array[2] == NULL); + + g_strfreev (array_test1_array); + + g_strfreev (argv); + g_option_context_free (context); +} + + +/* check that G_OPTION_REMAINING and -- work together */ +void +rest_test4 (void) +{ + GOptionContext *context; + gboolean retval; + GError *error = NULL; + gchar **argv; + int argc; + GOptionEntry entries [] = { + { "test", 0, 0, G_OPTION_ARG_NONE, &ignore_test1_boolean, NULL, NULL }, + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &array_test1_array, 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 foo --test -- -bar", &argc); + + retval = g_option_context_parse (context, &argc, &argv, &error); + g_assert (retval); + + /* Check array */ + g_assert (ignore_test1_boolean); + g_assert (strcmp (array_test1_array[0], "foo") == 0); + g_assert (strcmp (array_test1_array[1], "-bar") == 0); + g_assert (array_test1_array[2] == NULL); + + g_strfreev (array_test1_array); + + g_strfreev (argv); + g_option_context_free (context); +} + +/* test that G_OPTION_REMAINING works with G_OPTION_ARG_FILENAME_ARRAY */ +void +rest_test5 (void) +{ + GOptionContext *context; + gboolean retval; + GError *error = NULL; + gchar **argv; + int argc; + GOptionEntry entries [] = { + { "test", 0, 0, G_OPTION_ARG_NONE, &ignore_test1_boolean, NULL, NULL }, + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &array_test1_array, 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 foo --test bar", &argc); + + retval = g_option_context_parse (context, &argc, &argv, &error); + g_assert (retval); + + /* Check array */ + g_assert (ignore_test1_boolean); + g_assert (strcmp (array_test1_array[0], "foo") == 0); + g_assert (strcmp (array_test1_array[1], "bar") == 0); + g_assert (array_test1_array[2] == NULL); + + g_strfreev (array_test1_array); + + g_strfreev (argv); + g_option_context_free (context); +} + + int main (int argc, char **argv) { @@ -555,5 +733,12 @@ main (int argc, char **argv) empty_test2 (); empty_test3 (); + /* Test handling of rest args */ + rest_test1 (); + rest_test2 (); + rest_test3 (); + rest_test4 (); + rest_test5 (); + return 0; }