goption.c 78 KB
Newer Older
Anders Carlsson's avatar
Anders Carlsson committed
1 2 3 4 5 6
/* goption.c - Option parser
 *
 *  Copyright (C) 1999, 2003 Red Hat Software
 *  Copyright (C) 2004       Anders Carlsson <andersca@gnome.org>
 *
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
Anders Carlsson's avatar
Anders Carlsson committed
8
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
Anders Carlsson's avatar
Anders Carlsson committed
10 11 12
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
Anders Carlsson's avatar
Anders Carlsson committed
15
 *
16 17
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, see <http://www.gnu.org/licenses/>.
Anders Carlsson's avatar
Anders Carlsson committed
18
 */
19

20 21 22 23
/**
 * SECTION:option
 * @Short_description: parses commandline options
 * @Title: Commandline option parser
24 25 26 27 28
 *
 * 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:
 *
Matthias Clasen's avatar
Matthias Clasen committed
29
 * `testtreemodel -r 1 --max-size 20 --rand --display=:1.0 -vb -- file1 file2`
30
 *
31
 * The example demonstrates a number of features of the GOption
32 33 34 35 36 37 38 39 40
 * 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
41 42 43 44
 *   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.
45 46 47 48
 *
 * - Non-option arguments are returned to the application as rest arguments.
 *
 * - An argument consisting solely of two dashes turns off further parsing,
49
 *   any remaining arguments (even those starting with a dash) are returned
50
 *   to the application as rest arguments.
51
 *
52 53 54
 * 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
Matthias Clasen's avatar
Matthias Clasen committed
55 56 57
 * 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.
58
 *
59
 * |[
60 61
 * Usage:
 *   testtreemodel [OPTION...] - test tree model performance
62
 *  
63
 * Help Options:
64
 *   -h, --help               Show help options
65 66
 *   --help-all               Show all help options
 *   --help-gtk               Show GTK+ Options
67
 *  
68 69 70 71 72
 * 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
73
 *   -b, --beep               Beep when done
74
 *   --rand                   Randomize the data
75
 * ]|
76
 *
Matthias Clasen's avatar
Matthias Clasen committed
77
 * GOption groups options in #GOptionGroups, which makes it easy to
78 79 80 81
 * 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.
82
 *
83
 * If an option is declared to be of type string or filename, GOption takes
84 85 86 87 88
 * 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().
 *
89 90
 * Here is a complete example of setting up GOption to parse the example
 * commandline above and produce the example help output.
91
 * |[<!-- language="C" --> 
92 93 94 95
 * static gint repeats = 2;
 * static gint max_size = 8;
 * static gboolean verbose = FALSE;
 * static gboolean beep = FALSE;
96
 * static gboolean randomize = FALSE;
97 98
 *
 * static GOptionEntry entries[] =
99 100 101 102 103
 * {
 *   { "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 },
104
 *   { "rand", 0, 0, G_OPTION_ARG_NONE, &randomize, "Randomize the data", NULL },
105 106
 *   { NULL }
 * };
107 108
 *
 * int
109 110 111 112
 * main (int argc, char *argv[])
 * {
 *   GError *error = NULL;
 *   GOptionContext *context;
113
 *
114 115 116 117 118 119 120 121
 *   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);
 *     }
122
 *
Matthias Clasen's avatar
Matthias Clasen committed
123
 *   ...
124
 *
125
 * }
126
 * ]|
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
 *
 * 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:
 *
150
 * |[<!-- language="C" --> 
151 152 153 154 155 156 157 158 159 160 161 162 163
 * 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
 *
Matthias Clasen's avatar
Matthias Clasen committed
164
 *   // set up context
165 166 167
 *
 *   if (!g_option_context_parse_strv (context, &args, &error))
 *     {
Matthias Clasen's avatar
Matthias Clasen committed
168
 *       // error happened
169 170
 *     }
 *
Matthias Clasen's avatar
Matthias Clasen committed
171
 *   ...
172 173 174
 *
 *   g_strfreev (args);
 *
Matthias Clasen's avatar
Matthias Clasen committed
175
 *   ...
176 177
 * }
 * ]|
178
 */
Anders Carlsson's avatar
Anders Carlsson committed
179

180
#include "config.h"
Anders Carlsson's avatar
Anders Carlsson committed
181 182 183

#include <string.h>
#include <stdlib.h>
184
#include <stdio.h>
Anders Carlsson's avatar
Anders Carlsson committed
185 186
#include <errno.h>

