Re: glib parameter handling
- From: Havoc Pennington <hp redhat com>
- To: Wouter van Kleunen <w a p vankleunen student utwente nl>
- Cc: gtk-devel-list gnome org
- Subject: Re: glib parameter handling
- Date: 06 Feb 2001 10:48:50 -0500
Hi,
Appended is my current draft on this problem, which I like parts of
and really hate other parts of. It doesn't actually work, there are
little unimplemented bits. ;-)
I don't think we can get this feature in 2.0, so, lots of time to
think about alternatives.
One big-picture choice is between a huge try-to-do-everything approach
like the one I have drafted here, and a small getopt-type thing like
yours.
Havoc
#include <glib.h>
/* See notes at http://mail.gnome.org/pipermail/gtk-devel-list/2000-May/003357.html */
/* Note that the translation domain mechanism requires the use of gettext.
* which sort of means we need gettext to work on Windows and other
* glib targets.
*/
/* Note: handle "--" for end of options */
/* GOptionParser should really be a GObject, but we have a dependency
* problem with that. (Why is GObject in a separate lib anyway?)
*
* Object data could in particular be used to destroy "static"
* options, when the parser goes away.
*
* Object data is probably more useful than callback data, also,
* since callback data is a pain to stick in static structs.
*
*/
typedef struct _GOptionParser GOptionParser;
#define G_OPTION_ERROR g_option_error_quark ()
GQuark g_option_error_quark (void);
typedef enum
{
/* Missing arg on an option that requires an arg */
G_OPTION_ERROR_MISSING_ARG,
/* Couldn't parse an arg for an option with an arg */
G_OPTION_ERROR_BAD_ARG,
/* Don't know about this option */
G_OPTION_ERROR_UNKNOWN_OPTION,
/* Non-option args aren't allowed in argv,
* and there were some.
*/
G_OPTION_ERROR_EXTRA_ARGS,
/* Option that can only appear once appeared twice */
G_OPTION_ERROR_DUPLICATE
} GOptionError;
typedef enum
{
G_OPTION_SINGLE_DASH = 1 << 0, /* allow single-dash long arg */
G_OPTION_NO_DOUBLE_DASH = 1 << 1, /* don't allow double-dash long arg,
* only useful for legacy code
*/
G_OPTION_UNDOCUMENTED = 1 << 2, /* don't include the option in
* help output
*/
G_OPTION_ARG_OPTIONAL = 1 << 3, /* OK if the int/string/etc. arg
* is not present. default to
* 0/0.0/NULL
*/
G_OPTION_ARG_REQUIRED = 1 << 4, /* Force arg to be present;
* mutually exclusive with ARG_OPTIONAL
*/
G_OPTION_ONLY_DOCUMENT = 1 << 5, /* Don't parse this option,
* but do put it in the help output.
* Useful if you intend to parse the
* option yourself.
*/
G_OPTION_NO_DUPLICATES = 1 << 6 /* Set error if this option appears
* more than once
*/
} GOptionFlags;
typedef struct _GOptionName GOptionName;
struct _GOptionName
{
const gchar *long_name;
gchar short_name;
};
typedef enum
{
/* Force POSIX (no options after args). POSIX is normally used
* anyway if POSIXLY_CORRECT or POSIX_ME_HARDER env
* variables are set.
*/
G_OPTION_POSIX = 1 << 0,
/* Print errors to stderr */
G_OPTION_PRINT_ERRORS = 1 << 1,
/* Exit with a message on error */
G_OPTION_FATAL_ERRORS = 1 << 2,
/* Don't allow non-option arguments in argv (set error if found) */
G_OPTION_OPTIONS_ONLY = 1 << 3,
/* Allow unknown options without setting error */
G_OPTION_ALLOW_UNKNOWN_OPTIONS = 1 << 4,
/* Strip out options that we understood (not unknown options) */
G_OPTION_STRIP = 1 << 5
} GOptionParseFlags;
typedef void (* GOptionPrePostCallback) (GOptionParser *parser,
gpointer user_data);
typedef void (* GOptionCallback) (GOptionParser *parser,
const gchar *long_name,
gchar short_name,
const gchar *option_arg,
gpointer user_data,
GError **error);
typedef void (* GOptionArgCallback) (GOptionParser *parser,
const gchar *option_arg,
gpointer user_data,
GError **error);
GOptionParser* g_option_parser_new (void);
void g_option_parser_ref (GOptionParser *parser);
void g_option_parser_unref (GOptionParser *parser);
void g_option_parser_set_arg_callback (GOptionParser *parser,
GOptionArgCallback callback,
gpointer user_data);
void g_option_parser_push_table_name (GOptionParser *parser,
const gchar *table_name);
void g_option_parser_pop_table_name (GOptionParser *parser);
void g_option_parser_push_translation_domain (GOptionParser *parser,
const gchar *domain);
void g_option_parser_pop_translation_domain (GOptionParser *parser);
void g_option_parser_add_conflict_set (GOptionParser *parser,
const GOptionName *options_that_conflict,
gboolean is_static);
/* Of course we need to use GClosure, but it's in libgobject */
void g_option_parser_add_alias (GOptionParser *parser,
/* FIXME */
/* Instead of using the
* long/short pair to refer to
* an option, we could use a
* single string "--foo", "-f",
* etc. Could even do this
* in GOptionTemplate, and allow
* as many names as you want in an
* array of names there.
*/
const gchar *alias_name,
gchar alias_short_name,
GOptionFlags flags,
const gchar *target_long_name,
gchar target_short_name);
void g_option_parser_add_option (GOptionParser *parser,
const gchar *long_name,
gchar short_name,
GOptionFlags flags,
GOptionCallback callback,
gpointer user_data,
const gchar *description,
const gchar *arg_description,
gboolean is_static);
void g_option_parser_add_pre_callback (GOptionParser *parser,
GOptionPrePostCallback callback,
gpointer user_data);
void g_option_parser_add_post_callback (GOptionParser *parser,
GOptionPrePostCallback callback,
gpointer user_data);
/* If argc is NULL, assume argv is NULL-terminated.
*
* argv gets shrunk if G_OPTION_STRIP is specified and options are
* seen
* (If you dynamically allocated argv, you probably need to save
* a non-shrunk copy of it to free...)
*
* If args is non-NULL, return non-option args, if flags
* allow those
*
* If unknown_options is non-NULL, return unknown option args,
* if flags allow those
*
* If G_OPTION_STRIP is given, then unknown options and args
* are left in argc/argv, instead of separated and put in
* args and unknown_args. So args/unknown_options should always
* be NULL if you specify G_OPTION_STRIP.
*/
gboolean g_option_parser_parse (GOptionParser *parser,
GOptionParseFlags flags,
gint *argc,
gchar ***argv,
gchar ***args,
gchar ***unknown_options,
GError **err);
/* long help text */
gchar* g_option_parser_get_help (GOptionParser *parser);
/* short usage string */
gchar* g_option_parser_get_usage (GOptionParser *parser);
/* Add default help options table (-?, --help, --usage) */
void g_option_parser_add_help (GOptionParser *parser);
/* Convenience template stuff */
typedef enum
{
G_OPTION_TERMINATOR, /* terminate the option table */
G_OPTION_CALLBACK, /* type_data is a GOptionCallback to
* use for this GOption table.
* user_data is callback user data.
*/
G_OPTION_PRE_CALLBACK, /* type_data is a GOptionPrePostCallback to
* invoke before parsing options.
* user_data is callback user data.
*/
G_OPTION_POST_CALLBACK, /* type_data is a GOptionPrePostCallback to
* invoke after parsing options.
* user_data is callback user data.
*/
G_OPTION_TRANSLATION_DOMAIN, /* type_data is a const char *
* translation domain
*/
G_OPTION_ALIAS, /* type_data is a string name of an option
* this option is an alias for. All other
* elements of this GOption are ignored,
* it's as if the other option had been
* seen. If the string contains a single
* char, it's assumed to be a short name,
* otherwise a long name.
*/
/* The rest of these fill *type_data with some value.
* If type_data is NULL, then they don't. A standard on/off switch
* option is G_OPTION_BOOL.
*/
G_OPTION_DATA, /* type_data is a gpointer * to fill with user_data */
G_OPTION_BOOLEAN, /* type_data is a gboolean * to fill with TRUE if
* the option is seen
*/
G_OPTION_STRING, /* type_data is a gchar ** to fill with string
* value of the option
*/
G_OPTION_INT, /* type_data is an int * to fill with int value
* of the option
*/
G_OPTION_LONG, /* type_data is a glong * to fill with long value
* of the option
*/
G_OPTION_DOUBLE /* type_data is a gdouble * to fill with float value
* of the option
*/
} GOptionType;
typedef struct _GOptionTemplate GOptionTemplate;
struct _GOptionTemplate
{
GOptionType type;
const gchar *long_name;
gchar short_name;
gpointer type_data; /* depends on GOptionType */
gpointer user_data;
const gchar *description;
const gchar *argument_description;
GOptionFlags flags;
};
void g_option_parser_add_auto_option (GOptionParser *parser,
const gchar *long_name,
gchar short_name,
GOptionType type,
GOptionFlags flags,
gpointer address_of_value,
const gchar *description,
const gchar *arg_description,
gboolean is_static);
void g_option_parser_add_table (GOptionParser *parser,
const GOptionTemplate *option_table,
const gchar *table_name,
/* Don't copy any of the fields */
gboolean is_static);
/* If you just have a simple program with a single table, you can use
* this. If you need flexible, do more typing. ;-) Automatically does
* g_option_parser_add_help()
*/
void g_option_parse_simple (const gchar *table_name,
const GOptionTemplate *table,
GOptionParseFlags flags,
gint *argc,
gchar ***argv,
gchar ***args,
GError **err);
#include "gargparser.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define _(x) x
typedef struct _GOptionEntry GOptionEntry;
struct _GOptionParser
{
guint ref_count;
GSList *pre_callbacks;
GSList *post_callbacks;
GSList *options;
/* We optimize speed/mem for building the parser, and let actually
* parsing options be slow, since there will only be a few options
* on the average command line. That's why there's no hash table
* here as you might expect.
*/
GArray *entries;
GOptionArgCallback arg_callback;
gpointer arg_callback_data;
};
GOptionParser*
g_option_parser_new (void)
{
GOptionParser *parser;
parser = g_new (GOptionParser, 1);
parser->ref_count = 1;
parser->options = NULL;
return parser;
}
void
g_option_parser_ref (GOptionParser *parser)
{
g_return_if_fail (parser->ref_count > 0);
parser->ref_count += 1;
}
void
g_option_parser_unref (GOptionParser *parser)
{
g_return_if_fail (parser->ref_count > 0);
parser->ref_count -= 1;
if (parser->ref_count == 0)
{
/* FIXME */
}
}
struct _GOptionEntry
{
GOptionEntry *alias_target;
gchar *long_name;
GOptionCallback callback;
gpointer type_data;
gpointer user_data;
GOptionFlags flags;
gchar *description;
gchar *arg_description;
gchar short_name;
guint is_static : 1;
guint already_seen : 1;
};
void
g_option_parser_add_option (GOptionParser *parser,
const gchar *long_name,
gchar short_name,
GOptionFlags flags,
GOptionCallback callback,
gpointer user_data,
const gchar *description,
const gchar *arg_description,
gboolean is_static)
{
GOptionEntry entry = {
NULL,
NULL,
NULL,
NULL,
NULL,
0,
NULL,
NULL,
'\0',
FALSE,
FALSE
};
g_return_if_fail (parser != NULL);
if (parser->entries == NULL)
parser->entries = g_array_new (FALSE, TRUE, sizeof (GOptionEntry));
entry.flags = flags;
entry.long_name = is_static ? long_name : g_strdup (long_name);
entry.short_name = short_name;
entry.callback = callback;
entry.user_data = user_data;
entry.description = is_static ? description : g_strdup (description);
entry.arg_description = is_static ? arg_description : g_strdup (arg_description);
entry.is_static = is_static;
g_array_append_val (parser->entries, entry);
}
/* in lookups, return NULL if the entry is G_OPTION_ONLY_DOCUMENT */
static GOptionEntry*
resolve_alias (GOptionParser *parser,
GOptionEntry *entry)
{
if (entry->alias_target)
return resolve_alias (parser, entry->alias_target);
else if ((entry->flags & G_OPTION_ONLY_DOCUMENT) != 0)
return NULL;
else
return entry;
}
static GOptionEntry*
g_option_parser_lookup_single (GOptionParser *parser,
const gchar *arg)
{
gint i;
if (parser->entries == NULL)
return NULL;
i = 0;
while (i < parser->entries->len)
{
GOptionEntry *entry = & g_array_index (parser->entries,
GOptionEntry,
i);
if ((entry->flags & G_OPTION_SINGLE_DASH) != 0 &&
entry->long_name && strcmp (entry->long_name, arg))
return resolve_alias (parser, entry);
++i;
}
return NULL;
}
static GOptionEntry*
g_option_parser_lookup_double (GOptionParser *parser,
const gchar *arg)
{
gint i;
if (parser->entries == NULL)
return NULL;
i = 0;
while (i < parser->entries->len)
{
GOptionEntry *entry = & g_array_index (parser->entries,
GOptionEntry,
i);
if ((entry->flags & G_OPTION_NO_DOUBLE_DASH) == 0 &&
entry->long_name && strcmp (entry->long_name, arg))
return resolve_alias (parser, entry);
++i;
}
return NULL;
}
static GOptionEntry*
g_option_parser_lookup_short (GOptionParser *parser,
gchar arg)
{
gint i;
if (parser->entries == NULL)
return NULL;
i = 0;
while (i < parser->entries->len)
{
GOptionEntry *entry = & g_array_index (parser->entries,
GOptionEntry,
i);
if (entry->short_name && entry->short_name == arg)
return resolve_alias (parser, entry);
++i;
}
return NULL;
}
typedef enum
{
/* next arg is a required value for this option */
OPTION_ARG_NEXT_REQUIRED,
/* next arg is a value for this option, if it exists */
OPTION_ARG_NEXT_OPTIONAL,
/* we already have an arg */
OPTION_ARG_PROVIDED,
/* no arg */
OPTION_ARG_NONE
} OptionArgType;
static GSList*
find_options (GOptionParser *parser,
const gchar *arg,
GOptionFlags flags,
/* Option text we didn't understand */
gchar **unhandled,
OptionArgType *arg_type,
gchar **option_arg,
GError **err)
{
GOptionEntry *entry;
const gchar *arg_delim;
gchar *lookup;
GSList *entries = NULL;
g_assert (arg[0] == '-');
g_assert (arg[1] != '\0'); /* should not have treated '-' as an option */
*arg_type = OPTION_ARG_NONE;
arg_delim = strchr (arg, '=');
if (arg_delim == NULL)
arg_delim = strchr (arg, ' '); /* not likely from the shell */
if (arg_delim)
lookup = g_strndup (arg, arg_delim - arg);
else
lookup = g_strdup (arg);
if (unhandled)
*unhandled = NULL;
if (arg[1] == '-')
{
g_assert(arg[2] != '\0'); /* we already filtered "--" by itself */
/* Check for long arg */
entry = g_option_parser_lookup_double (parser, lookup);
if (entry == NULL)
{
if (unhandled)
*unhandled = g_strdup (arg);
goto out;
}
else
{
entries = g_slist_prepend (entries, entry);
if (arg_delim == NULL)
{
if ((entry->flags & G_OPTION_ARG_OPTIONAL) != 0)
*arg_type = OPTION_ARG_NEXT_OPTIONAL;
else if ((entry->flags & G_OPTION_ARG_REQUIRED) != 0)
*arg_type = OPTION_ARG_NEXT_REQUIRED;
}
goto out;
}
}
else
{
/* First try it as a single-dash long option, then as a collection
* of single char options
*/
gint i;
gint n_leftover = 0;
gchar arg_option = '\0';
entry = g_option_parser_lookup_single (parser, &arg[1]);
if (entry)
{
entries = g_slist_prepend (entries, entry);
if (arg_delim == NULL)
{
if ((entry->flags & G_OPTION_ARG_OPTIONAL) != 0)
*arg_type = OPTION_ARG_NEXT_OPTIONAL;
else if ((entry->flags & G_OPTION_ARG_REQUIRED) != 0)
*arg_type = OPTION_ARG_NEXT_REQUIRED;
}
goto out;
}
i = 1;
while (arg[i] && arg != arg_delim)
{
entry = g_option_parser_lookup_short (parser, arg[i]);
if (entry)
{
entries = g_slist_prepend (entries, entry);
if ((entry->flags & (G_OPTION_ARG_OPTIONAL | G_OPTION_ARG_REQUIRED)) != 0)
arg_option = arg[i];
if (arg_delim == NULL)
{
if ((entry->flags & G_OPTION_ARG_OPTIONAL) != 0)
*arg_type = OPTION_ARG_NEXT_OPTIONAL;
else if ((entry->flags & G_OPTION_ARG_REQUIRED) != 0)
*arg_type = OPTION_ARG_NEXT_REQUIRED;
}
}
else
{
if (unhandled)
{
++n_leftover;
/* - plus options plus nul */
*unhandled = g_realloc (*unhandled, n_leftover + 2);
(*unhandled)[0] = '-';
(*unhandled)[n_leftover + 1] = '\0';
(*unhandled)[n_leftover] = arg[i];
}
/* This has the somewhat odd effect that we can strip
* "-cgfqa" to "-cq" or something, and then someone
* after us could parse "-cq" as a long option called "cq",
* but since that case involves multiple levels of crack
* smoking we aren't going to try to work around it
*/
}
++i;
}
/* For short options, we don't allow arguments if multiple
* short options are merged in a block
*/
if (arg_option != '\0' && g_slist_length (entries) > 1)
{
if (unhandled)
{
g_free (*unhandled);
*unhandled = g_strdup (arg);
}
g_slist_free (entries);
entries = NULL;
g_set_error (err,
G_OPTION_ERROR,
G_OPTION_ERROR_BAD_ARG,
_("Option '%c' can have an argument, which means it can't be mixed in a block of options such as '%s' - specify it by itself, i.e. '-%c'"),
arg_option, lookup, arg_option);
}
goto out;
}
out:
if (entries && arg_delim)
{
*option_arg = g_strdup (arg_delim + 1);
*arg_type = OPTION_ARG_PROVIDED;
}
g_free (lookup);
return entries;
}
static gboolean
process_option (GOptionParser *parser,
GOptionEntry *entry,
const gchar *option_arg,
GError **error)
{
if (entry->already_seen &&
(entry->flags & G_OPTION_NO_DUPLICATES) != 0)
{
g_set_error (error,
G_OPTION_ERROR,
G_OPTION_ERROR_DUPLICATE,
_("Option 'FIXME' appears twice"));
return FALSE;
}
if (entry->callback)
{
(* entry->callback) (parser,
entry->long_name,
entry->short_name,
option_arg,
entry->user_data,
error);
if (error && *error)
return FALSE;
}
return TRUE;
}
static void
strip_null_args (gint *argc,
gchar ***argv)
{
gint i, j, k;
for (i = 1; i < *argc; i++)
{
for (k = i; k < *argc; k++)
if ((*argv)[k] != NULL)
break;
if (k > i)
{
k -= i;
for (j = i + k; j < *argc; j++)
(*argv)[j-k] = (*argv)[j];
*argc -= k;
}
}
}
gboolean
g_option_parser_parse (GOptionParser *parser,
GOptionParseFlags flags,
gint *argc,
gchar ***argv,
gchar ***args,
gchar ***unknown_options,
GError **err)
{
GSList *arg_list = NULL;
GSList *unknowns = NULL;
gint i;
gboolean allow_options = TRUE;
GError *real_err = NULL;
gint real_argc;
gchar **stripped_argv;
g_return_val_if_fail (parser != NULL, FALSE);
g_return_val_if_fail (argv != NULL, FALSE);
/* FIXME set all already_seen flags to FALSE */
/* FIXME call all pre callbacks */
if (argc == NULL)
{
i = 0;
while (argv[i])
++i;
real_argc = i;
}
else
real_argc = *argc;
/* We strip a copy of argv, then copy it back
* to the real argv, so that we leave argv
* untouched if errors occur.
*/
if ((flags & G_OPTION_STRIP) != 0)
stripped_argv = g_new0 (gchar*, real_argc);
else
stripped_argv = NULL;
i = 0;
while (i < real_argc)
{
gchar *arg = (*argv)[i];
if (allow_options && strcmp (arg, "--") == 0)
{
allow_options = FALSE;
stripped_argv[i] = NULL;
}
else if (allow_options && arg[0] == '-' &&
arg[1] != '\0')
{
GSList *entries;
gchar *unhandled = NULL;
OptionArgType arg_type;
gchar *option_arg = NULL;
entries = find_options (parser, arg,
flags,
&unhandled,
&arg_type,
&option_arg,
&real_err);
/* put this here immediately so it will get
* freed
*/
if (stripped_argv)
stripped_argv[i] = unhandled;
if (real_err)
{
g_free (option_arg);
g_slist_free (entries);
goto error;
}
if (unhandled &&
(flags & G_OPTION_ALLOW_UNKNOWN_OPTIONS) == 0)
{
g_set_error (&real_err,
G_OPTION_ERROR,
G_OPTION_ERROR_UNKNOWN_OPTION,
_("%s: unknown command line option '%s'"),
g_get_prgname (), unhandled);
g_free (option_arg);
g_slist_free (entries);
goto error;
}
if (arg_type == OPTION_ARG_NEXT_REQUIRED ||
arg_type == OPTION_ARG_NEXT_OPTIONAL)
{
g_assert (option_arg == NULL);
if ((i + 1) < real_argc)
{
option_arg = g_strdup ((*argv)[i + 1]);
++i; /* eat the next arg */
}
else if (arg_type == OPTION_ARG_NEXT_REQUIRED)
{
g_set_error (&real_err,
G_OPTION_ERROR,
G_OPTION_ERROR_MISSING_ARG,
_("%s: command line option '%s' requires an argument"),
g_get_prgname (), arg);
g_slist_free (entries);
goto error;
}
}
if (unhandled)
unknowns = g_slist_prepend (unknowns, g_strdup (unhandled));
if (entries)
{
GSList *tmp_list;
tmp_list = entries;
while (tmp_list != NULL)
{
if (!process_option (parser, tmp_list->data,
option_arg, &real_err))
{
g_free (option_arg);
g_slist_free (entries);
goto error;
}
tmp_list = g_slist_next (tmp_list);
}
}
}
else
{
if ((flags & G_OPTION_POSIX) != 0)
allow_options = FALSE; /* no options after args */
if ((flags & G_OPTION_OPTIONS_ONLY) != 0)
{
g_set_error (&real_err,
G_OPTION_ERROR,
G_OPTION_ERROR_EXTRA_ARGS,
_("%s: command line arguments are not allowed; problem argument is '%s'"),
g_get_prgname (), arg);
goto error;
}
if (parser->arg_callback)
(* parser->arg_callback) (parser,
arg,
parser->arg_callback_data,
&real_err);
if (real_err != NULL)
goto error;
if (args)
arg_list = g_slist_prepend (arg_list, arg);
}
++i;
}
if (stripped_argv)
{
i = 0;
while (i < real_argc)
{
if (stripped_argv[i])
{
g_assert (strlen ((*argv)[i]) <= strlen (stripped_argv[i]));
strcpy ((*argv)[i], stripped_argv[i]);
}
else
argv[i] = NULL;
g_free (stripped_argv[i]);
++i;
}
strip_null_args (argc, argv);
g_free (stripped_argv);
}
/* FIXME call all post callbacks */
if (args)
{
arg_list = g_slist_reverse (arg_list);
/* Fill in vector */
}
if (unknown_options)
{
unknowns = g_slist_reverse (unknowns);
/* Fill in vector */
}
return TRUE;
error:
g_assert (real_err != NULL);
g_slist_free (arg_list);
g_slist_foreach (unknowns, (GFunc) g_free, NULL);
g_slist_free (unknowns);
if (stripped_argv)
{
i = 0;
while (i < real_argc)
{
g_free (stripped_argv[i]);
++i;
}
g_free (stripped_argv);
}
if ((flags & G_OPTION_FATAL_ERRORS) != 0)
{
fprintf (stderr, "%s\n", real_err->message);
exit (1);
}
else if ((flags & G_OPTION_PRINT_ERRORS) != 0)
{
fprintf (stderr, "%s\n", real_err->message);
}
g_propagate_error (err, real_err);
return FALSE;
}
GQuark
g_option_error_quark (void)
{
static GQuark eq = 0;
if (eq == 0)
eq = g_quark_from_static_string ("g-option-error-quark");
return eq;
}
static void
auto_callback_BOOLEAN (GOptionParser *parser,
const gchar *long_name,
gchar short_name,
const gchar *option_arg,
gpointer user_data,
GError **error)
{
gint i;
gboolean *retloc;
static const gchar *true_vals[] = { "1", "yes", "true", "y", "t" };
static const gchar *false_vals[] = { "0", "no", "false", "n", "f" };
retloc = user_data;
i = 0;
while (i < G_N_ELEMENTS (true_vals))
{
if (g_strcasecmp (option_arg, true_vals[i]) == 0)
{
*retloc = TRUE;
return;
}
++i;
}
i = 0;
while (i < G_N_ELEMENTS (false_vals))
{
if (g_strcasecmp (option_arg, false_vals[i]) == 0)
{
*retloc = FALSE;
return;
}
++i;
}
parse_error:
g_set_error (error,
G_OPTION_ERROR,
G_OPTION_ERROR_BAD_ARG,
_("%s: '%s' is not a valid boolean value"),
g_get_prgname (), option_arg);
}
static void
auto_callback_STRING (GOptionParser *parser,
const gchar *long_name,
gchar short_name,
const gchar *option_arg,
gpointer user_data,
GError **error)
{
/* FIXME how does memory allocation work for the returned string? */
parse_error:
g_set_error (error,
G_OPTION_ERROR,
G_OPTION_ERROR_BAD_ARG,
_("%s: '%s' is not a valid value"),
g_get_prgname (), option_arg);
}
static void
auto_callback_INT (GOptionParser *parser,
const gchar *long_name,
gchar short_name,
const gchar *option_arg,
gpointer user_data,
GError **error)
{
gchar *end = NULL;
glong val;
gint *retloc = user_data;
errno = 0;
val = strtol (option_arg, &end, 10);
if (end == option_arg || errno == ERANGE)
goto parse_error;
if (val < (glong) - G_MAXINT || val > (glong) G_MAXINT)
goto parse_error;
*retloc = (gint) val;
return;
parse_error:
g_set_error (error,
G_OPTION_ERROR,
G_OPTION_ERROR_BAD_ARG,
_("%s: '%s' is not a valid integer value"),
g_get_prgname (), option_arg);
}
static void
auto_callback_LONG (GOptionParser *parser,
const gchar *long_name,
gchar short_name,
const gchar *option_arg,
gpointer user_data,
GError **error)
{
gchar *end = NULL;
glong val;
glong *retloc = user_data;
errno = 0;
val = strtol (option_arg, &end, 10);
if (end == option_arg || errno == ERANGE)
goto parse_error;
*retloc = val;
return;
parse_error:
g_set_error (error,
G_OPTION_ERROR,
G_OPTION_ERROR_BAD_ARG,
_("%s: '%s' is not a valid integer value"),
g_get_prgname (), option_arg);
}
static void
auto_callback_DOUBLE (GOptionParser *parser,
const gchar *long_name,
gchar short_name,
const gchar *option_arg,
gpointer user_data,
GError **error)
{
gchar *end = NULL;
gdouble val;
gdouble *retloc = user_data;
errno = 0;
val = strtod (option_arg, &end);
if (end == option_arg || errno == ERANGE)
goto parse_error;
*retloc = val;
return;
parse_error:
g_set_error (error,
G_OPTION_ERROR,
G_OPTION_ERROR_BAD_ARG,
_("%s: '%s' is not a valid floating point value"),
g_get_prgname (), option_arg);
}
void
g_option_parser_add_auto_option (GOptionParser *parser,
const gchar *long_name,
gchar short_name,
GOptionType type,
GOptionFlags flags,
gpointer address_of_value,
const gchar *description,
const gchar *arg_description,
gboolean is_static)
{
GOptionCallback callback = NULL;
switch (type)
{
case G_OPTION_BOOLEAN:
callback = auto_callback_BOOLEAN;
break;
case G_OPTION_STRING:
callback = auto_callback_STRING;
break;
case G_OPTION_INT:
callback = auto_callback_INT;
break;
case G_OPTION_LONG:
callback = auto_callback_LONG;
break;
case G_OPTION_DOUBLE:
callback = auto_callback_DOUBLE;
break;
default:
g_warning ("%s: only the auto-arg-parser option types are allowed here, e.g. G_OPTION_BOOLEAN or G_OPTION_INT", G_STRLOC);
break;
}
if (callback)
{
g_option_parser_add_option (parser,
long_name,
short_name,
flags,
callback,
address_of_value,
description,
arg_description,
is_static);
}
}
void
g_option_parser_add_table (GOptionParser *parser,
const GOptionTemplate *option_table,
const gchar *table_name,
gboolean is_static)
{
gint i;
g_return_if_fail (parser != NULL);
g_return_if_fail (option_table != NULL);
i = 0;
while (option_table[i].type != G_OPTION_TERMINATOR)
{
const GOptionTemplate *tmpl = &option_table[i];
switch (tmpl->type)
{
case G_OPTION_CALLBACK:
/* FIXME */
break;
case G_OPTION_PRE_CALLBACK:
/* FIXME */
break;
case G_OPTION_POST_CALLBACK:
/* FIXME */
break;
case G_OPTION_TRANSLATION_DOMAIN:
/* FIXME */
break;
case G_OPTION_ALIAS:
{
const gchar *target = tmpl->type_data;
gboolean is_short;
if (target == NULL)
{
g_warning ("Alias target in GOptionTemplate must be non-NULL");
return;
}
is_short = strlen (target) < 2;
g_option_parser_add_alias (parser,
tmpl->long_name,
tmpl->short_name,
tmpl->flags,
is_short ? NULL : target,
is_short ? *target : '\0');
}
break;
case G_OPTION_DATA:
/* FIXME */
break;
case G_OPTION_BOOL:
case G_OPTION_STRING:
case G_OPTION_INT:
case G_OPTION_LONG:
case G_OPTION_DOUBLE:
g_option_parser_add_auto_option (parser,
tmpl->long_name,
tmpl->short_name,
tmpl->type,
tmpl->flags,
tmpl->type_data,
tmpl->description,
tmpl->argument_description,
is_static);
break;
case G_OPTION_TERMINATOR:
g_assert_not_reached ();
break;
}
++i;
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]