Add and implement a new flag to turn off the automatic <groupname>-

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.
This commit is contained in:
Matthias Clasen 2005-07-12 18:56:25 +00:00 committed by Matthias Clasen
parent 392980c783
commit 58dd4814b2
8 changed files with 475 additions and 48 deletions

View File

@ -1,3 +1,21 @@
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> 2005-07-12 Matthias Clasen <mclasen@redhat.com>
* glib/gthread.c (g_static_rec_mutex_lock_full): Don't lock * glib/gthread.c (g_static_rec_mutex_lock_full): Don't lock

View File

@ -1,3 +1,21 @@
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> 2005-07-12 Matthias Clasen <mclasen@redhat.com>
* glib/gthread.c (g_static_rec_mutex_lock_full): Don't lock * glib/gthread.c (g_static_rec_mutex_lock_full): Don't lock

View File

@ -1,3 +1,21 @@
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> 2005-07-12 Matthias Clasen <mclasen@redhat.com>
* glib/gthread.c (g_static_rec_mutex_lock_full): Don't lock * glib/gthread.c (g_static_rec_mutex_lock_full): Don't lock

View File

@ -1,3 +1,21 @@
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> 2005-07-12 Matthias Clasen <mclasen@redhat.com>
* glib/gthread.c (g_static_rec_mutex_lock_full): Don't lock * glib/gthread.c (g_static_rec_mutex_lock_full): Don't lock

View File

@ -275,6 +275,15 @@ Flags which modify individual options.
@G_OPTION_FLAG_FILENAME: For options of the %G_OPTION_ARG_CALLBACK @G_OPTION_FLAG_FILENAME: For options of the %G_OPTION_ARG_CALLBACK
kind, this flag indicates that the argument should be passed to the 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 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 ##### --> <!-- ##### MACRO G_OPTION_REMAINING ##### -->
<para> <para>

View File