187 188 189 190 191
#if defined __OpenBSD__
#include <unistd.h>
#include <sys/sysctl.h>
#endif

192 193 194 195 196
#include "goption.h"

#include "gprintf.h"
#include "glibintl.h"

197 198 199 200
#if defined G_OS_WIN32
#include <windows.h>
#endif

Anders Carlsson's avatar
Anders Carlsson committed
201 202
#define TRANSLATE(group, str) (((group)->translate_func ? (* (group)->translate_func) ((str), (group)->translate_data) : (str)))

203 204 205 206
#define NO_ARG(entry) ((entry)->arg == G_OPTION_ARG_NONE ||       \
                       ((entry)->arg == G_OPTION_ARG_CALLBACK &&  \
                        ((entry)->flags & G_OPTION_FLAG_NO_ARG)))

207 208 209
#define OPTIONAL_ARG(entry) ((entry)->arg == G_OPTION_ARG_CALLBACK &&  \
                       (entry)->flags & G_OPTION_FLAG_OPTIONAL_ARG)

210
typedef struct
211
{
Anders Carlsson's avatar
Anders Carlsson committed
212
  GOptionArg arg_type;
213 214
  gpointer arg_data;
  union
215
  {
Anders Carlsson's avatar
Anders Carlsson committed
216 217 218 219
    gboolean bool;
    gint integer;
    gchar *str;
    gchar **array;
220
    gdouble dbl;
221
    gint64 int64;
Anders Carlsson's avatar
Anders Carlsson committed
222
  } prev;
223
  union
224
  {
Anders Carlsson's avatar
Anders Carlsson committed
225
    gchar *str;
226
    struct
227
    {
228
      gint len;
Anders Carlsson's avatar
Anders Carlsson committed
229 230 231 232 233 234 235 236 237 238 239 240 241
      gchar **data;
    } array;
  } allocated;
} Change;

typedef struct
{
  gchar **ptr;
  gchar *value;
} PendingNull;

struct _GOptionContext
{
242
  GList           *groups;
Anders Carlsson's avatar
Anders Carlsson committed
243

244 245 246
  gchar           *parameter_string;
  gchar           *summary;
  gchar           *description;
Anders Carlsson's avatar
Anders Carlsson committed
247

248 249
  GTranslateFunc   translate_func;
  GDestroyNotify   translate_notify;
250
  gpointer         translate_data;
251 252 253

  guint            help_enabled   : 1;
  guint            ignore_unknown : 1;
254
  guint            strv_mode      : 1;
255
  guint            strict_posix   : 1;
256

257
  GOptionGroup    *main_group;
Anders Carlsson's avatar
Anders Carlsson committed
258 259

  /* We keep a list of change so we can revert them */
260
  GList           *changes;
261 262

  /* We also keep track of all argv elements
263
   * that should be NULLed or modified.
Anders Carlsson's avatar
Anders Carlsson committed
264
   */
265
  GList           *pending_nulls;
Anders Carlsson's avatar
Anders Carlsson committed
266 267 268 269
};

struct _GOptionGroup
{
270 271 272
  gchar           *name;
  gchar           *description;
  gchar           *help_description;
Anders Carlsson's avatar
Anders Carlsson committed
273

274 275
  gint             ref_count;

276 277
  GDestroyNotify   destroy_notify;
  gpointer         user_data;
Anders Carlsson's avatar
Anders Carlsson committed
278

279 280
  GTranslateFunc   translate_func;
  GDestroyNotify   translate_notify;
281
  gpointer         translate_data;
Anders Carlsson's avatar
Anders Carlsson committed
282

283
  GOptionEntry    *entries;
284
  gsize            n_entries;
Anders Carlsson's avatar
Anders Carlsson committed
285 286 287 288 289 290 291

  GOptionParseFunc pre_parse_func;
  GOptionParseFunc post_parse_func;
  GOptionErrorFunc error_func;
};

static void free_changes_list (GOptionContext *context,
292
                               gboolean        revert);
Anders Carlsson's avatar
Anders Carlsson committed
293
static void free_pending_nulls (GOptionContext *context,
294
                                gboolean        perform_nulls);
Anders Carlsson's avatar
Anders Carlsson committed
295

296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311

