* static gint max_size = 8;
* static gboolean verbose = FALSE;
* static gboolean beep = FALSE;
- * static gboolean rand = FALSE;
+ * static gboolean randomize = FALSE;
*
* static GOptionEntry entries[] =
* {
* { "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, &rand, "Randomize the data", NULL },
+ * { "rand", 0, 0, G_OPTION_ARG_NONE, &randomize, "Randomize the data", NULL },
* { NULL }
* };
*
*
* }
* </programlisting></informalexample>
+ *
+ * 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
+ *
+ * /* ... setup context ... */
+ *
+ * if (!g_option_context_parse_strv (context, &args, &error))
+ * {
+ * /* ... error ... */
+ * }
+ *
+ * /* ... */
+ *
+ * g_strfreev (args);
+ *
+ * /* ... */
+ * }
+ * ]|
*/
#include "config.h"
guint help_enabled : 1;
guint ignore_unknown : 1;
+ guint strv_mode : 1;
GOptionGroup *main_group;
/**
* g_option_context_new:
- * @parameter_string: a string which is displayed in
+ * @parameter_string: (allow-none): a string which is displayed in
* the first line of <option>--help</option> output, after the
* usage summary
* <literal><replaceable>programname</replaceable> [OPTION...]</literal>
}
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;
if (entry->flags & G_OPTION_FLAG_HIDDEN)
continue;
- len = _g_utf8_strwidth (entry->long_name);
+ long_name = g_hash_table_lookup (aliases, &entry->long_name);
+ if (!long_name)
+ long_name = entry->long_name;
+ len = _g_utf8_strwidth (long_name);
if (entry->short_name)
len += 4;
print_entry (GOptionGroup *group,
gint max_length,
const GOptionEntry *entry,
- GString *string)
+ GString *string,
+ GHashTable *aliases)
{
GString *str;
+ const gchar *long_name;
if (entry->flags & G_OPTION_FLAG_HIDDEN)
return;
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));
GOptionGroup *group)
{
GList *list;
- gint max_length, len;
+ gint max_length = 0, len;
gint i;
GOptionEntry *entry;
GHashTable *shadow_map;
+ GHashTable *aliases;
gboolean seen[256];
const gchar *rest_description;
GString *string;
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)
{
entry = &g->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", g->name, entry->long_name);
+ {
+ 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);
list = context->groups;
- max_length = _g_utf8_strwidth ("-?, --help");
-
- if (list)
+ if (context->help_enabled)
{
- len = _g_utf8_strwidth ("--help-all");
- 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);
}
{
GOptionGroup *g = list->data;
- /* First, we check the --help-<groupname> options */
- len = _g_utf8_strwidth ("--help-") + _g_utf8_strwidth (g->name);
- max_length = MAX (max_length, len);
+ if (context->help_enabled)
+ {
+ /* First, we check the --help-<groupname> 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 (g);
+ 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_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);
+ print_entry (group, max_length, &group->entries[i], string, aliases);
g_string_append (string, "\n");
}
}
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);
+ print_entry (g, max_length, &g->entries[i], string, aliases);
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], string);
+ &context->main_group->entries[i], string, aliases);
while (list != NULL)
{
/* 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);
+ print_entry (g, max_length, &g->entries[i], string, aliases);
list = list->next;
}
g_string_append (string, "\n");
}
+ g_hash_table_destroy (aliases);
+
return g_string_free (string, FALSE);
}
{
gchar *data;
+#if 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;
{
gchar *data;
+#if 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;
gchar *data;
#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);
if (!data)
return FALSE;
gchar *data;
#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);
if (!data)
return FALSE;
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);
#endif
if (perform_nulls)
{
+ if (context->strv_mode)
+ g_free (*n->ptr);
+
if (n->value)
{
/* Copy back the short options */
return context->description;
}
+
+/**
+ * g_option_context_parse_strv:
+ * @context: a #GOptionContext
+ * @arguments: (inout) (array null-terminated=1): a pointer to the
+ * command line arguments (which must be in UTF-8 on Windows)
+ * @error: a return location for errors
+ *
+ * Parses the command line arguments.
+ *
+ * This function is similar to g_option_context_parse() except that it
+ * respects the normal memory rules when dealing with a strv instead of
+ * assuming that the passed-in array is the argv of the main function.
+ *
+ * In particular, strings that are removed from the arguments list will
+ * be freed using g_free().
+ *
+ * On Windows, the strings are expected to be in UTF-8. This is in
+ * contrast to g_option_context_parse() which expects them to be in the
+ * system codepage, which is how they are passed as @argv to main().
+ * See g_win32_get_command_line() for a solution.
+ *
+ * This function is useful if you are trying to use #GOptionContext with
+ * #GApplication.
+ *
+ * Returns: %TRUE if the parsing was successful,
+ * %FALSE if an error occurred
+ *
+ * Since: 2.40
+ **/
+gboolean
+g_option_context_parse_strv (GOptionContext *context,
+ gchar ***arguments,
+ GError **error)
+{
+ gboolean success;
+ gint argc;
+
+ context->strv_mode = TRUE;
+ argc = g_strv_length (*arguments);
+ success = g_option_context_parse (context, &argc, arguments, error);
+ context->strv_mode = FALSE;
+
+ return success;
+}