@ -37,6 +37,9 @@
((entry)->arg == G_OPTION_ARG_CALLBACK && \ ((entry)->arg == G_OPTION_ARG_CALLBACK && \
((entry)->flags & G_OPTION_FLAG_NO_ARG))) ((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 typedef struct
{ {
GOptionArg arg_type; GOptionArg arg_type;
@ -494,12 +497,14 @@ print_help (GOptionContext *context,
for (i = 0; i < group->n_entries; i++) for (i = 0; i < group->n_entries; i++)
{ {
entry = &group->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); entry->long_name = g_strdup_printf ("%s-%s", group->name, entry->long_name);
else else
g_hash_table_insert (shadow_map, (gpointer)entry->long_name, entry); 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; entry->short_name = 0;
else else
seen[(guchar)entry->short_name] = TRUE; seen[(guchar)entry->short_name] = TRUE;
@ -854,7 +859,9 @@ parse_arg (GOptionContext *context,
gchar *data; gchar *data;
gboolean retval; 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; data = NULL;
else if (entry->flags & G_OPTION_FLAG_FILENAME) else if (entry->flags & G_OPTION_FLAG_FILENAME)
{ {
@ -867,7 +874,8 @@ parse_arg (GOptionContext *context,
else else
data = g_locale_to_utf8 (value, -1, NULL, NULL, error); 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; return FALSE;
retval = (* (GOptionArgFunc) entry->arg_data) (option_name, data, group->user_data, error); retval = (* (GOptionArgFunc) entry->arg_data) (option_name, data, group->user_data, error);
@ -901,23 +909,16 @@ parse_short_option (GOptionContext *context,
for (j = 0; j < group->n_entries; j++) for (j = 0; j < group->n_entries; j++)
{ {
if (arg == group->entries[j].short_name) 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 else
{ {
gchar *value = NULL;
gchar *option_name;
if (*new_index > index) if (*new_index > index)
{ {
g_warning ("FIXME: figure out the correct error here"); g_warning ("FIXME: figure out the correct error here");
@ -928,11 +929,27 @@ parse_short_option (GOptionContext *context,
option_name = g_strdup_printf ("-%c", group->entries[j].short_name); option_name = g_strdup_printf ("-%c", group->entries[j].short_name);
if (index < *argc - 1) if (index < *argc - 1)
{
if (!OPTIONAL_ARG (&group->entries[j]))
{ {
value = (*argv)[index + 1]; value = (*argv)[index + 1];
add_pending_null (context, &((*argv)[index + 1]), NULL); add_pending_null (context, &((*argv)[index + 1]), NULL);
*new_index = index+1; *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 else
{ {
g_set_error (error, g_set_error (error,
@ -941,8 +958,10 @@ parse_short_option (GOptionContext *context,
g_free (option_name); g_free (option_name);
return FALSE; 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); g_free (option_name);
return FALSE; return FALSE;
@ -952,7 +971,6 @@ parse_short_option (GOptionContext *context,
*parsed = TRUE; *parsed = TRUE;
} }
} }
}
return TRUE; return TRUE;
} }
@ -962,6 +980,7 @@ parse_long_option (GOptionContext *context,
GOptionGroup *group, GOptionGroup *group,
gint *index, gint *index,
gchar *arg, gchar *arg,
gboolean aliased,
gint *argc, gint *argc,
gchar ***argv, gchar ***argv,
GError **error, GError **error,
@ -974,6 +993,9 @@ parse_long_option (GOptionContext *context,
if (*index >= *argc) if (*index >= *argc)
return TRUE; return TRUE;
if (aliased && (group->entries[j].flags & G_OPTION_FLAG_NOALIAS))
continue;
if (NO_ARG (&group->entries[j]) && if (NO_ARG (&group->entries[j]) &&
strcmp (arg, group->entries[j].long_name) == 0) strcmp (arg, group->entries[j].long_name) == 0)
{ {
@ -1003,11 +1025,42 @@ parse_long_option (GOptionContext *context,
if (arg[len] == '=') if (arg[len] == '=')
value = arg + len + 1; value = arg + len + 1;
else if (*index < *argc - 1) else if (*index < *argc - 1)
{
if (!(group->entries[j].flags & G_OPTION_FLAG_OPTIONAL_ARG))
{ {
value = (*argv)[*index + 1]; value = (*argv)[*index + 1];
add_pending_null (context, &((*argv)[*index + 1]), NULL); add_pending_null (context, &((*argv)[*index + 1]), NULL);
(*index)++; (*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 else
{ {
g_set_error (error, g_set_error (error,
@ -1017,7 +1070,8 @@ parse_long_option (GOptionContext *context,
return FALSE; 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); g_free (option_name);
return FALSE; return FALSE;
@ -1268,7 +1322,7 @@ g_option_context_parse (GOptionContext *context,
if (context->main_group && if (context->main_group &&
!parse_long_option (context, context->main_group, &i, arg, !parse_long_option (context, context->main_group, &i, arg,
argc, argv, error, &parsed)) FALSE, argc, argv, error, &parsed))
goto fail; goto fail;
if (parsed) if (parsed)
@ -1281,7 +1335,7 @@ g_option_context_parse (GOptionContext *context,
GOptionGroup *group = list->data; GOptionGroup *group = list->data;
if (!parse_long_option (context, group, &i, arg, if (!parse_long_option (context, group, &i, arg,
argc, argv, error, &parsed)) FALSE, argc, argv, error, &parsed))
goto fail; goto fail;
if (parsed) if (parsed)
@ -1306,7 +1360,7 @@ g_option_context_parse (GOptionContext *context,
if (strncmp (group->name, arg, dash - arg) == 0) if (strncmp (group->name, arg, dash - arg) == 0)
{ {
if (!parse_long_option (context, group, &i, dash + 1, if (!parse_long_option (context, group, &i, dash + 1,
argc, argv, error, &parsed)) TRUE, argc, argv, error, &parsed))
goto fail; goto fail;
if (parsed) if (parsed)

View File

@ -36,7 +36,9 @@ typedef enum
G_OPTION_FLAG_IN_MAIN = 1 << 1, G_OPTION_FLAG_IN_MAIN = 1 << 1,
G_OPTION_FLAG_REVERSE = 1 << 2, G_OPTION_FLAG_REVERSE = 1 << 2,
G_OPTION_FLAG_NO_ARG = 1 << 3, G_OPTION_FLAG_NO_ARG = 1 << 3,
G_OPTION_FLAG_FILENAME = 1 << 4 G_OPTION_FLAG_FILENAME = 1 << 4,
G_OPTION_FLAG_OPTIONAL_ARG = 1 << 5,
G_OPTION_FLAG_NOALIAS = 1 << 6
} GOptionFlags; } GOptionFlags;
typedef enum typedef enum

View File

@ -12,6 +12,9 @@ gchar *arg_test3_filename;
gchar *callback_test1_string; gchar *callback_test1_string;
gboolean callback_test2_int; gboolean callback_test2_int;
gchar *callback_test_optional_string;
gboolean callback_test_optional_boolean;
gchar **array_test1_array; gchar **array_test1_array;
gboolean ignore_test1_boolean; gboolean ignore_test1_boolean;
@ -399,6 +402,283 @@ callback_test2 (void)
g_option_context_free (context); 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 void
ignore_test1 (void) ignore_test1 (void)
{ {
@ -1016,6 +1296,16 @@ main (int argc, char **argv)
callback_test1 (); callback_test1 ();
callback_test2 (); 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 */ /* Test ignoring options */
ignore_test1 (); ignore_test1 ();
ignore_test2 (); ignore_test2 ();