static int
_g_unichar_get_width (gunichar c)
{
  if (G_UNLIKELY (g_unichar_iszerowidth (c)))
    return 0;

  /* 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
Matthias Clasen's avatar
Matthias Clasen committed
312
_g_utf8_strwidth (const gchar *p)
313 314
{
  glong len = 0;
Matthias Clasen's avatar
Matthias Clasen committed
315
  g_return_val_if_fail (p != NULL, 0);
316

Matthias Clasen's avatar
Matthias Clasen committed
317
  while (*p)
318 319
    {
      len += _g_unichar_get_width (g_utf8_get_char (p));
320
      p = g_utf8_next_char (p);
321 322 323 324 325
    }

  return len;
}

326
G_DEFINE_QUARK (g-option-context-error-quark, g_option_error)
Anders Carlsson's avatar
Anders Carlsson committed
327

Matthias Clasen's avatar
Matthias Clasen committed
328 329
/**
 * g_option_context_new:
330
 * @parameter_string: (nullable): a string which is displayed in
Matthias Clasen's avatar
Matthias Clasen committed
331 332
 *    the first line of `--help` output, after the usage summary
 *    `programname [OPTION...]`
Matthias Clasen's avatar
Matthias Clasen committed
333
 *
334
 * Creates a new option context.
Matthias Clasen's avatar
Matthias Clasen committed
335
 *
336
 * The @parameter_string can serve multiple purposes. It can be used
337 338
 * to add descriptions for "rest" arguments, which are not parsed by
 * the #GOptionContext, typically something like "FILES" or
339
 * "FILE1 FILE2...". If you are using #G_OPTION_REMAINING for
340 341
 * collecting "rest" arguments, GLib handles this automatically by
 * using the @arg_description of the corresponding #GOptionEntry in
342 343 344 345 346 347 348
 * the usage summary.
 *
 * Another usage is to give a short summary of the program
 * functionality, like " - frob the strings", which will be displayed
 * in the same line as the usage. For a longer description of the
 * program functionality that should be displayed as a paragraph
 * below the usage line, use g_option_context_set_summary().
349
 *
350 351 352
 * 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.
353
 *
Matthias Clasen's avatar
Matthias Clasen committed
354 355 356 357 358
 * Returns: a newly created #GOptionContext, which must be
 *    freed with g_option_context_free() after use.
 *
 * Since: 2.6
 */
Anders Carlsson's avatar
Anders Carlsson committed
359 360 361 362 363 364 365 366 367
GOptionContext *
g_option_context_new (const gchar *parameter_string)

{
  GOptionContext *context;

  context = g_new0 (GOptionContext, 1);

  context->parameter_string = g_strdup (parameter_string);
368
  context->strict_posix = FALSE;
Anders Carlsson's avatar
Anders Carlsson committed
369
  context->help_enabled = TRUE;
Matthias Clasen's avatar
Matthias Clasen committed
370
  context->ignore_unknown = FALSE;
Anders Carlsson's avatar
Anders Carlsson committed
371 372 373 374

  return context;
}

Matthias Clasen's avatar
Matthias Clasen committed
375 376
/**
 * g_option_context_free:
377
 * @context: a #GOptionContext
Matthias Clasen's avatar
Matthias Clasen committed
378
 *
379
 * Frees context and all the groups which have been
Matthias Clasen's avatar
Matthias Clasen committed
380 381
 * added to it.
 *
382 383 384
 * Please note that parsed arguments need to be freed separately (see
 * #GOptionEntry).
 *
Matthias Clasen's avatar
Matthias Clasen committed
385 386
 * Since: 2.6
 */
387
void g_option_context_free (GOptionContext *context)
388 389
{
  g_return_if_fail (context != NULL);
Anders Carlsson's avatar
Anders Carlsson committed
390

391
  g_list_free_full (context->groups, (GDestroyNotify) g_option_group_unref);
Anders Carlsson's avatar
Anders Carlsson committed
392

393
  if (context->main_group)
394
    g_option_group_unref (context->main_group);
Anders Carlsson's avatar
Anders Carlsson committed
395 396 397

  free_changes_list (context, FALSE);
  free_pending_nulls (context, FALSE);
398

Anders Carlsson's avatar
Anders Carlsson committed
399
  g_free (context->parameter_string);
400 401
  g_free (context->summary);
  g_free (context->description);
402

403 404 405
  if (context->translate_notify)
    (* context->translate_notify) (context->translate_data);

Anders Carlsson's avatar
Anders Carlsson committed
406 407 408
  g_free (context);
}

Matthias Clasen's avatar
Matthias Clasen committed
409 410 411 412

/**
 * g_option_context_set_help_enabled:
 * @context: a #GOptionContext
Matthias Clasen's avatar
Matthias Clasen committed
413
 * @help_enabled: %TRUE to enable `--help`, %FALSE to disable it
Matthias Clasen's avatar
Matthias Clasen committed
414
 *
Matthias Clasen's avatar
Matthias Clasen committed
415 416 417 418
 * 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.
Matthias Clasen's avatar
Matthias Clasen committed
419 420 421 422 423
 *
 * Since: 2.6
 */
void g_option_context_set_help_enabled (GOptionContext *context,
                                        gboolean        help_enabled)
Anders Carlsson's avatar
Anders Carlsson committed
424 425 426 427 428 429 430

{
  g_return_if_fail (context != NULL);

  context->help_enabled = help_enabled;
}

Matthias Clasen's avatar
Matthias Clasen committed
431 432 433
/**
 * g_option_context_get_help_enabled:
 * @context: a #GOptionContext
434
 *
Matthias Clasen's avatar
Matthias Clasen committed
435
 * Returns whether automatic `--help` generation
Matthias Clasen's avatar
Matthias Clasen committed
436
 * is turned on for @context. See g_option_context_set_help_enabled().
437
 *
Matthias Clasen's avatar
Matthias Clasen committed
438 439 440 441
 * Returns: %TRUE if automatic help generation is turned on.
 *
 * Since: 2.6
 */
442 443
gboolean
g_option_context_get_help_enabled (GOptionContext *context)
Anders Carlsson's avatar
Anders Carlsson committed
444 445
{
  g_return_val_if_fail (context != NULL, FALSE);
446

Anders Carlsson's avatar
Anders Carlsson committed
447 448 449
  return context->help_enabled;
}

Matthias Clasen's avatar
Matthias Clasen committed
450 451 452 453 454
/**
 * g_option_context_set_ignore_unknown_options:
 * @context: a #GOptionContext
 * @ignore_unknown: %TRUE to ignore unknown options, %FALSE to produce
 *    an error when unknown options are met
455 456 457
 *
 * Sets whether to ignore unknown options or not. If an argument is
 * ignored, it is left in the @argv array after parsing. By default,
Matthias Clasen's avatar
Matthias Clasen committed
458
 * g_option_context_parse() treats unknown options as error.
459 460
 *
 * This setting does not affect non-option arguments (i.e. arguments
Matthias Clasen's avatar
Matthias Clasen committed
461 462
 * which don't start with a dash). But note that GOption cannot reliably
 * determine whether a non-option belongs to a preceding unknown option.
Matthias Clasen's avatar
Matthias Clasen committed
463 464 465
 *
 * Since: 2.6
 **/
Anders Carlsson's avatar
Anders Carlsson committed
466 467
void
g_option_context_set_ignore_unknown_options (GOptionContext *context,
468
                                             gboolean        ignore_unknown)
Anders Carlsson's avatar
Anders Carlsson committed
469 470 471 472 473 474
{
  g_return_if_fail (context != NULL);

  context->ignore_unknown = ignore_unknown;
}

Matthias Clasen's avatar
Matthias Clasen committed
475 476 477
/**
 * g_option_context_get_ignore_unknown_options:
 * @context: a #GOptionContext
478
 *
Matthias Clasen's avatar
Matthias Clasen committed
479 480
 * Returns whether unknown options are ignored or not. See
 * g_option_context_set_ignore_unknown_options().
481
 *
Matthias Clasen's avatar
Matthias Clasen committed
482
 * Returns: %TRUE if unknown options are ignored.
483
 *
Matthias Clasen's avatar
Matthias Clasen committed
484 485
 * Since: 2.6
 **/
Anders Carlsson's avatar
Anders Carlsson committed
486 487 488 489 490 491 492 493
gboolean
g_option_context_get_ignore_unknown_options (GOptionContext *context)
{
  g_return_val_if_fail (context != NULL, FALSE);

  return context->ignore_unknown;
}

494 495
/**
 * g_option_context_set_strict_posix:
496
 * @context: a #GOptionContext
497
 * @strict_posix: the new value
498 499 500
 *
 * Sets strict POSIX mode.
 *
501 502
 * By default, this mode is disabled.
 *
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
 * In strict POSIX mode, the first non-argument parameter encountered
 * (eg: filename) terminates argument processing.  Remaining arguments
 * are treated as non-options and are not attempted to be parsed.
 *
 * If strict POSIX mode is disabled then parsing is done in the GNU way
 * where option arguments can be freely mixed with non-options.
 *
 * As an example, consider "ls foo -l".  With GNU style parsing, this
 * will list "foo" in long mode.  In strict POSIX style, this will list
 * the files named "foo" and "-l".
 *
 * It may be useful to force strict POSIX mode when creating "verb
 * style" command line tools.  For example, the "gsettings" command line
 * tool supports the global option "--schemadir" as well as many
 * subcommands ("get", "set", etc.) which each have their own set of
 * arguments.  Using strict POSIX mode will allow parsing the global
 * options up to the verb name while leaving the remaining options to be
 * parsed by the relevant subcommand (which can be determined by
 * examining the verb name, which should be present in argv[1] after
 * parsing).
 *
 * Since: 2.44
 **/
void
g_option_context_set_strict_posix (GOptionContext *context,
                                   gboolean        strict_posix)
{
  g_return_if_fail (context != NULL);

  context->strict_posix = strict_posix;
}

/**
 * g_option_context_get_strict_posix:
537
 * @context: a #GOptionContext
538 539 540 541 542
 *
 * Returns whether strict POSIX code is enabled.
 *
 * See g_option_context_set_strict_posix() for more information.
 *
543 544
 * Returns: %TRUE if strict POSIX is enabled, %FALSE otherwise.
 *
545 546 547 548 549 550 551 552 553 554
 * Since: 2.44
 **/
gboolean
g_option_context_get_strict_posix (GOptionContext *context)
{
  g_return_val_if_fail (context != NULL, FALSE);

  return context->strict_posix;
}

Matthias Clasen's avatar
Matthias Clasen committed
555 556 557
/**
 * g_option_context_add_group:
 * @context: a #GOptionContext
558
 * @group: (transfer full): the group to add
559
 *
Matthias Clasen's avatar
Matthias Clasen committed
560
 * Adds a #GOptionGroup to the @context, so that parsing with @context
561 562
 * will recognize the options in the group. Note that this will take
 * ownership of the @group and thus the @group should not be freed.
Matthias Clasen's avatar
Matthias Clasen committed
563 564 565
 *
 * Since: 2.6
 **/
Anders Carlsson's avatar
Anders Carlsson committed
566 567
void
g_option_context_add_group (GOptionContext *context,
568
                            GOptionGroup   *group)
Anders Carlsson's avatar
Anders Carlsson committed
569
{
570 571
  GList *list;

Anders Carlsson's avatar
Anders Carlsson committed
572 573 574 575 576 577
  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);

578 579 580 581 582
  for (list = context->groups; list; list = list->next)
    {
      GOptionGroup *g = (GOptionGroup *)list->data;

      if ((group->name == NULL && g->name == NULL) ||
583 584 585
          (group->name && g->name && strcmp (group->name, g->name) == 0))
        g_warning ("A group named \"%s\" is already part of this GOptionContext",
                   group->name);
586 587 588
    }

  context->groups = g_list_append (context->groups, group);
Anders Carlsson's avatar
Anders Carlsson committed
589 590
}

