#include "config.h"
-#include "galias.h"
-
#include "goption.h"
#include "glib.h"
#include "gi18n.h"
+#include "galias.h"
+
#include <string.h>
#include <stdlib.h>
#include <errno.h>
/**
* g_option_context_new:
- * @parameter_string: the parameter string to be used in
- * <option>--help</option> output.
+ * @parameter_string: a string which is displayed in
+ * the first line of <option>--help</option> output, after
+ * <literal><replaceable>programname</replaceable> [OPTION...]</literal>
*
* Creates a new option context.
*
* g_option_context_parse() treats unknown options as error.
*
* This setting does not affect non-option arguments (i.e. arguments
- * which don't start with a dash).
+ * which don't start with a dash). But note that GOption cannot reliably
+ * determine whether a non-option belongs to a preceding unknown option.
*
* Since: 2.6
**/
g_option_context_add_group (GOptionContext *context,
GOptionGroup *group)
{
+ GList *list;
+
g_return_if_fail (context != NULL);
g_return_if_fail (group != NULL);
g_return_if_fail (group->name != NULL);
g_return_if_fail (group->description != NULL);
g_return_if_fail (group->help_description != NULL);
- context->groups = g_list_prepend (context->groups, group);
+ for (list = context->groups; list; list = list->next)
+ {
+ 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);
+ }
+
+ context->groups = g_list_append (context->groups, group);
}
/**
g_return_if_fail (context != NULL);
g_return_if_fail (group != NULL);
+ if (context->main_group)
+ {
+ g_warning ("This GOptionContext already has a main group");
+
+ return;
+ }
+
context->main_group = group;
}
g_option_group_set_translation_domain (context->main_group, translation_domain);
}
+static gint
+calculate_max_length (GOptionGroup *group)
+{
+ GOptionEntry *entry;
+ gint i, len, max_length;
+
+ max_length = 0;
+
+ for (i = 0; i < group->n_entries; i++)
+ {
+ entry = &group->entries[i];
+
+ if (entry->flags & G_OPTION_FLAG_HIDDEN)
+ continue;
+
+ len = g_utf8_strlen (entry->long_name, -1);
+
+ if (entry->short_name)
+ len += 4;
+
+ if (entry->arg != G_OPTION_ARG_NONE && entry->arg_description)
+ len += 1 + g_utf8_strlen (TRANSLATE (group, entry->arg_description), -1);
+
+ max_length = MAX (max_length, len);
+ }
+
+ return max_length;
+}
+
static void
print_entry (GOptionGroup *group,
gint max_length,
if (entry->flags & G_OPTION_FLAG_HIDDEN)
return;
-
+
+ if (entry->long_name[0] == 0)
+ return;
+
str = g_string_new (NULL);
if (entry->short_name)
static void
print_help (GOptionContext *context,
gboolean main_help,
- GOptionGroup *group)
+ GOptionGroup *group)
{
GList *list;
gint max_length, len;
gint i;
+ GOptionEntry *entry;
+ GHashTable *shadow_map;
+ gboolean seen[256];
g_print ("%s\n %s %s %s\n\n",
_("Usage:"), g_get_prgname(), _("[OPTION...]"),
context->parameter_string ? context->parameter_string : "");
+ memset (seen, 0, sizeof (gboolean) * 256);
+ shadow_map = g_hash_table_new (g_str_hash, g_str_equal);
+
+ 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;
+ }
+ }
+
+ 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->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->short_name = 0;
+ else
+ seen[(guchar)entry->short_name] = TRUE;
+ }
+ list = list->next;
+ }
+
+ g_hash_table_destroy (shadow_map);
+
list = context->groups;
- max_length = g_utf8_strlen ("--help, -?", -1);
+ max_length = g_utf8_strlen ("-?, --help", -1);
if (list)
{
max_length = MAX (max_length, len);
}
+ if (context->main_group)
+ {
+ len = calculate_max_length (context->main_group);
+ max_length = MAX (max_length, len);
+ }
+
while (list != NULL)
{
GOptionGroup *group = list->data;
max_length = MAX (max_length, len);
/* Then we go through the entries */
- for (i = 0; i < group->n_entries; i++)
- {
- if (group->entries[i].flags & G_OPTION_FLAG_HIDDEN)
- continue;
-
- len = g_utf8_strlen (group->entries[i].long_name, -1);
-
- if (group->entries[i].short_name)
- len += 4;
-
- if (group->entries[i].arg != G_OPTION_ARG_NONE &&
- group->entries[i].arg_description)
- len += 1 + g_utf8_strlen (TRANSLATE (group, group->entries[i].arg_description), -1);
-
- max_length = MAX (max_length, len);
- }
+ len = calculate_max_length (group);
+ max_length = MAX (max_length, len);
list = list->next;
}
/* Add a bit of padding */
max_length += 4;
-
- list = context->groups;
-
- g_print ("%s\n --%-*s %s\n",
- _("Help Options:"), max_length, "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"));
-
- while (list)
+ if (!group)
{
- GOptionGroup *group = list->data;
-
- g_print (" --help-%-*s %s\n", max_length - 5, group->name, TRANSLATE (group, group->help_description));
+ list = context->groups;
- list = list->next;
- }
+ g_print ("%s\n -%c, --%-*s %s\n",
+ _("Help Options:"), '?', 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"));
+
+ 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");
+ g_print ("\n");
+ }
if (group)
{
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]);
+ print_entry (context->main_group, max_length,
+ &context->main_group->entries[i]);
while (list != NULL)
{
g_print ("\n");
}
-
exit (0);
}
{
g_set_error (error,
G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
- _("Cannot parse integer value '%s' for --%s"),
+ _("Cannot parse integer value '%s' for %s"),
arg, arg_name);
return FALSE;
}
change = get_change (context, G_OPTION_ARG_NONE,
entry->arg_data);
- *(gboolean *)entry->arg_data = TRUE;
+ *(gboolean *)entry->arg_data = !(entry->flags & G_OPTION_FLAG_REVERSE);
break;
}
case G_OPTION_ARG_STRING:
{
gchar *data;
+#ifdef G_OS_WIN32
+ data = g_locale_to_utf8 (value, -1, NULL, NULL, error);
+
+ if (!data)
+ return FALSE;
+#else
data = g_strdup (value);
-
+#endif
change = get_change (context, G_OPTION_ARG_FILENAME,
entry->arg_data);
g_free (change->allocated.str);
{
gchar *data;
+#ifdef G_OS_WIN32
+ data = g_locale_to_utf8 (value, -1, NULL, NULL, error);
+
+ if (!data)
+ return FALSE;
+#else
data = g_strdup (value);
-
+#endif
change = get_change (context, G_OPTION_ARG_STRING_ARRAY,
entry->arg_data);
*
* If the parsing is successful, any parsed arguments are
* removed from the array and @argc and @argv are updated
- * accordingly. In case of an error, @argc and @argv are
- * left unmodified.
+ * 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.
+ *
+ * If automatic <option>--help</option> 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 <literal>exit (0)</literal>.
*
* Return value: %TRUE if the parsing was successful,
* %FALSE if an error occurred
if (argc && argv)
{
gboolean stop_parsing = FALSE;
+ gboolean has_unknown = FALSE;
+ gint separator_pos = 0;
for (i = 1; i < *argc; i++)
{
- gchar *arg;
+ gchar *arg, *dash;
gboolean parsed = FALSE;
if ((*argv)[i][0] == '-' && !stop_parsing)
/* '--' terminates list of arguments */
if (*arg == 0)
{
- add_pending_null (context, &((*argv)[i]), NULL);
+ separator_pos = i;
stop_parsing = TRUE;
continue;
}
list = list->next;
}
+
+ if (parsed)
+ continue;
+ /* Now look for --<group>-<option> */
+ dash = strchr (arg, '-');
+ if (dash)
+ {
+ /* Try the groups */
+ list = context->groups;
+ while (list)
+ {
+ GOptionGroup *group = list->data;
+
+ if (strncmp (group->name, arg, dash - arg) == 0)
+ {
+ if (!parse_long_option (context, group, &i, dash + 1,
+ argc, argv, error, &parsed))
+ goto fail;
+
+ if (parsed)
+ break;
+ }
+
+ list = list->next;
+ }
+ }
+
if (context->ignore_unknown)
continue;
}
else
{
+ /* short option */
+
gint new_i, j;
gboolean *nulled_out = NULL;
for (j = 0; j < strlen (arg); j++)
{
+ if (context->help_enabled && arg[j] == '?')
+ print_help (context, TRUE, NULL);
+
parsed = FALSE;
if (context->main_group &&
if (!nulled_out[j])
{
if (!new_arg)
- new_arg = g_malloc (strlen (arg));
+ new_arg = g_malloc (strlen (arg) + 1);
new_arg[arg_index++] = arg[j];
}
}
}
}
+ if (!parsed)
+ has_unknown = TRUE;
+
if (!parsed && !context->ignore_unknown)
{
g_set_error (error,
G_OPTION_ERROR, G_OPTION_ERROR_UNKNOWN_OPTION,
- _("Unknown option %s"), (*argv)[i]);
+ _("Unknown option %s"), (*argv)[i]);
goto fail;
}
}
argc, argv, error, &parsed))
goto fail;
+ if (!parsed && (has_unknown || (*argv)[i][0] == '-'))
+ separator_pos = 0;
}
}
- /* Call post-parse hooks */
- list = context->groups;
- while (list)
- {
- GOptionGroup *group = list->data;
-
- if (group->post_parse_func)
- {
- if (!(* group->post_parse_func) (context, group,
- group->user_data, error))
- goto fail;
- }
-
- list = list->next;
- }
+ if (separator_pos > 0)
+ add_pending_null (context, &((*argv)[separator_pos]), NULL);
+
+ }
- if (context->main_group && context->main_group->post_parse_func)
+ /* Call post-parse hooks */
+ list = context->groups;
+ while (list)
+ {
+ GOptionGroup *group = list->data;
+
+ if (group->post_parse_func)
{
- if (!(* context->main_group->post_parse_func) (context, context->main_group,
- context->main_group->user_data, error))
+ if (!(* group->post_parse_func) (context, group,
+ group->user_data, error))
goto fail;
}
-
+
+ list = list->next;
+ }
+
+ if (context->main_group && context->main_group->post_parse_func)
+ {
+ if (!(* context->main_group->post_parse_func) (context, context->main_group,
+ context->main_group->user_data, error))
+ goto fail;
+ }
+
+ if (argc && argv)
+ {
free_pending_nulls (context, TRUE);
for (i = 1; i < *argc; i++)
return group;
}
+
/**
* g_option_group_free:
* @group: a #GOptionGroup
g_free);
}
+#define __G_OPTION_C__
+#include "galiasdef.c"