Re: arguement parsing in glib
- From: Havoc Pennington <hp redhat com>
- To: Jeff Rose <rosejn Colorado EDU>
- Cc: gtk-devel-list redhat com
- Subject: Re: arguement parsing in glib
- Date: 18 Oct 2000 12:02:13 -0400
Jeff Rose <rosejn Colorado EDU> writes:
> I would like to work on integrating/renaming popt into glib. Its on the
> task list, but I wanted to make sure someone else hasn't already done it.
>
I'm a bit later than I said sending you this, but here are some code
doodles I had related to this. It doesn't compile; in fact the .c file
is total nonsense, half of it works one way and half of it works
another way, I didn't finish rewriting it. But the header might be a
starting point. It is intended to be roughly similar to popt to make
conversion possible.
The design perhaps makes the GnomeUIInfo/popt mistake, keeping the
convenience templates around as data structures. This results in pain
all around; but is an efficiency hack that could be worth it. I don't
know.
I wonder if G_OPTION_STRIP should be an option flag, maybe instead it
should be a flag passed to g_option_parser_parse.
There are no doubt countless other issues, I didn't finish this code.
Havoc
/* 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 */
#define G_OPTION_ERROR g_option_error_quark ()
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
} GOptionError;
typedef enum
{
G_OPTION_TERMINATOR, /* terminate the option table */
G_OPTION_CALLBACK, /* type_data is a GOptionCallbackInfo to
* use for this GOption table.
* user_data is callback user data.
*/
G_OPTION_TRANSLATION_DOMAIN, /* type_data is a const char *
* translation domain
*/
G_OPTION_IGNORED, /* type_data is ignored; this option is valid,
* but not used. The table callback if any is
* not called. For use with e.g. legacy options
* that no longer make sense.
*/
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. If an option has an argument,
* it is passed to the callback as arg_text
*/
G_OPTION_DATA, /* type_data is a gpointer * to fill with GOption::user_data */
G_OPTION_BOOL, /* 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 enum
{
G_OPTION_SINGLE_DASH = 1 << 0, /* allow single-dash long arg */
G_OPTION_UNDOCUMENTED = 1 << 1, /* don't include the option in
* help output
*/
G_OPTION_STRIP = 1 << 2, /* strip the option out of the argv
* vector
*/
G_OPTION_ARG_OPTIONAL = 1 << 3, /* OK if the int/string/etc. arg
* is not present. default to
* 0/0.0/NULL
*/
G_OPTION_CALLBACK_PRE = 1 << 4, /* Invoke callback in preparse phase */
G_OPTION_CALLBACK_POST = 1 << 5 /* Invoke callback in postparse phase */
} GOptionFlags;
typedef struct _GOption GOption;
struct _GOption
{
GOptionType type;
const gchar *long_name;
gunichar short_name;
gpointer type_data; /* depends on GOptionType */
gpointer user_data;
const gchar *description;
const gchar *argument_description;
GOptionFlags flags;
};
typedef enum
{
/* Force POSIX (no options after args). POSIX is normally used
* anyway if POSIXLY_CORRECT or POSIX_ME_HARDER are set.
*/
G_OPTION_POSIX = 1 << 0,
/* Exit with a message on error */
G_OPTION_FATAL_ERRORS = 1 << 1,
/* Don't allow non-option arguments in argv (set error if found) */
G_OPTION_OPTIONS_ONLY = 1 << 2,
/* Allow unknown options without setting error */
G_OPTION_ALLOW_UNKNOWN_OPTIONS = 1 << 3
} GOptionParseFlags;
typedef enum
{
/* Before parsing any options */
G_OPTION_PHASE_PRE = 1 << 0,
/* After parsing all options */
G_OPTION_PHASE_POST = 1 << 1,
/* Callback invoked on an option, while parsing
* options.
*/
G_OPTION_PHASE_DURING = 1 << 2
} GOptionParsePhase;
typedef void (* GOptionCallback) (GOptionParser *parser,
GOptionParsePhase phase,
const GOption *option,
const char *arg_text,
/* user data from the callback's GOption */
gpointer callback_data,
GError **error);
/* 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;
GOptionParser* g_option_parser_new (void);
void g_option_parser_ref (GOptionParser *parser);
void g_option_parser_unref (GOptionParser *parser);
/* If is_static, the table and all its contents are assumed to be
* static. "static" just means "will last longer than the arg parser
* so don't bother making a copy"
*/
GOptionTable* g_option_parser_add_table (GOptionParser *parser,
const GOption *option_table,
const gchar *table_name,
gboolean is_static);
/* If argc is NULL, assume argv is NULL-terminated.
*
* argv gets shrunk if G_OPTION_STRIP options are seen and removed.
* (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 unknown_options is non-NULL, return unknown option args
*/
gboolean g_option_parser_parse (GOptionParser *parser,
GOptionParseFlags flags,
gint *argc,
gchar ***argv,
gchar ***args,
gchar ***unknown_options,
GError **err);
/* If you just have a simple program with a single table,
* you can use this. Prints usage/help and exits if there's
* an error; if you need flexible, do more typing. ;-)
* Automatically does g_option_parser_add_help()
*/
void g_option_parse_simple (const gchar *table_name,
const GOption *table,
gint *argc,
gchar ***argv,
gchar ***args);
/* 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 */
GOptionTable* g_option_parser_add_help (GOptionParser *parser);
#include "gargparser.h"
typedef struct _GOptionEntry GOptionEntry;
typedef struct _GOptionTable GOptionTable;
static GOptionTable* table_new (const gchar *name);
static GOptionEntry* table_add_option (GOptionTable *table,
gboolean free_option,
gchar *translation_domain,
const GOption *callback,
GOption *single_option);
/* these can cast off const, sort of lame, but hey */
static void table_add_domain (GOptionTable *table,
gboolean make_copy,
const gchar *domain,
gchar **out);
static void parser_add_option (GOptionParser *parser,
GOptionTable *table,
gboolean free_option,
gchar *translation_domain,
const GOption *callback,
GOption *single_option);
struct _GOptionEntry
{
GOption *option;
const GOption *callback_option;
const gchar *translation_domain;
guint free_option : 1;
};
struct _GOptionTable
{
gchar *name;
/* Stored here so we can free them. */
GSList *nonstatic_domains;
GSList *entries;
};
struct _GOptionParser
{
guint ref_count;
GSList *tables;
GHashTable *long_options;
GHashTable *short_options;
};
static gint
g_unichar_equal (gconstpointer v1,
gconstpointer v2)
{
return *((const gunichar*) v1) == *((const gunichar*) v2);
}
static guint
g_unichar_hash (gconstpointer v)
{
return *(const gunichar*) v;
}
static gchar*
g_unichar_to_string (gunichar c, gchar *buf)
{
gint len = g_unichar_to_utf8 (c, buf);
buf[len] = '\0';
return buf;
}
GOptionParser*
g_option_parser_new (void)
{
GOptionParser *parser;
parser = g_new (GOptionParser, 1);
parser->ref_count = 1;
parser->tables = g_hash_table_new (g_str_hash, g_str_equal);
parser->long_options = g_hash_table_new (g_str_hash, g_str_equal);
parser->short_options = g_hash_table_new (g_unichar_hash, g_unichar_equal);
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 */
}
}
static void
parser_add_option (GOptionParser *parser,
GOptionTable *table,
gboolean free_option,
gchar *translation_domain,
const GOption *callback_option,
GOption *single_option)
{
GOptionEntry *entry;
entry = table_add_option (table,
free_option,
translation_domain,
callback_option,
single_option);
if (entry->option->long_name)
{
#ifndef G_DISABLE_CHECKS
if (g_hash_table_lookup (parser->long_options,
entry->option->long_name) != NULL)
g_warning ("Option '%s' added twice", entry->option->long_name);
#endif
g_hash_table_insert (parser->long_options,
entry->option->long_name,
entry);
}
if (entry->option->short_name)
{
#ifndef G_DISABLE_CHECKS
gchar buf[7];
if (g_hash_table_lookup (parser->short_options,
&entry->option->short_name) != NULL)
g_warning ("Option '%s' added twice",
g_unichar_to_string (entry->option->short_name, buf));
#endif
g_hash_table_insert (parser->short_options,
&entry->option->short_name,
entry);
}
}
GOptionTable*
g_option_parser_add_table (GOptionParser *parser,
const GOption *option_table,
const gchar *table_name,
gboolean is_static)
{
const GOption *current_callback = NULL;
gchar *current_domain = NULL;
const GOption *current_option = NULL;
GOptionTable *table;
g_return_val_if_fail (parser != NULL, NULL);
g_return_val_if_fail (table_name != NULL, NULL);
table = table_new (table_name);
parser->tables = g_slist_append (parser->tables, table);
if (option_table)
{
current_option = option_table;
while (current_option->type != G_OPTION_TERMINATOR)
{
switch (option->type)
{
case G_OPTION_TRANSLATION_DOMAIN:
table_add_domain (table, !is_static,
current_option->type_data,
¤t_domain);
break;
case G_OPTION_CALLBACK:
/* FIXME must copy callback if necessary */
g_warning ("FIXME");
current_callback = current_option;
break;
default:
parser_add_option (parser,
table,
!is_static,
current_domain,
current_callback,
current_option);
break;
}
++current_option;
}
}
}
gboolean
g_option_parser_parse (GOptionParser *parser,
GOptionParseFlags flags,
gint *argc,
gchar ***argv,
gchar ***args,
gchar ***unknown_options,
GError **err)
{
}
void
g_option_parse_simple (const gchar *table_name,
const GOption *table,
gint *argc,
gchar ***argv,
gchar ***args)
{
}
gchar*
g_option_parser_get_help (GOptionParser *parser)
{
}
gchar*
g_option_parser_get_usage (GOptionParser *parser)
{
}
enum
{
OPTION_HELP,
OPTION_USAGE
};
static void
help_callback (GOptionParser *parser,
GOptionParsePhase phase,
const GOption *option,
const char *arg_text,
gpointer callback_data,
GError **error)
{
switch (GPOINTER_TO_INT (option->user_data))
{
case OPTION_HELP:
break;
case OPTION_USAGE:
break;
default:
g_assert_not_reached ();
break;
}
}
static GOption help_options [] =
{
{ G_OPTION_TRANSLATION_DOMAIN,
NULL, '\0',
PACKAGE, NULL,
NULL, NULL, 0 },
{ G_OPTION_CALLBACK,
NULL, '\0',
&help_callback, NULL,
NULL, NULL, 0 },
{ G_OPTION_BOOL,
"help", '?',
NULL, GINT_TO_POINTER (OPTION_HELP),
N_("Show this help message"), NULL, 0 },
/* FIXME this is just for testing, we don't really
* want to set up -h I don't think. (Or maybe popt
* just skips -h for RPM's benefit, and we should
* set up -h, I don't know)
*/
{ G_OPTION_ALIAS,
NULL, 'h',
"help", NULL,
NULL, NULL, 0 },
{ G_OPTION_BOOL,
"usage", '\0',
NULL, GINT_TO_POINTER (OPTION_USAGE),
N_("Display brief usage message"), NULL, 0 }
{ G_OPTION_TERMINATOR,
NULL, '\0',
NULL, NULL,
NULL, NULL, 0 }
};
GOptionTable*
g_option_parser_add_help (GOptionParser *parser)
{
g_return_val_if_fail (parser != NULL, NULL);
return g_option_parser_add_table (parser,
help_options,
_("Help options"),
TRUE);
}
static GOptionTable*
table_new (const gchar *name)
{
GOptionTable *table;
table = g_new0 (GOptionTable, 1);
table->name = g_strdup (name);
return table;
}
static GOptionEntry*
table_add_option (GOptionTable *table,
gboolean free_option,
gchar *translation_domain,
const GOption *callback,
GOption *single_option)
{
GOptionEntry *entry = g_new (GOptionEntry, 1);
table->entries = g_slist_prepend (table->entries,
entry);
entry->free_option = free_option;
entry->option = single_option;
entry->callback_option = callback;
entry->translation_domain = translation_domain;
return entry;
}
static void
table_add_domain (GOptionTable *table,
gboolean make_copy,
const gchar *domain,
gchar **out)
{
if (!make_copy)
{
*out = (gchar*) domain;
}
else
{
if (table->nonstatic_domains &&
strcmp (table->nonstatic_domains->data, domain) == 0)
; /* We'll just recycle the last one - simple optimization
* for a common case.
*/
else
{
table->nonstatic_domains =
g_slist_prepend (table->nonstatic_domains,
g_strdup (domain));
}
*out = table->nonstatic_domains->data;
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]