Matthias Clasen's avatar
Matthias Clasen committed
591 592 593
/**
 * g_option_context_set_main_group:
 * @context: a #GOptionContext
594
 * @group: (transfer full): the group to set as main group
595 596 597 598
 *
 * 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
Matthias Clasen's avatar
Matthias Clasen committed
599
 * treated differently when generating `--help` output.
Matthias Clasen's avatar
Matthias Clasen committed
600 601 602
 *
 * Since: 2.6
 **/
Anders Carlsson's avatar
Anders Carlsson committed
603 604
void
g_option_context_set_main_group (GOptionContext *context,
605
                                 GOptionGroup   *group)
Anders Carlsson's avatar
Anders Carlsson committed
606 607 608 609
{
  g_return_if_fail (context != NULL);
  g_return_if_fail (group != NULL);

610 611 612 613 614 615
  if (context->main_group)
    {
      g_warning ("This GOptionContext already has a main group");

      return;
    }
616

Anders Carlsson's avatar
Anders Carlsson committed
617 618 619
  context->main_group = group;
}

Matthias Clasen's avatar
Matthias Clasen committed
620 621 622
/**
 * g_option_context_get_main_group:
 * @context: a #GOptionContext
623
 *
Matthias Clasen's avatar
Matthias Clasen committed
624
 * Returns a pointer to the main group of @context.
625
 *
626 627 628
 * Returns: (transfer none): 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.
Matthias Clasen's avatar
Matthias Clasen committed
629 630 631
 *
 * Since: 2.6
 **/
