X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=glib%2Fgoption.c;h=6438281200d255758b6feefd79b721db479a7245;hb=49a5d0f6f2aed99cd78f25655f137f4448e47d92;hp=8b5139ad0888fb7ab8e5d81c2d23748f3f07ec99;hpb=700b6c4b875c6095660c052913bbdcb40f58ce10;p=platform%2Fupstream%2Fglib.git diff --git a/glib/goption.c b/glib/goption.c index 8b5139a..6438281 100644 --- a/glib/goption.c +++ b/glib/goption.c @@ -10,27 +10,190 @@ * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library; if not, see . */ -#include "config.h" - -#include "goption.h" -#include "glib.h" -#include "glibintl.h" +/** + * SECTION:option + * @Short_description: parses commandline options + * @Title: Commandline option parser + * + * The GOption commandline parser is intended to be a simpler replacement + * for the popt library. It supports short and long commandline options, + * as shown in the following example: + * + * `testtreemodel -r 1 --max-size 20 --rand --display=:1.0 -vb -- file1 file2` + * + * The example demonstrates a number of features of the GOption + * commandline parser: + * + * - Options can be single letters, prefixed by a single dash. + * + * - Multiple short options can be grouped behind a single dash. + * + * - Long options are prefixed by two consecutive dashes. + * + * - Options can have an extra argument, which can be a number, a string or + * a filename. For long options, the extra argument can be appended with + * an equals sign after the option name, which is useful if the extra + * argument starts with a dash, which would otherwise cause it to be + * interpreted as another option. + * + * - Non-option arguments are returned to the application as rest arguments. + * + * - An argument consisting solely of two dashes turns off further parsing, + * any remaining arguments (even those starting with a dash) are returned + * to the application as rest arguments. + * + * Another important feature of GOption is that it can automatically + * generate nicely formatted help output. Unless it is explicitly turned + * off with g_option_context_set_help_enabled(), GOption will recognize + * the `--help`, `-?`, `--help-all` and `--help-groupname` options + * (where `groupname` is the name of a #GOptionGroup) and write a text + * similar to the one shown in the following example to stdout. + * + * |[ + * Usage: + * testtreemodel [OPTION...] - test tree model performance + * + * Help Options: + * -h, --help Show help options + * --help-all Show all help options + * --help-gtk Show GTK+ Options + * + * Application Options: + * -r, --repeats=N Average over N repetitions + * -m, --max-size=M Test up to 2^M items + * --display=DISPLAY X display to use + * -v, --verbose Be verbose + * -b, --beep Beep when done + * --rand Randomize the data + * ]| + * + * GOption groups options in #GOptionGroups, which makes it easy to + * incorporate options from multiple sources. The intended use for this is + * to let applications collect option groups from the libraries it uses, + * add them to their #GOptionContext, and parse all options by a single call + * to g_option_context_parse(). See gtk_get_option_group() for an example. + * + * If an option is declared to be of type string or filename, GOption takes + * care of converting it to the right encoding; strings are returned in + * UTF-8, filenames are returned in the GLib filename encoding. Note that + * this only works if setlocale() has been called before + * g_option_context_parse(). + * + * Here is a complete example of setting up GOption to parse the example + * commandline above and produce the example help output. + * |[ + * static gint repeats = 2; + * static gint max_size = 8; + * static gboolean verbose = FALSE; + * static gboolean beep = FALSE; + * static gboolean randomize = FALSE; + * + * static GOptionEntry entries[] = + * { + * { "repeats", 'r', 0, G_OPTION_ARG_INT, &repeats, "Average over N repetitions", "N" }, + * { "max-size", 'm', 0, G_OPTION_ARG_INT, &max_size, "Test up to 2^M items", "M" }, + * { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Be verbose", NULL }, + * { "beep", 'b', 0, G_OPTION_ARG_NONE, &beep, "Beep when done", NULL }, + * { "rand", 0, 0, G_OPTION_ARG_NONE, &randomize, "Randomize the data", NULL }, + * { NULL } + * }; + * + * int + * main (int argc, char *argv[]) + * { + * GError *error = NULL; + * GOptionContext *context; + * + * context = g_option_context_new ("- test tree model performance"); + * g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); + * g_option_context_add_group (context, gtk_get_option_group (TRUE)); + * if (!g_option_context_parse (context, &argc, &argv, &error)) + * { + * g_print ("option parsing failed: %s\n", error->message); + * exit (1); + * } + * + * ... + * + * } + * ]| + * + * On UNIX systems, the argv that is passed to main() has no particular + * encoding, even to the extent that different parts of it may have + * different encodings. In general, normal arguments and flags will be + * in the current locale and filenames should be considered to be opaque + * byte strings. Proper use of %G_OPTION_ARG_FILENAME vs + * %G_OPTION_ARG_STRING is therefore important. + * + * Note that on Windows, filenames do have an encoding, but using + * #GOptionContext with the argv as passed to main() will result in a + * program that can only accept commandline arguments with characters + * from the system codepage. This can cause problems when attempting to + * deal with filenames containing Unicode characters that fall outside + * of the codepage. + * + * A solution to this is to use g_win32_get_command_line() and + * g_option_context_parse_strv() which will properly handle full Unicode + * filenames. If you are using #GApplication, this is done + * automatically for you. + * + * The following example shows how you can use #GOptionContext directly + * in order to correctly deal with Unicode filenames on Windows: + * + * |[ + * int + * main (int argc, char **argv) + * { + * GError *error = NULL; + * GOptionContext *context; + * gchar **args; + * + * #ifdef G_OS_WIN32 + * args = g_win32_get_command_line (); + * #else + * args = g_strdupv (argv); + * #endif + * + * // set up context + * + * if (!g_option_context_parse_strv (context, &args, &error)) + * { + * // error happened + * } + * + * ... + * + * g_strfreev (args); + * + * ... + * } + * ]| + */ -#include "galias.h" +#include "config.h" #include #include +#include #include +#if defined __OpenBSD__ +#include +#include +#endif + +#include "goption.h" + +#include "gprintf.h" +#include "glibintl.h" + #define TRANSLATE(group, str) (((group)->translate_func ? (* (group)->translate_func) ((str), (group)->translate_data) : (str))) #define NO_ARG(entry) ((entry)->arg == G_OPTION_ARG_NONE || \ @@ -40,11 +203,11 @@ #define OPTIONAL_ARG(entry) ((entry)->arg == G_OPTION_ARG_CALLBACK && \ (entry)->flags & G_OPTION_FLAG_OPTIONAL_ARG) -typedef struct +typedef struct { GOptionArg arg_type; - gpointer arg_data; - union + gpointer arg_data; + union { gboolean bool; gint integer; @@ -53,10 +216,10 @@ typedef struct gdouble dbl; gint64 int64; } prev; - union + union { gchar *str; - struct + struct { gint len; gchar **data; @@ -80,17 +243,18 @@ struct _GOptionContext GTranslateFunc translate_func; GDestroyNotify translate_notify; - gpointer translate_data; + gpointer translate_data; guint help_enabled : 1; guint ignore_unknown : 1; - + guint strv_mode : 1; + GOptionGroup *main_group; /* We keep a list of change so we can revert them */ GList *changes; - - /* We also keep track of all argv elements + + /* We also keep track of all argv elements * that should be NULLed or modified. */ GList *pending_nulls; @@ -107,7 +271,7 @@ struct _GOptionGroup GTranslateFunc translate_func; GDestroyNotify translate_notify; - gpointer translate_data; + gpointer translate_data; GOptionEntry *entries; gint n_entries; @@ -118,24 +282,49 @@ struct _GOptionGroup }; static void free_changes_list (GOptionContext *context, - gboolean revert); + gboolean revert); static void free_pending_nulls (GOptionContext *context, - gboolean perform_nulls); + gboolean perform_nulls); + + +static int +_g_unichar_get_width (gunichar c) +{ + if (G_UNLIKELY (g_unichar_iszerowidth (c))) + return 0; -GQuark -g_option_error_quark (void) + /* we ignore the fact that we should call g_unichar_iswide_cjk() under + * some locales (legacy East Asian ones) */ + if (g_unichar_iswide (c)) + return 2; + + return 1; +} + +static glong +_g_utf8_strwidth (const gchar *p) { - return g_quark_from_static_string ("g-option-context-error-quark"); + glong len = 0; + g_return_val_if_fail (p != NULL, 0); + + while (*p) + { + len += _g_unichar_get_width (g_utf8_get_char (p)); + p = g_utf8_next_char (p); + } + + return len; } +G_DEFINE_QUARK (g-option-context-error-quark, g_option_error) + /** * g_option_context_new: - * @parameter_string: a string which is displayed in - * the first line of output, after the - * usage summary - * programname [OPTION...] + * @parameter_string: (allow-none): a string which is displayed in + * the first line of `--help` output, after the usage summary + * `programname [OPTION...]` * - * Creates a new option context. + * Creates a new option context. * * The @parameter_string can serve multiple purposes. It can be used * to add descriptions for "rest" arguments, which are not parsed by @@ -151,8 +340,9 @@ g_option_error_quark (void) * program functionality that should be displayed as a paragraph * below the usage line, use g_option_context_set_summary(). * - * Note that the @parameter_string is translated (see - * g_option_context_set_translate_func()). + * Note that the @parameter_string is translated using the + * function set with g_option_context_set_translate_func(), so + * it should normally be passed untranslated. * * Returns: a newly created #GOptionContext, which must be * freed with g_option_context_free() after use. @@ -176,30 +366,32 @@ g_option_context_new (const gchar *parameter_string) /** * g_option_context_free: - * @context: a #GOptionContext + * @context: a #GOptionContext * - * Frees context and all the groups which have been + * Frees context and all the groups which have been * added to it. * + * Please note that parsed arguments need to be freed separately (see + * #GOptionEntry). + * * Since: 2.6 */ -void g_option_context_free (GOptionContext *context) +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); + g_list_free_full (context->groups, (GDestroyNotify) g_option_group_free); - if (context->main_group) + if (context->main_group) g_option_group_free (context->main_group); free_changes_list (context, FALSE); free_pending_nulls (context, FALSE); - + g_free (context->parameter_string); g_free (context->summary); g_free (context->description); - + if (context->translate_notify) (* context->translate_notify) (context->translate_data); @@ -210,13 +402,12 @@ void g_option_context_free (GOptionContext *context) /** * g_option_context_set_help_enabled: * @context: a #GOptionContext - * @help_enabled: %TRUE to enable , %FALSE to disable it + * @help_enabled: %TRUE to enable `--help`, %FALSE to disable it * - * Enables or disables automatic generation of - * output. By default, g_option_context_parse() recognizes - * , , - * and groupname and creates - * suitable output to stdout. + * Enables or disables automatic generation of `--help` output. + * By default, g_option_context_parse() recognizes `--help`, `-h`, + * `-?`, `--help-all` and `--help-groupname` and creates suitable + * output to stdout. * * Since: 2.6 */ @@ -232,19 +423,19 @@ void g_option_context_set_help_enabled (GOptionContext *context, /** * g_option_context_get_help_enabled: * @context: a #GOptionContext - * - * Returns whether automatic generation + * + * Returns whether automatic `--help` generation * is turned on for @context. See g_option_context_set_help_enabled(). - * + * * Returns: %TRUE if automatic help generation is turned on. * * Since: 2.6 */ -gboolean -g_option_context_get_help_enabled (GOptionContext *context) +gboolean +g_option_context_get_help_enabled (GOptionContext *context) { g_return_val_if_fail (context != NULL, FALSE); - + return context->help_enabled; } @@ -253,12 +444,12 @@ g_option_context_get_help_enabled (GOptionContext *context) * @context: a #GOptionContext * @ignore_unknown: %TRUE to ignore unknown options, %FALSE to produce * an error when unknown options are met - * - * Sets whether to ignore unknown options or not. If an argument is - * ignored, it is left in the @argv array after parsing. By default, + * + * Sets whether to ignore unknown options or not. If an argument is + * ignored, it is left in the @argv array after parsing. By default, * g_option_context_parse() treats unknown options as error. - * - * This setting does not affect non-option arguments (i.e. arguments + * + * This setting does not affect non-option arguments (i.e. arguments * which don't start with a dash). But note that GOption cannot reliably * determine whether a non-option belongs to a preceding unknown option. * @@ -266,7 +457,7 @@ g_option_context_get_help_enabled (GOptionContext *context) **/ void g_option_context_set_ignore_unknown_options (GOptionContext *context, - gboolean ignore_unknown) + gboolean ignore_unknown) { g_return_if_fail (context != NULL); @@ -276,12 +467,12 @@ g_option_context_set_ignore_unknown_options (GOptionContext *context, /** * g_option_context_get_ignore_unknown_options: * @context: a #GOptionContext - * + * * Returns whether unknown options are ignored or not. See * g_option_context_set_ignore_unknown_options(). - * + * * Returns: %TRUE if unknown options are ignored. - * + * * Since: 2.6 **/ gboolean @@ -296,7 +487,7 @@ g_option_context_get_ignore_unknown_options (GOptionContext *context) * g_option_context_add_group: * @context: a #GOptionContext * @group: the group to add - * + * * Adds a #GOptionGroup to the @context, so that parsing with @context * will recognize the options in the group. Note that the group will * be freed together with the context when g_option_context_free() is @@ -307,7 +498,7 @@ g_option_context_get_ignore_unknown_options (GOptionContext *context) **/ void g_option_context_add_group (GOptionContext *context, - GOptionGroup *group) + GOptionGroup *group) { GList *list; @@ -322,9 +513,9 @@ g_option_context_add_group (GOptionContext *context, GOptionGroup *g = (GOptionGroup *)list->data; if ((group->name == NULL && g->name == NULL) || - (group->name && g->name && strcmp (group->name, g->name) == 0)) - g_warning ("A group named \"%s\" is already part of this GOptionContext", - group->name); + (group->name && g->name && strcmp (group->name, g->name) == 0)) + g_warning ("A group named \"%s\" is already part of this GOptionContext", + group->name); } context->groups = g_list_append (context->groups, group); @@ -334,17 +525,17 @@ g_option_context_add_group (GOptionContext *context, * g_option_context_set_main_group: * @context: a #GOptionContext * @group: the group to set as main group - * - * Sets a #GOptionGroup as main group of the @context. - * This has the same effect as calling g_option_context_add_group(), - * the only difference is that the options in the main group are - * treated differently when generating output. + * + * Sets a #GOptionGroup as main group of the @context. + * This has the same effect as calling g_option_context_add_group(), + * the only difference is that the options in the main group are + * treated differently when generating `--help` output. * * Since: 2.6 **/ void g_option_context_set_main_group (GOptionContext *context, - GOptionGroup *group) + GOptionGroup *group) { g_return_if_fail (context != NULL); g_return_if_fail (group != NULL); @@ -355,17 +546,17 @@ g_option_context_set_main_group (GOptionContext *context, return; } - + context->main_group = group; } /** * g_option_context_get_main_group: * @context: a #GOptionContext - * + * * Returns a pointer to the main group of @context. - * - * Return value: the main group of @context, or %NULL if @context doesn't + * + * Returns: the main group of @context, or %NULL if @context doesn't * have a main group. Note that group belongs to @context and should * not be modified or freed. * @@ -382,35 +573,37 @@ g_option_context_get_main_group (GOptionContext *context) /** * g_option_context_add_main_entries: * @context: a #GOptionContext - * @entries: a %NULL-terminated array of #GOptionEntrys - * @translation_domain: a translation domain to use for translating - * the output for the options in @entries + * @entries: a %NULL-terminated array of #GOptionEntrys + * @translation_domain: (allow-none): a translation domain to use for translating + * the `--help` output for the options in @entries * with gettext(), or %NULL - * - * A convenience function which creates a main group if it doesn't + * + * A convenience function which creates a main group if it doesn't * exist, adds the @entries to it and sets the translation domain. - * + * * Since: 2.6 **/ void g_option_context_add_main_entries (GOptionContext *context, - const GOptionEntry *entries, - const gchar *translation_domain) + const GOptionEntry *entries, + const gchar *translation_domain) { g_return_if_fail (entries != NULL); if (!context->main_group) context->main_group = g_option_group_new (NULL, NULL, NULL, NULL, NULL); - + g_option_group_add_entries (context->main_group, entries); g_option_group_set_translation_domain (context->main_group, translation_domain); } static gint -calculate_max_length (GOptionGroup *group) +calculate_max_length (GOptionGroup *group, + GHashTable *aliases) { GOptionEntry *entry; gint i, len, max_length; + const gchar *long_name; max_length = 0; @@ -419,16 +612,19 @@ calculate_max_length (GOptionGroup *group) entry = &group->entries[i]; if (entry->flags & G_OPTION_FLAG_HIDDEN) - continue; + continue; + + long_name = g_hash_table_lookup (aliases, &entry->long_name); + if (!long_name) + long_name = entry->long_name; + len = _g_utf8_strwidth (long_name); - len = g_utf8_strlen (entry->long_name, -1); - if (entry->short_name) - len += 4; - + len += 4; + if (!NO_ARG (entry) && entry->arg_description) - len += 1 + g_utf8_strlen (TRANSLATE (group, entry->arg_description), -1); - + len += 1 + _g_utf8_strwidth (TRANSLATE (group, entry->arg_description)); + max_length = MAX (max_length, len); } @@ -437,10 +633,13 @@ calculate_max_length (GOptionGroup *group) static void print_entry (GOptionGroup *group, - gint max_length, - const GOptionEntry *entry) + gint max_length, + const GOptionEntry *entry, + GString *string, + GHashTable *aliases) { GString *str; + const gchar *long_name; if (entry->flags & G_OPTION_FLAG_HIDDEN) return; @@ -448,97 +647,215 @@ print_entry (GOptionGroup *group, if (entry->long_name[0] == 0) return; + long_name = g_hash_table_lookup (aliases, &entry->long_name); + if (!long_name) + long_name = entry->long_name; + str = g_string_new (NULL); - + if (entry->short_name) - g_string_append_printf (str, " -%c, --%s", entry->short_name, entry->long_name); + g_string_append_printf (str, " -%c, --%s", entry->short_name, long_name); else - g_string_append_printf (str, " --%s", entry->long_name); - + g_string_append_printf (str, " --%s", long_name); + if (entry->arg_description) g_string_append_printf (str, "=%s", TRANSLATE (group, entry->arg_description)); - - g_print ("%-*s %s\n", max_length + 4, str->str, - entry->description ? TRANSLATE (group, entry->description) : ""); - g_string_free (str, TRUE); + + g_string_append_printf (string, "%s%*s %s\n", str->str, + (int) (max_length + 4 - _g_utf8_strwidth (str->str)), "", + entry->description ? TRANSLATE (group, entry->description) : ""); + g_string_free (str, TRUE); } -static void -print_help (GOptionContext *context, - gboolean main_help, - GOptionGroup *group) +static gboolean +group_has_visible_entries (GOptionContext *context, + GOptionGroup *group, + gboolean main_entries) { + GOptionFlags reject_filter = G_OPTION_FLAG_HIDDEN; + GOptionEntry *entry; + gint i, l; + gboolean main_group = group == context->main_group; + + if (!main_entries) + reject_filter |= G_OPTION_FLAG_IN_MAIN; + + for (i = 0, l = (group ? group->n_entries : 0); i < l; i++) + { + entry = &group->entries[i]; + + if (main_entries && !main_group && !(entry->flags & G_OPTION_FLAG_IN_MAIN)) + continue; + if (entry->long_name[0] == 0) /* ignore rest entry */ + continue; + if (!(entry->flags & reject_filter)) + return TRUE; + } + + return FALSE; +} + +static gboolean +group_list_has_visible_entries (GOptionContext *context, + GList *group_list, + gboolean main_entries) +{ + while (group_list) + { + if (group_has_visible_entries (context, group_list->data, main_entries)) + return TRUE; + + group_list = group_list->next; + } + + return FALSE; +} + +static gboolean +context_has_h_entry (GOptionContext *context) +{ + gsize i; GList *list; - gint max_length, len; + + if (context->main_group) + { + for (i = 0; i < context->main_group->n_entries; i++) + { + if (context->main_group->entries[i].short_name == 'h') + return TRUE; + } + } + + for (list = context->groups; list != NULL; list = g_list_next (list)) + { + GOptionGroup *group; + + group = (GOptionGroup*)list->data; + for (i = 0; i < group->n_entries; i++) + { + if (group->entries[i].short_name == 'h') + return TRUE; + } + } + return FALSE; +} + +/** + * g_option_context_get_help: + * @context: a #GOptionContext + * @main_help: if %TRUE, only include the main group + * @group: (allow-none): the #GOptionGroup to create help for, or %NULL + * + * Returns a formatted, translated help text for the given context. + * To obtain the text produced by `--help`, call + * `g_option_context_get_help (context, TRUE, NULL)`. + * To obtain the text produced by `--help-all`, call + * `g_option_context_get_help (context, FALSE, NULL)`. + * To obtain the help text for an option group, call + * `g_option_context_get_help (context, FALSE, group)`. + * + * Returns: A newly allocated string containing the help text + * + * Since: 2.14 + */ +gchar * +g_option_context_get_help (GOptionContext *context, + gboolean main_help, + GOptionGroup *group) +{ + GList *list; + gint max_length = 0, len; gint i; GOptionEntry *entry; GHashTable *shadow_map; + GHashTable *aliases; gboolean seen[256]; const gchar *rest_description; - + GString *string; + guchar token; + + string = g_string_sized_new (1024); + rest_description = NULL; if (context->main_group) { for (i = 0; i < context->main_group->n_entries; i++) - { - entry = &context->main_group->entries[i]; - if (entry->long_name[0] == 0) - { - rest_description = TRANSLATE (context->main_group, entry->arg_description); - break; - } - } + { + entry = &context->main_group->entries[i]; + if (entry->long_name[0] == 0) + { + rest_description = TRANSLATE (context->main_group, entry->arg_description); + break; + } + } + } + + g_string_append_printf (string, "%s\n %s %s", + _("Usage:"), g_get_prgname(), _("[OPTION...]")); + + if (rest_description) + { + g_string_append (string, " "); + g_string_append (string, rest_description); + } + + if (context->parameter_string) + { + g_string_append (string, " "); + g_string_append (string, TRANSLATE (context, context->parameter_string)); } - g_print ("%s\n %s %s%s%s%s%s\n\n", - _("Usage:"), g_get_prgname(), _("[OPTION...]"), - rest_description ? " " : "", - rest_description ? rest_description : "", - context->parameter_string ? " " : "", - context->parameter_string ? TRANSLATE (context, context->parameter_string) : ""); + g_string_append (string, "\n\n"); if (context->summary) - g_print ("%s\n\n", TRANSLATE (context, context->summary)); + { + g_string_append (string, TRANSLATE (context, context->summary)); + g_string_append (string, "\n\n"); + } memset (seen, 0, sizeof (gboolean) * 256); shadow_map = g_hash_table_new (g_str_hash, g_str_equal); + aliases = g_hash_table_new_full (NULL, NULL, NULL, g_free); if (context->main_group) { for (i = 0; i < context->main_group->n_entries; i++) - { - entry = &context->main_group->entries[i]; - g_hash_table_insert (shadow_map, - (gpointer)entry->long_name, - entry); - - if (seen[(guchar)entry->short_name]) - entry->short_name = 0; - else - seen[(guchar)entry->short_name] = TRUE; - } + { + entry = &context->main_group->entries[i]; + g_hash_table_insert (shadow_map, + (gpointer)entry->long_name, + entry); + + if (seen[(guchar)entry->short_name]) + entry->short_name = 0; + else + seen[(guchar)entry->short_name] = TRUE; + } } list = context->groups; while (list != NULL) { - GOptionGroup *group = list->data; - for (i = 0; i < group->n_entries; i++) - { - entry = &group->entries[i]; - 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] && - !(entry->flags && G_OPTION_FLAG_NOALIAS)) - entry->short_name = 0; - else - seen[(guchar)entry->short_name] = TRUE; - } + GOptionGroup *g = list->data; + for (i = 0; i < g->n_entries; i++) + { + entry = &g->entries[i]; + if (g_hash_table_lookup (shadow_map, entry->long_name) && + !(entry->flags & G_OPTION_FLAG_NOALIAS)) + { + g_hash_table_insert (aliases, &entry->long_name, + g_strdup_printf ("%s-%s", g->name, entry->long_name)); + } + else + g_hash_table_insert (shadow_map, (gpointer)entry->long_name, entry); + + if (seen[(guchar)entry->short_name] && + !(entry->flags & G_OPTION_FLAG_NOALIAS)) + entry->short_name = 0; + else + seen[(guchar)entry->short_name] = TRUE; + } list = list->next; } @@ -546,72 +863,87 @@ print_help (GOptionContext *context, list = context->groups; - max_length = g_utf8_strlen ("-?, --help", -1); - - if (list) + if (context->help_enabled) { - len = g_utf8_strlen ("--help-all", -1); - max_length = MAX (max_length, len); + max_length = _g_utf8_strwidth ("-?, --help"); + + if (list) + { + len = _g_utf8_strwidth ("--help-all"); + max_length = MAX (max_length, len); + } } if (context->main_group) { - len = calculate_max_length (context->main_group); + len = calculate_max_length (context->main_group, aliases); max_length = MAX (max_length, len); } while (list != NULL) { - GOptionGroup *group = list->data; - - /* First, we check the --help- options */ - len = g_utf8_strlen ("--help-", -1) + g_utf8_strlen (group->name, -1); - max_length = MAX (max_length, len); + GOptionGroup *g = list->data; + + if (context->help_enabled) + { + /* First, we check the --help- options */ + len = _g_utf8_strwidth ("--help-") + _g_utf8_strwidth (g->name); + max_length = MAX (max_length, len); + } /* Then we go through the entries */ - len = calculate_max_length (group); + len = calculate_max_length (g, aliases); max_length = MAX (max_length, len); - + list = list->next; } /* Add a bit of padding */ max_length += 4; - if (!group) + if (!group && context->help_enabled) { list = context->groups; - - g_print ("%s\n -%c, --%-*s %s\n", - _("Help Options:"), '?', max_length - 4, "help", - _("Show help options")); - + + token = context_has_h_entry (context) ? '?' : 'h'; + + g_string_append_printf (string, "%s\n -%c, --%-*s %s\n", + _("Help Options:"), token, max_length - 4, "help", + _("Show help options")); + /* We only want --help-all when there are groups */ if (list) - g_print (" --%-*s %s\n", max_length, "help-all", - _("Show all help options")); - + g_string_append_printf (string, " --%-*s %s\n", + max_length, "help-all", + _("Show all help options")); + while (list) - { - GOptionGroup *group = list->data; - - g_print (" --help-%-*s %s\n", max_length - 5, group->name, - TRANSLATE (group, group->help_description)); - - list = list->next; - } - - g_print ("\n"); + { + GOptionGroup *g = list->data; + + if (group_has_visible_entries (context, g, FALSE)) + g_string_append_printf (string, " --help-%-*s %s\n", + max_length - 5, g->name, + TRANSLATE (g, g->help_description)); + + list = list->next; + } + + g_string_append (string, "\n"); } if (group) { /* Print a certain group */ - - g_print ("%s\n", TRANSLATE (group, group->description)); - for (i = 0; i < group->n_entries; i++) - print_entry (group, max_length, &group->entries[i]); - g_print ("\n"); + + if (group_has_visible_entries (context, group, FALSE)) + { + g_string_append (string, TRANSLATE (group, group->description)); + g_string_append (string, "\n"); + for (i = 0; i < group->n_entries; i++) + print_entry (group, max_length, &group->entries[i], string, aliases); + g_string_append (string, "\n"); + } } else if (!main_help) { @@ -620,71 +952,97 @@ print_help (GOptionContext *context, list = context->groups; while (list) - { - GOptionGroup *group = list->data; - - g_print ("%s\n", group->description); - - for (i = 0; i < group->n_entries; i++) - if (!(group->entries[i].flags & G_OPTION_FLAG_IN_MAIN)) - print_entry (group, max_length, &group->entries[i]); - - g_print ("\n"); - list = list->next; - } + { + GOptionGroup *g = list->data; + + if (group_has_visible_entries (context, g, FALSE)) + { + g_string_append (string, g->description); + g_string_append (string, "\n"); + for (i = 0; i < g->n_entries; i++) + if (!(g->entries[i].flags & G_OPTION_FLAG_IN_MAIN)) + print_entry (g, max_length, &g->entries[i], string, aliases); + + g_string_append (string, "\n"); + } + + list = list->next; + } } - + /* Print application options if --help or --help-all has been specified */ - if (main_help || !group) + if ((main_help || !group) && + (group_has_visible_entries (context, context->main_group, TRUE) || + group_list_has_visible_entries (context, context->groups, TRUE))) { list = context->groups; - g_print ("%s\n", _("Application Options:")); - + g_string_append (string, _("Application Options:")); + g_string_append (string, "\n"); if (context->main_group) - for (i = 0; i < context->main_group->n_entries; i++) - print_entry (context->main_group, max_length, - &context->main_group->entries[i]); + for (i = 0; i < context->main_group->n_entries; i++) + print_entry (context->main_group, max_length, + &context->main_group->entries[i], string, aliases); while (list != NULL) - { - GOptionGroup *group = list->data; - - /* Print main entries from other groups */ - for (i = 0; i < group->n_entries; i++) - if (group->entries[i].flags & G_OPTION_FLAG_IN_MAIN) - print_entry (group, max_length, &group->entries[i]); - - list = list->next; - } - - g_print ("\n"); + { + GOptionGroup *g = list->data; + + /* Print main entries from other groups */ + for (i = 0; i < g->n_entries; i++) + if (g->entries[i].flags & G_OPTION_FLAG_IN_MAIN) + print_entry (g, max_length, &g->entries[i], string, aliases); + + list = list->next; + } + + g_string_append (string, "\n"); } - + if (context->description) - g_print ("%s\n", TRANSLATE (context, context->description)); - + { + g_string_append (string, TRANSLATE (context, context->description)); + g_string_append (string, "\n"); + } + + g_hash_table_destroy (aliases); + + return g_string_free (string, FALSE); +} + +G_GNUC_NORETURN +static void +print_help (GOptionContext *context, + gboolean main_help, + GOptionGroup *group) +{ + gchar *help; + + help = g_option_context_get_help (context, main_help, group); + g_print ("%s", help); + g_free (help); + exit (0); } static gboolean parse_int (const gchar *arg_name, - const gchar *arg, - gint *result, - GError **error) + const gchar *arg, + gint *result, + GError **error) { gchar *end; glong tmp; errno = 0; tmp = strtol (arg, &end, 0); - + if (*arg == '\0' || *end != '\0') { g_set_error (error, - G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, - _("Cannot parse integer value '%s' for %s"), - arg, arg_name); + G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + _("Cannot parse integer value '%s' for %s"), + arg, arg_name); return FALSE; } @@ -692,9 +1050,9 @@ parse_int (const gchar *arg_name, if (*result != tmp || errno == ERANGE) { g_set_error (error, - G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, - _("Integer value '%s' for %s out of range"), - arg, arg_name); + G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + _("Integer value '%s' for %s out of range"), + arg, arg_name); return FALSE; } @@ -704,44 +1062,44 @@ parse_int (const gchar *arg_name, static gboolean parse_double (const gchar *arg_name, - const gchar *arg, - gdouble *result, - GError **error) + const gchar *arg, + gdouble *result, + GError **error) { gchar *end; gdouble tmp; errno = 0; tmp = g_strtod (arg, &end); - + if (*arg == '\0' || *end != '\0') { g_set_error (error, - G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, - _("Cannot parse double value '%s' for %s"), - arg, arg_name); + G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + _("Cannot parse double value '%s' for %s"), + arg, arg_name); return FALSE; } if (errno == ERANGE) { g_set_error (error, - G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, - _("Double value '%s' for %s out of range"), - arg, arg_name); + G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + _("Double value '%s' for %s out of range"), + arg, arg_name); return FALSE; } *result = tmp; - + return TRUE; } static gboolean parse_int64 (const gchar *arg_name, - const gchar *arg, - gint64 *result, - GError **error) + const gchar *arg, + gint64 *result, + GError **error) { gchar *end; gint64 tmp; @@ -752,48 +1110,48 @@ parse_int64 (const gchar *arg_name, if (*arg == '\0' || *end != '\0') { g_set_error (error, - G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, - _("Cannot parse integer value '%s' for %s"), - arg, arg_name); + G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + _("Cannot parse integer value '%s' for %s"), + arg, arg_name); return FALSE; } if (errno == ERANGE) { g_set_error (error, - G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, - _("Integer value '%s' for %s out of range"), - arg, arg_name); + G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + _("Integer value '%s' for %s out of range"), + arg, arg_name); return FALSE; } *result = tmp; - + return TRUE; } static Change * get_change (GOptionContext *context, - GOptionArg arg_type, - gpointer arg_data) + GOptionArg arg_type, + gpointer arg_data) { GList *list; Change *change = NULL; - + for (list = context->changes; list != NULL; list = list->next) { change = list->data; if (change->arg_data == arg_data) - goto found; + goto found; } change = g_new0 (Change, 1); change->arg_type = arg_type; change->arg_data = arg_data; - + context->changes = g_list_prepend (context->changes, change); - + found: return change; @@ -801,8 +1159,8 @@ get_change (GOptionContext *context, static void add_pending_null (GOptionContext *context, - gchar **ptr, - gchar *value) + gchar **ptr, + gchar *value) { PendingNull *n; @@ -812,15 +1170,15 @@ add_pending_null (GOptionContext *context, context->pending_nulls = g_list_prepend (context->pending_nulls, n); } - + static gboolean parse_arg (GOptionContext *context, - GOptionGroup *group, - GOptionEntry *entry, - const gchar *value, - const gchar *option_name, - GError **error) - + GOptionGroup *group, + GOptionEntry *entry, + const gchar *value, + const gchar *option_name, + GError **error) + { Change *change; @@ -830,201 +1188,229 @@ parse_arg (GOptionContext *context, { case G_OPTION_ARG_NONE: { - change = get_change (context, G_OPTION_ARG_NONE, - entry->arg_data); + (void) get_change (context, G_OPTION_ARG_NONE, + entry->arg_data); - *(gboolean *)entry->arg_data = !(entry->flags & G_OPTION_FLAG_REVERSE); - break; - } + *(gboolean *)entry->arg_data = !(entry->flags & G_OPTION_FLAG_REVERSE); + break; + } case G_OPTION_ARG_STRING: { - gchar *data; - - data = g_locale_to_utf8 (value, -1, NULL, NULL, error); - - if (!data) - return FALSE; - - change = get_change (context, G_OPTION_ARG_STRING, - entry->arg_data); - g_free (change->allocated.str); - - change->prev.str = *(gchar **)entry->arg_data; - change->allocated.str = data; - - *(gchar **)entry->arg_data = data; - break; + gchar *data; + +#ifdef G_OS_WIN32 + if (!context->strv_mode) + data = g_locale_to_utf8 (value, -1, NULL, NULL, error); + else + data = g_strdup (value); +#else + data = g_locale_to_utf8 (value, -1, NULL, NULL, error); +#endif + + if (!data) + return FALSE; + + change = get_change (context, G_OPTION_ARG_STRING, + entry->arg_data); + g_free (change->allocated.str); + + change->prev.str = *(gchar **)entry->arg_data; + change->allocated.str = data; + + *(gchar **)entry->arg_data = data; + break; } case G_OPTION_ARG_STRING_ARRAY: { - gchar *data; + gchar *data; - data = g_locale_to_utf8 (value, -1, NULL, NULL, error); +#ifdef G_OS_WIN32 + if (!context->strv_mode) + data = g_locale_to_utf8 (value, -1, NULL, NULL, error); + else + data = g_strdup (value); +#else + data = g_locale_to_utf8 (value, -1, NULL, NULL, error); +#endif - if (!data) - return FALSE; + if (!data) + return FALSE; - change = get_change (context, G_OPTION_ARG_STRING_ARRAY, - entry->arg_data); + change = get_change (context, G_OPTION_ARG_STRING_ARRAY, + entry->arg_data); - if (change->allocated.array.len == 0) - { - change->prev.array = *(gchar ***)entry->arg_data; - change->allocated.array.data = g_new (gchar *, 2); - } - else - change->allocated.array.data = - g_renew (gchar *, change->allocated.array.data, - change->allocated.array.len + 2); + if (change->allocated.array.len == 0) + { + change->prev.array = *(gchar ***)entry->arg_data; + change->allocated.array.data = g_new (gchar *, 2); + } + else + change->allocated.array.data = + g_renew (gchar *, change->allocated.array.data, + change->allocated.array.len + 2); - change->allocated.array.data[change->allocated.array.len] = data; - change->allocated.array.data[change->allocated.array.len + 1] = NULL; + change->allocated.array.data[change->allocated.array.len] = data; + change->allocated.array.data[change->allocated.array.len + 1] = NULL; - change->allocated.array.len ++; + change->allocated.array.len ++; - *(gchar ***)entry->arg_data = change->allocated.array.data; + *(gchar ***)entry->arg_data = change->allocated.array.data; - break; + break; } - + case G_OPTION_ARG_FILENAME: { - gchar *data; + gchar *data; #ifdef G_OS_WIN32 - data = g_locale_to_utf8 (value, -1, NULL, NULL, error); - - if (!data) - return FALSE; + if (!context->strv_mode) + data = g_locale_to_utf8 (value, -1, NULL, NULL, error); + else + data = g_strdup (value); + + if (!data) + return FALSE; #else - data = g_strdup (value); + data = g_strdup (value); #endif - change = get_change (context, G_OPTION_ARG_FILENAME, - entry->arg_data); - g_free (change->allocated.str); - - change->prev.str = *(gchar **)entry->arg_data; - change->allocated.str = data; - - *(gchar **)entry->arg_data = data; - break; + change = get_change (context, G_OPTION_ARG_FILENAME, + entry->arg_data); + g_free (change->allocated.str); + + change->prev.str = *(gchar **)entry->arg_data; + change->allocated.str = data; + + *(gchar **)entry->arg_data = data; + break; } case G_OPTION_ARG_FILENAME_ARRAY: { - gchar *data; - + gchar *data; + #ifdef G_OS_WIN32 - data = g_locale_to_utf8 (value, -1, NULL, NULL, error); - - if (!data) - return FALSE; + if (!context->strv_mode) + data = g_locale_to_utf8 (value, -1, NULL, NULL, error); + else + data = g_strdup (value); + + if (!data) + return FALSE; #else - data = g_strdup (value); + data = g_strdup (value); #endif - change = get_change (context, G_OPTION_ARG_STRING_ARRAY, - entry->arg_data); + change = get_change (context, G_OPTION_ARG_STRING_ARRAY, + entry->arg_data); - if (change->allocated.array.len == 0) - { - change->prev.array = *(gchar ***)entry->arg_data; - change->allocated.array.data = g_new (gchar *, 2); - } - else - change->allocated.array.data = - g_renew (gchar *, change->allocated.array.data, - change->allocated.array.len + 2); + if (change->allocated.array.len == 0) + { + change->prev.array = *(gchar ***)entry->arg_data; + change->allocated.array.data = g_new (gchar *, 2); + } + else + change->allocated.array.data = + g_renew (gchar *, change->allocated.array.data, + change->allocated.array.len + 2); - change->allocated.array.data[change->allocated.array.len] = data; - change->allocated.array.data[change->allocated.array.len + 1] = NULL; + change->allocated.array.data[change->allocated.array.len] = data; + change->allocated.array.data[change->allocated.array.len + 1] = NULL; - change->allocated.array.len ++; + change->allocated.array.len ++; - *(gchar ***)entry->arg_data = change->allocated.array.data; + *(gchar ***)entry->arg_data = change->allocated.array.data; - break; + break; } - + case G_OPTION_ARG_INT: { - gint data; - - if (!parse_int (option_name, value, - &data, - error)) - return FALSE; - - change = get_change (context, G_OPTION_ARG_INT, - entry->arg_data); - change->prev.integer = *(gint *)entry->arg_data; - *(gint *)entry->arg_data = data; - break; + gint data; + + if (!parse_int (option_name, value, + &data, + error)) + return FALSE; + + change = get_change (context, G_OPTION_ARG_INT, + entry->arg_data); + change->prev.integer = *(gint *)entry->arg_data; + *(gint *)entry->arg_data = data; + break; } case G_OPTION_ARG_CALLBACK: { - gchar *data; - gboolean retval; - - 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) - { + gchar *data; + gboolean retval; + + 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); + if (!context->strv_mode) + data = g_locale_to_utf8 (value, -1, NULL, NULL, error); + else + data = g_strdup (value); #else - data = g_strdup (value); + data = g_strdup (value); #endif - } - else - data = g_locale_to_utf8 (value, -1, NULL, NULL, error); - - 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); - - g_free (data); - - return retval; - - break; + } + else + data = g_locale_to_utf8 (value, -1, NULL, NULL, error); + + 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); + + if (!retval && error != NULL && *error == NULL) + g_set_error (error, + G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + _("Error parsing option %s"), option_name); + + g_free (data); + + return retval; + + break; } case G_OPTION_ARG_DOUBLE: { - gdouble data; - - if (!parse_double (option_name, value, - &data, - error)) - { - return FALSE; - } - - change = get_change (context, G_OPTION_ARG_DOUBLE, - entry->arg_data); - change->prev.dbl = *(gdouble *)entry->arg_data; - *(gdouble *)entry->arg_data = data; - break; + gdouble data; + + if (!parse_double (option_name, value, + &data, + error)) + { + return FALSE; + } + + change = get_change (context, G_OPTION_ARG_DOUBLE, + entry->arg_data); + change->prev.dbl = *(gdouble *)entry->arg_data; + *(gdouble *)entry->arg_data = data; + break; } case G_OPTION_ARG_INT64: { gint64 data; - if (!parse_int64 (option_name, value, - &data, - error)) - { - return FALSE; - } - - change = get_change (context, G_OPTION_ARG_INT64, - entry->arg_data); - change->prev.int64 = *(gint64 *)entry->arg_data; - *(gint64 *)entry->arg_data = data; - break; + if (!parse_int64 (option_name, value, + &data, + error)) + { + return FALSE; + } + + change = get_change (context, G_OPTION_ARG_INT64, + entry->arg_data); + change->prev.int64 = *(gint64 *)entry->arg_data; + *(gint64 *)entry->arg_data = data; + break; } default: g_assert_not_reached (); @@ -1035,81 +1421,81 @@ parse_arg (GOptionContext *context, static gboolean parse_short_option (GOptionContext *context, - GOptionGroup *group, - gint index, - gint *new_index, - gchar arg, - gint *argc, - gchar ***argv, - GError **error, - gboolean *parsed) + GOptionGroup *group, + gint idx, + gint *new_idx, + gchar arg, + gint *argc, + gchar ***argv, + GError **error, + gboolean *parsed) { gint j; - + for (j = 0; j < group->n_entries; j++) { if (arg == group->entries[j].short_name) - { - gchar *option_name; - gchar *value = NULL; - - option_name = g_strdup_printf ("-%c", group->entries[j].short_name); - - if (NO_ARG (&group->entries[j])) - value = NULL; - else - { - if (*new_index > index) - { - g_set_error (error, - G_OPTION_ERROR, G_OPTION_ERROR_FAILED, - _("Error parsing option %s"), option_name); - g_free (option_name); - return FALSE; - } - - if (index < *argc - 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, - G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, - _("Missing argument for %s"), option_name); - 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; - } + { + gchar *option_name; + gchar *value = NULL; + + option_name = g_strdup_printf ("-%c", group->entries[j].short_name); + + if (NO_ARG (&group->entries[j])) + value = NULL; + else + { + if (*new_idx > idx) + { + g_set_error (error, + G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + _("Error parsing option %s"), option_name); + g_free (option_name); + return FALSE; + } + + if (idx < *argc - 1) + { + if (!OPTIONAL_ARG (&group->entries[j])) + { + value = (*argv)[idx + 1]; + add_pending_null (context, &((*argv)[idx + 1]), NULL); + *new_idx = idx + 1; + } + else + { + if ((*argv)[idx + 1][0] == '-') + value = NULL; + else + { + value = (*argv)[idx + 1]; + add_pending_null (context, &((*argv)[idx + 1]), NULL); + *new_idx = idx + 1; + } + } + } + else if (idx >= *argc - 1 && OPTIONAL_ARG (&group->entries[j])) + value = NULL; + else + { + g_set_error (error, + G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + _("Missing argument for %s"), option_name); + 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; + } } return TRUE; @@ -1117,142 +1503,145 @@ parse_short_option (GOptionContext *context, static gboolean parse_long_option (GOptionContext *context, - GOptionGroup *group, - gint *index, - gchar *arg, - gboolean aliased, - gint *argc, - gchar ***argv, - GError **error, - gboolean *parsed) + GOptionGroup *group, + gint *idx, + gchar *arg, + gboolean aliased, + gint *argc, + gchar ***argv, + GError **error, + gboolean *parsed) { gint j; for (j = 0; j < group->n_entries; j++) { - if (*index >= *argc) - return TRUE; + if (*idx >= *argc) + return TRUE; if (aliased && (group->entries[j].flags & G_OPTION_FLAG_NOALIAS)) - continue; + continue; if (NO_ARG (&group->entries[j]) && - strcmp (arg, group->entries[j].long_name) == 0) - { - gchar *option_name; - - option_name = g_strconcat ("--", group->entries[j].long_name, NULL); - parse_arg (context, group, &group->entries[j], - NULL, option_name, error); - g_free(option_name); - - add_pending_null (context, &((*argv)[*index]), NULL); - *parsed = TRUE; - } + strcmp (arg, group->entries[j].long_name) == 0) + { + gchar *option_name; + gboolean retval; + + option_name = g_strconcat ("--", group->entries[j].long_name, NULL); + retval = parse_arg (context, group, &group->entries[j], + NULL, option_name, error); + g_free (option_name); + + add_pending_null (context, &((*argv)[*idx]), NULL); + *parsed = TRUE; + + return retval; + } else - { - gint len = strlen (group->entries[j].long_name); - - if (strncmp (arg, group->entries[j].long_name, len) == 0 && - (arg[len] == '=' || arg[len] == 0)) - { - gchar *value = NULL; - gchar *option_name; - - add_pending_null (context, &((*argv)[*index]), NULL); - option_name = g_strconcat ("--", group->entries[j].long_name, NULL); - - if (arg[len] == '=') - value = arg + len + 1; - else if (*index < *argc - 1) - { - 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 - { - g_set_error (error, - G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, - _("Missing argument for %s"), option_name); - 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; - } - } + { + gint len = strlen (group->entries[j].long_name); + + if (strncmp (arg, group->entries[j].long_name, len) == 0 && + (arg[len] == '=' || arg[len] == 0)) + { + gchar *value = NULL; + gchar *option_name; + + add_pending_null (context, &((*argv)[*idx]), NULL); + option_name = g_strconcat ("--", group->entries[j].long_name, NULL); + + if (arg[len] == '=') + value = arg + len + 1; + else if (*idx < *argc - 1) + { + if (!OPTIONAL_ARG (&group->entries[j])) + { + value = (*argv)[*idx + 1]; + add_pending_null (context, &((*argv)[*idx + 1]), NULL); + (*idx)++; + } + else + { + if ((*argv)[*idx + 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)[*idx + 1]; + add_pending_null (context, &((*argv)[*idx + 1]), NULL); + (*idx)++; + } + } + } + else if (*idx >= *argc - 1 && OPTIONAL_ARG (&group->entries[j])) + { + gboolean retval; + retval = parse_arg (context, group, &group->entries[j], + NULL, option_name, error); + *parsed = TRUE; + g_free (option_name); + return retval; + } + else + { + g_set_error (error, + G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + _("Missing argument for %s"), option_name); + 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; + } + } } - + return TRUE; } static gboolean parse_remaining_arg (GOptionContext *context, - GOptionGroup *group, - gint *index, - gint *argc, - gchar ***argv, - GError **error, - gboolean *parsed) + GOptionGroup *group, + gint *idx, + gint *argc, + gchar ***argv, + GError **error, + gboolean *parsed) { gint j; for (j = 0; j < group->n_entries; j++) { - if (*index >= *argc) - return TRUE; + if (*idx >= *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; - + continue; + + g_return_val_if_fail (group->entries[j].arg == G_OPTION_ARG_CALLBACK || + group->entries[j].arg == G_OPTION_ARG_STRING_ARRAY || + group->entries[j].arg == G_OPTION_ARG_FILENAME_ARRAY, FALSE); + + add_pending_null (context, &((*argv)[*idx]), NULL); + + if (!parse_arg (context, group, &group->entries[j], (*argv)[*idx], "", error)) + return FALSE; + *parsed = TRUE; return TRUE; } @@ -1262,7 +1651,7 @@ parse_remaining_arg (GOptionContext *context, static void free_changes_list (GOptionContext *context, - gboolean revert) + gboolean revert) { GList *list; @@ -1271,36 +1660,36 @@ free_changes_list (GOptionContext *context, Change *change = list->data; if (revert) - { - switch (change->arg_type) - { - case G_OPTION_ARG_NONE: - *(gboolean *)change->arg_data = change->prev.bool; - break; - case G_OPTION_ARG_INT: - *(gint *)change->arg_data = change->prev.integer; - break; - case G_OPTION_ARG_STRING: - case G_OPTION_ARG_FILENAME: + { + switch (change->arg_type) + { + case G_OPTION_ARG_NONE: + *(gboolean *)change->arg_data = change->prev.bool; + break; + case G_OPTION_ARG_INT: + *(gint *)change->arg_data = change->prev.integer; + break; + case G_OPTION_ARG_STRING: + case G_OPTION_ARG_FILENAME: g_free (change->allocated.str); - *(gchar **)change->arg_data = change->prev.str; - break; - case G_OPTION_ARG_STRING_ARRAY: - case G_OPTION_ARG_FILENAME_ARRAY: - g_strfreev (change->allocated.array.data); - *(gchar ***)change->arg_data = change->prev.array; - break; - case G_OPTION_ARG_DOUBLE: - *(gdouble *)change->arg_data = change->prev.dbl; - break; - case G_OPTION_ARG_INT64: - *(gint64 *)change->arg_data = change->prev.int64; - break; - default: - g_assert_not_reached (); - } - } - + *(gchar **)change->arg_data = change->prev.str; + break; + case G_OPTION_ARG_STRING_ARRAY: + case G_OPTION_ARG_FILENAME_ARRAY: + g_strfreev (change->allocated.array.data); + *(gchar ***)change->arg_data = change->prev.array; + break; + case G_OPTION_ARG_DOUBLE: + *(gdouble *)change->arg_data = change->prev.dbl; + break; + case G_OPTION_ARG_INT64: + *(gint64 *)change->arg_data = change->prev.int64; + break; + default: + g_assert_not_reached (); + } + } + g_free (change); } @@ -1310,7 +1699,7 @@ free_changes_list (GOptionContext *context, static void free_pending_nulls (GOptionContext *context, - gboolean perform_nulls) + gboolean perform_nulls) { GList *list; @@ -1319,17 +1708,22 @@ free_pending_nulls (GOptionContext *context, PendingNull *n = list->data; if (perform_nulls) - { - if (n->value) - { - /* Copy back the short options */ - *(n->ptr)[0] = '-'; - strcpy (*n->ptr + 1, n->value); - } - else - *n->ptr = NULL; - } - + { + if (n->value) + { + /* Copy back the short options */ + *(n->ptr)[0] = '-'; + strcpy (*n->ptr + 1, n->value); + } + else + { + if (context->strv_mode) + g_free (*n->ptr); + + *n->ptr = NULL; + } + } + g_free (n->value); g_free (n); } @@ -1338,46 +1732,103 @@ free_pending_nulls (GOptionContext *context, context->pending_nulls = NULL; } +/* Use a platform-specific mechanism to look up the first argument to + * the current process. + * Note if you implement this for other platforms, also add it to + * tests/option-argv0.c + */ +static char * +platform_get_argv0 (void) +{ +#if defined __linux + char *cmdline; + char *base_arg0; + gsize len; + + if (!g_file_get_contents ("/proc/self/cmdline", + &cmdline, + &len, + NULL)) + return NULL; + /* Sanity check for a NUL terminator. */ + if (!memchr (cmdline, 0, len)) + return NULL; + /* We could just return cmdline, but I think it's better + * to hold on to a smaller malloc block; the arguments + * could be large. + */ + base_arg0 = g_path_get_basename (cmdline); + g_free (cmdline); + return base_arg0; +#elif defined __OpenBSD__ + char **cmdline; + char *base_arg0; + gsize len; + + int mib[] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV }; + + if (sysctl (mib, G_N_ELEMENTS (mib), NULL, &len, NULL, 0) == -1) + return NULL; + + cmdline = g_malloc0 (len); + + if (sysctl (mib, G_N_ELEMENTS (mib), cmdline, &len, NULL, 0) == -1) + { + g_free (cmdline); + return NULL; + } + + /* We could just return cmdline, but I think it's better + * to hold on to a smaller malloc block; the arguments + * could be large. + */ + base_arg0 = g_path_get_basename (*cmdline); + g_free (cmdline); + return base_arg0; +#endif + + return NULL; +} + /** * g_option_context_parse: * @context: a #GOptionContext - * @argc: a pointer to the number of command line arguments - * @argv: a pointer to the array of command line arguments - * @error: a return location for errors - * + * @argc: (inout) (allow-none): a pointer to the number of command line arguments + * @argv: (inout) (array length=argc) (allow-none): a pointer to the array of command line arguments + * @error: a return location for errors + * * Parses the command line arguments, recognizing options - * which have been added to @context. A side-effect of + * which have been added to @context. A side-effect of * calling this function is that g_set_prgname() will be * called. * * If the parsing is successful, any parsed arguments are - * removed from the array and @argc and @argv are updated + * removed from the array and @argc and @argv are updated * accordingly. A '--' option is stripped from @argv - * unless there are unparsed options before and after it, - * or some of the options after it start with '-'. In case - * of an error, @argc and @argv are left unmodified. + * unless there are unparsed options before and after it, + * or some of the options after it start with '-'. In case + * of an error, @argc and @argv are left unmodified. * - * If automatic support is enabled - * (see g_option_context_set_help_enabled()), and the + * If automatic `--help` support is enabled + * (see g_option_context_set_help_enabled()), and the * @argv array contains one of the recognized help options, * this function will produce help output to stdout and - * call exit (0). + * call `exit (0)`. * - * Note that function depends on the - * current locale for + * Note that function depends on the [current locale][setlocale] for * automatic character set conversion of string and filename * arguments. - * - * Return value: %TRUE if the parsing was successful, + * + * Returns: %TRUE if the parsing was successful, * %FALSE if an error occurred * * Since: 2.6 **/ gboolean g_option_context_parse (GOptionContext *context, - gint *argc, - gchar ***argv, - GError **error) + gint *argc, + gchar ***argv, + GError **error) { gint i, j, k; GList *list; @@ -1385,16 +1836,19 @@ g_option_context_parse (GOptionContext *context, /* Set program name */ if (!g_get_prgname()) { + gchar *prgname; + if (argc && argv && *argc) - { - gchar *prgname; - - prgname = g_path_get_basename ((*argv)[0]); - g_set_prgname (prgname); - g_free (prgname); - } + prgname = g_path_get_basename ((*argv)[0]); + else + prgname = platform_get_argv0 (); + + if (prgname) + g_set_prgname (prgname); else g_set_prgname (""); + + g_free (prgname); } /* Call pre-parse hooks */ @@ -1402,22 +1856,22 @@ g_option_context_parse (GOptionContext *context, while (list) { GOptionGroup *group = list->data; - + if (group->pre_parse_func) - { - if (!(* group->pre_parse_func) (context, group, - group->user_data, error)) - goto fail; - } - + { + if (!(* group->pre_parse_func) (context, group, + group->user_data, error)) + goto fail; + } + list = list->next; } if (context->main_group && context->main_group->pre_parse_func) { if (!(* context->main_group->pre_parse_func) (context, context->main_group, - context->main_group->user_data, error)) - goto fail; + context->main_group->user_data, error)) + goto fail; } if (argc && argv) @@ -1427,198 +1881,199 @@ g_option_context_parse (GOptionContext *context, gint separator_pos = 0; for (i = 1; i < *argc; i++) - { - gchar *arg, *dash; - gboolean parsed = FALSE; - - if ((*argv)[i][0] == '-' && (*argv)[i][1] != '\0' && !stop_parsing) - { - if ((*argv)[i][1] == '-') - { - /* -- option */ - - arg = (*argv)[i] + 2; - - /* '--' terminates list of arguments */ - if (*arg == 0) - { - separator_pos = i; - stop_parsing = TRUE; - continue; - } - - /* Handle help options */ - if (context->help_enabled) - { - if (strcmp (arg, "help") == 0) - print_help (context, TRUE, NULL); - else if (strcmp (arg, "help-all") == 0) - print_help (context, FALSE, NULL); - else if (strncmp (arg, "help-", 5) == 0) - { - GList *list; - - list = context->groups; - - while (list) - { - GOptionGroup *group = list->data; - - if (strcmp (arg + 5, group->name) == 0) - print_help (context, FALSE, group); - - list = list->next; - } - } - } - - if (context->main_group && - !parse_long_option (context, context->main_group, &i, arg, - FALSE, argc, argv, error, &parsed)) - goto fail; - - if (parsed) - continue; - - /* Try the groups */ - list = context->groups; - while (list) - { - GOptionGroup *group = list->data; - - if (!parse_long_option (context, group, &i, arg, - FALSE, argc, argv, error, &parsed)) - goto fail; - - if (parsed) - break; - - list = list->next; - } - - if (parsed) - continue; - - /* Now look for ---