Anders Carlsson's avatar
Anders Carlsson committed
632 633 634 635 636 637 638 639
GOptionGroup *
g_option_context_get_main_group (GOptionContext *context)
{
  g_return_val_if_fail (context != NULL, NULL);

  return context->main_group;
}

Matthias Clasen's avatar
Matthias Clasen committed
640 641 642
/**
 * g_option_context_add_main_entries:
 * @context: a #GOptionContext
643
 * @entries: (array zero-terminated=1): a %NULL-terminated array of #GOptionEntrys
644
 * @translation_domain: (nullable): a translation domain to use for translating
Matthias Clasen's avatar
Matthias Clasen committed
645
 *    the `--help` output for the options in @entries
Matthias Clasen's avatar
Matthias Clasen committed
646
 *    with gettext(), or %NULL
647 648
 *
 * A convenience function which creates a main group if it doesn't
Matthias Clasen's avatar
Matthias Clasen committed
649
 * exist, adds the @entries to it and sets the translation domain.
650
 *
Matthias Clasen's avatar
Matthias Clasen committed
651 652
 * Since: 2.6
 **/
Anders Carlsson's avatar
Anders Carlsson committed
653 654
void
g_option_context_add_main_entries (GOptionContext      *context,
655 656
                                   const GOptionEntry  *entries,
                                   const gchar         *translation_domain)
Anders Carlsson's avatar
Anders Carlsson committed
657
{
658
  g_return_if_fail (context != NULL);
Anders Carlsson's avatar
Anders Carlsson committed
659 660 661 662
  g_return_if_fail (entries != NULL);

  if (!context->main_group)
    context->main_group = g_option_group_new (NULL, NULL, NULL, NULL, NULL);
663

Anders Carlsson's avatar
Anders Carlsson committed
664 665 666 667
  g_option_group_add_entries (context->main_group, entries);
  g_option_group_set_translation_domain (context->main_group, translation_domain);
}

668
static gint
669 670
calculate_max_length (GOptionGroup *group,
                      GHashTable   *aliases)
671 672
{
  GOptionEntry *entry;
673
  gsize i, len, max_length;
674
  const gchar *long_name;
675 676 677 678 679 680 681 682

  max_length = 0;

  for (i = 0; i < group->n_entries; i++)
    {
      entry = &group->entries[i];

      if (entry->flags & G_OPTION_FLAG_HIDDEN)
683
        continue;
684

685 686 687 688
      long_name = g_hash_table_lookup (aliases, &entry->long_name);
      if (!long_name)
        long_name = entry->long_name;
      len = _g_utf8_strwidth (long_name);
689

690
      if (entry->short_name)
691 692
        len += 4;

693
      if (!NO_ARG (entry) && entry->arg_description)
Matthias Clasen's avatar
Matthias Clasen committed
694
        len += 1 + _g_utf8_strwidth (TRANSLATE (group, entry->arg_description));
695

696 697 698 699 700 701
      max_length = MAX (max_length, len);
    }

  return max_length;
}

Anders Carlsson's avatar
Anders Carlsson committed
702 703
static void
print_entry (GOptionGroup       *group,
704 705
             gint                max_length,
             const GOptionEntry *entry,
706 707
             GString            *string,
             GHashTable         *aliases)
Anders Carlsson's avatar
Anders Carlsson committed
708 709
{
  GString *str;
710
  const gchar *long_name;
711

Anders Carlsson's avatar
Anders Carlsson committed
712 713
  if (entry->flags & G_OPTION_FLAG_HIDDEN)
    return;
714

715 716 717
  if (entry->long_name[0] == 0)
    return;

718 719 720 721
  long_name = g_hash_table_lookup (aliases, &entry->long_name);
  if (!long_name)
    long_name = entry->long_name;

Anders Carlsson's avatar
Anders Carlsson committed
722
  str = g_string_new (NULL);
723

Anders Carlsson's avatar
Anders Carlsson committed
724
  if (entry->short_name)
725
    g_string_append_printf (str, "  -%c, --%s", entry->short_name, long_name);
Anders Carlsson's avatar
Anders Carlsson committed
726
  else
727
    g_string_append_printf (str, "  --%s", long_name);
728

Anders Carlsson's avatar
Anders Carlsson committed
729 730
  if (entry->arg_description)
    g_string_append_printf (str, "=%s", TRANSLATE (group, entry->arg_description));
731

732
  g_string_append_printf (string, "%s%*s %s\n", str->str,
Matthias Clasen's avatar
Matthias Clasen committed
733
                          (int) (max_length + 4 - _g_utf8_strwidth (str->str)), "",
734 735
                          entry->description ? TRANSLATE (group, entry->description) : "");
  g_string_free (str, TRUE);
Anders Carlsson's avatar
Anders Carlsson committed
736 737
}

738
static gboolean
739 740
group_has_visible_entries (GOptionContext *context,
                           GOptionGroup *group,
741 742 743 744 745
                           gboolean      main_entries)
{
  GOptionFlags reject_filter = G_OPTION_FLAG_HIDDEN;
  GOptionEntry *entry;
  gint i, l;
746
  gboolean main_group = group == context->main_group;
747 748 749 750 751 752 753 754

  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];

755
      if (main_entries && !main_group && !(entry->flags & G_OPTION_FLAG_IN_MAIN))
756
        continue;
757 758
      if (entry->long_name[0] == 0) /* ignore rest entry */
        continue;
759 760 761 762 763 764 765 766
      if (!(entry->flags & reject_filter))
        return TRUE;
    }

  return FALSE;
}

static gboolean
Matthias Clasen's avatar
Matthias Clasen committed
767
group_list_has_visible_entries (GOptionContext *context,
768 769
                                GList          *group_list,
                                gboolean       main_entries)
770 771 772
{
  while (group_list)
    {
773
      if (group_has_visible_entries (context, group_list->data, main_entries))
774 775 776 777 778 779 780 781
        return TRUE;

      group_list = group_list->next;
    }

  return FALSE;
}

782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810
static gboolean
context_has_h_entry (GOptionContext *context)
{
  gsize i;
  GList *list;

  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;
}

811
/**
812
 * g_option_context_get_help:
813
 * @context: a #GOptionContext
814
 * @main_help: if %TRUE, only include the main group
815
 * @group: (nullable): the #GOptionGroup to create help for, or %NULL
816 817
 *
 * Returns a formatted, translated help text for the given context.
Matthias Clasen's avatar
Matthias Clasen committed
818
 * To obtain the text produced by `--help`, call
Matthias Clasen's avatar
Matthias Clasen committed
819
 * `g_option_context_get_help (context, TRUE, NULL)`.
Matthias Clasen's avatar
Matthias Clasen committed
820
 * To obtain the text produced by `--help-all`, call
Matthias Clasen's avatar
Matthias Clasen committed
821
 * `g_option_context_get_help (context, FALSE, NULL)`.
822
 * To obtain the help text for an option group, call
Matthias Clasen's avatar
Matthias Clasen committed
823
 * `g_option_context_get_help (context, FALSE, group)`.
824 825 826 827 828 829 830
 *
 * Returns: A newly allocated string containing the help text
 *
 * Since: 2.14
 */
gchar *
g_option_context_get_help (GOptionContext *context,
831 832
                           gboolean        main_help,
                           GOptionGroup   *group)
Anders Carlsson's avatar
Anders Carlsson committed
833 834
{
  GList *list;
835
  gint max_length = 0, len;
836
  gsize i;
837
  GOptionEntry *entry;
838
  GHashTable *shadow_map;
839
  GHashTable *aliases;
840
  gboolean seen[256];
841
  const gchar *rest_description;
842
  GString *string;
843
  guchar token;
844

845 846
  g_return_val_if_fail (context != NULL, NULL);

847 848
  string = g_string_sized_new (1024);

849 850 851
  rest_description = NULL;
  if (context->main_group)
    {
852

853
      for (i = 0; i < context->main_group->n_entries; i++)
854 855 856 857 858 859 860 861
        {
          entry = &context->main_group->entries[i];
          if (entry->long_name[0] == 0)
            {
              rest_description = TRANSLATE (context->main_group, entry->arg_description);
              break;
            }
        }
862
    }
863

864
  g_string_append_printf (string, "%s\n  %s", _("Usage:"), g_get_prgname ());
865
  if (context->help_enabled ||
866
      (context->main_group && context->main_group->n_entries > 0) ||
867
      context->groups != NULL)
868
    g_string_append_printf (string, " %s", _("[OPTION…]"));
869 870 871

  if (rest_description)
    {
872 873
      g_string_append (string, " ");
      g_string_append (string, rest_description);
874 875 876 877
    }

  if (context->parameter_string)
    {
878 879
      g_string_append (string, " ");
      g_string_append (string, TRANSLATE (context, context->parameter_string));
880 881 882
    }

  g_string_append (string, "\n\n");
883 884

  if (context->summary)
885 886 887 888
    {
      g_string_append (string, TRANSLATE (context, context->summary));
      g_string_append (string, "\n\n");
    }
Anders Carlsson's avatar
Anders Carlsson committed
889

890 891
  memset (seen, 0, sizeof (gboolean) * 256);
  shadow_map = g_hash_table_new (g_str_hash, g_str_equal);
892
  aliases = g_hash_table_new_full (NULL, NULL, NULL, g_free);
893 894 895 896

  if (context->main_group)
    {
      for (i = 0; i < context->main_group->n_entries; i++)
897 898 899 900 901 902 903 904 905 906 907
        {
          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;
        }
908 909 910 911 912
    }

  list = context->groups;
  while (list != NULL)
    {
Matthias Clasen's avatar
Matthias Clasen committed
913 914
      GOptionGroup *g = list->data;
      for (i = 0; i < g->n_entries; i++)
915 916 917 918
        {
          entry = &g->entries[i];
          if (g_hash_table_lookup (shadow_map, entry->long_name) &&
              !(entry->flags & G_OPTION_FLAG_NOALIAS))
919 920 921 922
            {
              g_hash_table_insert (aliases, &entry->long_name,
                                   g_strdup_printf ("%s-%s", g->name, entry->long_name));
            }
923 924 925 926 927 928 929 930 931
          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;
        }
932 933 934 935 936
      list = list->next;
    }

  g_hash_table_destroy (shadow_map);

Anders Carlsson's avatar
Anders Carlsson committed
937 938
  list = context->groups;

939
  if (context->help_enabled)
Anders Carlsson's avatar
Anders Carlsson committed
940
    {
941 942 943 944 945 946 947
      max_length = _g_utf8_strwidth ("-?, --help");

      if (list)
        {
          len = _g_utf8_strwidth ("--help-all");
          max_length = MAX (max_length, len);
        }
Anders Carlsson's avatar
Anders Carlsson committed
948 949
    }

950 951
  if (context->main_group)
    {
952
      len = calculate_max_length (context->main_group, aliases);
953 954 955
      max_length = MAX (max_length, len);
    }

Anders Carlsson's avatar
Anders Carlsson committed
956 957
  while (list != NULL)
    {
Matthias Clasen's avatar
Matthias Clasen committed
958
      GOptionGroup *g = list->data;
959

960 961 962 963 964 965
      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);
        }
Anders Carlsson's avatar
Anders Carlsson committed
966 967

      /* Then we go through the entries */
968
      len = calculate_max_length (g, aliases);
969
      max_length = MAX (max_length, len);
970

Anders Carlsson's avatar
Anders Carlsson committed
971 972 973 974 975 976
      list = list->next;
    }

  /* Add a bit of padding */
  max_length += 4;

977
  if (!group && context->help_enabled)
Anders Carlsson's avatar
Anders Carlsson committed
978
    {
979
      list = context->groups;
980 981 982

      token = context_has_h_entry (context) ? '?' : 'h';

983 984 985 986
      g_string_append_printf (string, "%s\n  -%c, --%-*s %s\n",
                              _("Help Options:"), token, max_length - 4, "help",
                              _("Show help options"));

987 988
      /* We only want --help-all when there are groups */
      if (list)
989 990
        g_string_append_printf (string, "  --%-*s %s\n",
                                max_length, "help-all",
991
                                _("Show all help options"));
992

993
      while (list)
994 995 996 997 998 999 1000
        {
          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));
1001

1002 1003
          list = list->next;
        }
1004

1005
      g_string_append (string, "\n");
1006
    }
Anders Carlsson's avatar
Anders Carlsson committed
1007 1008 1009 1010

  if (group)
    {
      /* Print a certain group */
1011

1012
      if (group_has_visible_entries (context, group, FALSE))
1013 1014 1015 1016
        {
          g_string_append (string, TRANSLATE (group, group->description));
          g_string_append (string, "\n");
          for (i = 0; i < group->n_entries; i++)
1017
            print_entry (group, max_length, &group->entries[i], string, aliases);
1018 1019
          g_string_append (string, "\n");
        }
Anders Carlsson's avatar
Anders Carlsson committed
1020 1021 1022 1023 1024 1025 1026 1027
    }
  else if (!main_help)
    {
      /* Print all groups */

      list = context->groups;

      while (list)
1028 1029 1030 1031 1032 1033 1034 1035 1036
        {
          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;