goption.c 69.4 KB
Newer Older
Anders Carlsson's avatar
Anders Carlsson committed
1 2 3 4 5 6 7 8 9 10 11 12
/* 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
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * 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
Anders Carlsson's avatar
Anders Carlsson committed
14 15 16 17 18 19 20
 * 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.
 */
21

22 23 24 25
/**
 * SECTION:option
 * @Short_description: parses commandline options
 * @Title: Commandline option parser
26 27 28 29 30
 *
 * 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:
 *
31
 * <literal>testtreemodel -r 1 --max-size 20 --rand --display=:1.0 -vb -- file1 file2</literal>
32
 *
33 34
 * The example demonstrates a number of features of the GOption
 * commandline parser
35
 * <itemizedlist><listitem><para>
36 37
 *   Options can be single letters, prefixed by a single dash. Multiple
 *   short options can be grouped behind a single dash.
38
 * </para></listitem><listitem><para>
39
 *   Long options are prefixed by two consecutive dashes.
40
 * </para></listitem><listitem><para>
41 42 43 44 45
 *   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.
46
 * </para></listitem><listitem><para>
47
 *   Non-option arguments are returned to the application as rest arguments.
48
 * </para></listitem><listitem><para>
49 50
 *   An argument consisting solely of two dashes turns off further parsing,
 *   any remaining arguments (even those starting with a dash) are returned
51
 *   to the application as rest arguments.
52
 * </para></listitem></itemizedlist>
53
 *
54 55 56 57 58 59 60 61 62
 * 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 <option>--help</option>, <option>-?</option>,
 * <option>--help-all</option> and
 * <option>--help-</option><replaceable>groupname</replaceable> options
 * (where <replaceable>groupname</replaceable> is the name of a
 * #GOptionGroup) and write a text similar to the one shown in the
 * following example to stdout.
63
 *
64 65 66
 * <informalexample><screen>
 * Usage:
 *   testtreemodel [OPTION...] - test tree model performance
67
 *  
68
 * Help Options:
69
 *   -h, --help               Show help options
70 71
 *   --help-all               Show all help options
 *   --help-gtk               Show GTK+ Options
72
 *  
73 74 75 76 77
 * 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
78
 *   -b, --beep               Beep when done
79 80
 *   --rand                   Randomize the data
 * </screen></informalexample>
81
 *
82 83 84 85 86
 * GOption groups options in #GOptionGroup<!-- -->s, 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.
87
 *
88
 * If an option is declared to be of type string or filename, GOption takes
89 90 91 92 93
 * 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().
 *
94 95
 * Here is a complete example of setting up GOption to parse the example
 * commandline above and produce the example help output.
96
 *
97 98 99 100 101 102
 * <informalexample><programlisting>
 * static gint repeats = 2;
 * static gint max_size = 8;
 * static gboolean verbose = FALSE;
 * static gboolean beep = FALSE;
 * static gboolean rand = FALSE;
103 104
 *
 * static GOptionEntry entries[] =
105 106 107 108 109 110 111 112
 * {
 *   { "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, &rand, "Randomize the data", NULL },
 *   { NULL }
 * };
113 114
 *
 * int
115 116 117 118
 * main (int argc, char *argv[])
 * {
 *   GError *error = NULL;
 *   GOptionContext *context;
119
 *
120 121 122 123 124 125 126 127
 *   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);
 *     }
128 129 130
 *
 *   /&ast; ... &ast;/
 *
131 132 133
 * }
 * </programlisting></informalexample>
 */
Anders Carlsson's avatar
Anders Carlsson committed
134

135
#include "config.h"
Anders Carlsson's avatar
Anders Carlsson committed
136 137 138

#include <string.h>
#include <stdlib.h>
139
#include <stdio.h>
Anders Carlsson's avatar
Anders Carlsson committed
140 141
#include <errno.h>

142 143 144 145 146 147 148
#if defined __OpenBSD__
#include <sys/types.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#endif

149 150 151 152 153
#include "goption.h"

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

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

156 157 158 159
#define NO_ARG(entry) ((entry)->arg == G_OPTION_ARG_NONE ||       \
                       ((entry)->arg == G_OPTION_ARG_CALLBACK &&  \
                        ((entry)->flags & G_OPTION_FLAG_NO_ARG)))

160 161 162
#define OPTIONAL_ARG(entry) ((entry)->arg == G_OPTION_ARG_CALLBACK &&  \
                       (entry)->flags & G_OPTION_FLAG_OPTIONAL_ARG)

163
typedef struct
164
{
Anders Carlsson's avatar
Anders Carlsson committed
165
  GOptionArg arg_type;
166 167
  gpointer arg_data;
  union
168
  {
Anders Carlsson's avatar
Anders Carlsson committed
169 170 171 172
    gboolean bool;
    gint integer;
    gchar *str;
    gchar **array;
173
    gdouble dbl;
174
    gint64 int64;
Anders Carlsson's avatar
Anders Carlsson committed
175
  } prev;
176
  union
177
  {
Anders Carlsson's avatar
Anders Carlsson committed
178
    gchar *str;
179
    struct
180
    {
181
      gint len;
Anders Carlsson's avatar
Anders Carlsson committed
182 183 184 185 186 187 188 189 190 191 192 193 194
      gchar **data;
    } array;
  } allocated;
} Change;

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

struct _GOptionContext
{
195
  GList           *groups;
Anders Carlsson's avatar
Anders Carlsson committed
196

197 198 199
  gchar           *parameter_string;
  gchar           *summary;
  gchar           *description;
Anders Carlsson's avatar
Anders Carlsson committed
200

201 202
  GTranslateFunc   translate_func;
  GDestroyNotify   translate_notify;
203
  gpointer         translate_data;
204 205 206

  guint            help_enabled   : 1;
  guint            ignore_unknown : 1;
207

208
  GOptionGroup    *main_group;
Anders Carlsson's avatar
Anders Carlsson committed
209 210

  /* We keep a list of change so we can revert them */
211
  GList           *changes;
212 213

  /* We also keep track of all argv elements
214
   * that should be NULLed or modified.
Anders Carlsson's avatar
Anders Carlsson committed
215
   */
216
  GList           *pending_nulls;
Anders Carlsson's avatar
Anders Carlsson committed
217 218 219 220
};

struct _GOptionGroup
{
221 222 223
  gchar           *name;
  gchar           *description;
  gchar           *help_description;
Anders Carlsson's avatar
Anders Carlsson committed
224

225 226
  GDestroyNotify   destroy_notify;
  gpointer         user_data;
Anders Carlsson's avatar
Anders Carlsson committed
227

228 229
  GTranslateFunc   translate_func;
  GDestroyNotify   translate_notify;
230
  gpointer         translate_data;
Anders Carlsson's avatar
Anders Carlsson committed
231

232 233
  GOptionEntry    *entries;
  gint             n_entries;
Anders Carlsson's avatar
Anders Carlsson committed
234 235 236 237 238 239 240

  GOptionParseFunc pre_parse_func;
  GOptionParseFunc post_parse_func;
  GOptionErrorFunc error_func;
};

static void free_changes_list (GOptionContext *context,
241
                               gboolean        revert);
Anders Carlsson's avatar
Anders Carlsson committed
242
static void free_pending_nulls (GOptionContext *context,
243
                                gboolean        perform_nulls);
Anders Carlsson's avatar
Anders Carlsson committed
244

245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260

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
261
_g_utf8_strwidth (const gchar *p)
262 263
{
  glong len = 0;
Matthias Clasen's avatar
Matthias Clasen committed
264
  g_return_val_if_fail (p != NULL, 0);
265

Matthias Clasen's avatar
Matthias Clasen committed
266
  while (*p)
267 268
    {
      len += _g_unichar_get_width (g_utf8_get_char (p));
269
      p = g_utf8_next_char (p);
270 271 272 273 274
    }

  return len;
}

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

Matthias Clasen's avatar
Matthias Clasen committed
277 278
/**
 * g_option_context_new:
Matthias Clasen's avatar
Matthias Clasen committed
279
 * @parameter_string: a string which is displayed in
280
 *    the first line of <option>--help</option> output, after the
281
 *    usage summary
Matthias Clasen's avatar
Matthias Clasen committed
282
 *    <literal><replaceable>programname</replaceable> [OPTION...]</literal>
Matthias Clasen's avatar
Matthias Clasen committed
283
 *
284
 * Creates a new option context.
Matthias Clasen's avatar
Matthias Clasen committed
285
 *
286
 * The @parameter_string can serve multiple purposes. It can be used
287 288
 * to add descriptions for "rest" arguments, which are not parsed by
 * the #GOptionContext, typically something like "FILES" or
289
 * "FILE1 FILE2...". If you are using #G_OPTION_REMAINING for
290 291
 * collecting "rest" arguments, GLib handles this automatically by
 * using the @arg_description of the corresponding #GOptionEntry in
292 293 294 295 296 297 298
 * 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().
299
 *
300 301 302
 * 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.
303
 *
Matthias Clasen's avatar
Matthias Clasen committed
304 305 306 307 308
 * 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
309 310 311 312 313 314 315 316 317 318
GOptionContext *
g_option_context_new (const gchar *parameter_string)

{
  GOptionContext *context;

  context = g_new0 (GOptionContext, 1);

  context->parameter_string = g_strdup (parameter_string);
  context->help_enabled = TRUE;
Matthias Clasen's avatar
Matthias Clasen committed
319
  context->ignore_unknown = FALSE;
Anders Carlsson's avatar
Anders Carlsson committed
320 321 322 323

  return context;
}

Matthias Clasen's avatar
Matthias Clasen committed
324 325
/**
 * g_option_context_free:
326
 * @context: a #GOptionContext
Matthias Clasen's avatar
Matthias Clasen committed
327
 *
328
 * Frees context and all the groups which have been
Matthias Clasen's avatar
Matthias Clasen committed
329 330
 * added to it.
 *
331 332 333
 * Please note that parsed arguments need to be freed separately (see
 * #GOptionEntry).
 *
Matthias Clasen's avatar
Matthias Clasen committed
334 335
 * Since: 2.6
 */
336
void g_option_context_free (GOptionContext *context)
337 338
{
  g_return_if_fail (context != NULL);
Anders Carlsson's avatar
Anders Carlsson committed
339

340
  g_list_free_full (context->groups, (GDestroyNotify) g_option_group_free);
Anders Carlsson's avatar
Anders Carlsson committed
341

342
  if (context->main_group)
343
    g_option_group_free (context->main_group);
Anders Carlsson's avatar
Anders Carlsson committed
344 345 346

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

Anders Carlsson's avatar
Anders Carlsson committed
348
  g_free (context->parameter_string);
349 350
  g_free (context->summary);
  g_free (context->description);
351

352 353 354
  if (context->translate_notify)
    (* context->translate_notify) (context->translate_data);

Anders Carlsson's avatar
Anders Carlsson committed
355 356 357
  g_free (context);
}

Matthias Clasen's avatar
Matthias Clasen committed
358 359 360 361 362 363

/**
 * g_option_context_set_help_enabled:
 * @context: a #GOptionContext
 * @help_enabled: %TRUE to enable <option>--help</option>, %FALSE to disable it
 *
364
 * Enables or disables automatic generation of <option>--help</option>
Matthias Clasen's avatar
Matthias Clasen committed
365
 * output. By default, g_option_context_parse() recognizes
366 367
 * <option>--help</option>, <option>-h</option>,
 * <option>-?</option>, <option>--help-all</option>
Matthias Clasen's avatar
Matthias Clasen committed
368
 * and <option>--help-</option><replaceable>groupname</replaceable> and creates
369
 * suitable output to stdout.
Matthias Clasen's avatar
Matthias Clasen committed
370 371 372 373 374
 *
 * Since: 2.6
 */
void g_option_context_set_help_enabled (GOptionContext *context,
                                        gboolean        help_enabled)
Anders Carlsson's avatar
Anders Carlsson committed
375 376 377 378 379 380 381

{
  g_return_if_fail (context != NULL);

  context->help_enabled = help_enabled;
}

Matthias Clasen's avatar
Matthias Clasen committed
382 383 384
/**
 * g_option_context_get_help_enabled:
 * @context: a #GOptionContext
385
 *
Matthias Clasen's avatar
Matthias Clasen committed
386 387
 * Returns whether automatic <option>--help</option> generation
 * is turned on for @context. See g_option_context_set_help_enabled().
388
 *
Matthias Clasen's avatar
Matthias Clasen committed
389 390 391 392
 * Returns: %TRUE if automatic help generation is turned on.
 *
 * Since: 2.6
 */
393 394
gboolean
g_option_context_get_help_enabled (GOptionContext *context)
Anders Carlsson's avatar
Anders Carlsson committed
395 396
{
  g_return_val_if_fail (context != NULL, FALSE);
397

Anders Carlsson's avatar
Anders Carlsson committed
398 399 400
  return context->help_enabled;
}

Matthias Clasen's avatar
Matthias Clasen committed
401 402 403 404 405
/**
 * 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
406 407 408
 *
 * 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
409
 * g_option_context_parse() treats unknown options as error.
410 411
 *
 * This setting does not affect non-option arguments (i.e. arguments
Matthias Clasen's avatar
Matthias Clasen committed
412 413
 * 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
414 415 416
 *
 * Since: 2.6
 **/
Anders Carlsson's avatar
Anders Carlsson committed
417 418
void
g_option_context_set_ignore_unknown_options (GOptionContext *context,
419
                                             gboolean        ignore_unknown)
Anders Carlsson's avatar
Anders Carlsson committed
420 421 422 423 424 425
{
  g_return_if_fail (context != NULL);

  context->ignore_unknown = ignore_unknown;
}

Matthias Clasen's avatar
Matthias Clasen committed
426 427 428
/**
 * g_option_context_get_ignore_unknown_options:
 * @context: a #GOptionContext
429
 *
Matthias Clasen's avatar
Matthias Clasen committed
430 431
 * Returns whether unknown options are ignored or not. See
 * g_option_context_set_ignore_unknown_options().
432
 *
Matthias Clasen's avatar
Matthias Clasen committed
433
 * Returns: %TRUE if unknown options are ignored.
434
 *
Matthias Clasen's avatar
Matthias Clasen committed
435 436
 * Since: 2.6
 **/
Anders Carlsson's avatar
Anders Carlsson committed
437 438 439 440 441 442 443 444
gboolean
g_option_context_get_ignore_unknown_options (GOptionContext *context)
{
  g_return_val_if_fail (context != NULL, FALSE);

  return context->ignore_unknown;
}

Matthias Clasen's avatar
Matthias Clasen committed
445 446 447 448
/**
 * g_option_context_add_group:
 * @context: a #GOptionContext
 * @group: the group to add
449
 *
Matthias Clasen's avatar
Matthias Clasen committed
450 451 452 453 454 455 456 457
 * 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
 * called, so you must not free the group yourself after adding it
 * to a context.
 *
 * Since: 2.6
 **/
Anders Carlsson's avatar
Anders Carlsson committed
458 459
void
g_option_context_add_group (GOptionContext *context,
460
                            GOptionGroup   *group)
Anders Carlsson's avatar
Anders Carlsson committed
461
{
462 463
  GList *list;

Anders Carlsson's avatar
Anders Carlsson committed
464 465 466 467 468 469
  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);

470 471 472 473 474
  for (list = context->groups; list; list = list->next)
    {
      GOptionGroup *g = (GOptionGroup *)list->data;

      if ((group->name == NULL && g->name == NULL) ||
475 476 477
          (group->name && g->name && strcmp (group->name, g->name) == 0))
        g_warning ("A group named \"%s\" is already part of this GOptionContext",
                   group->name);
478 479 480
    }

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

Matthias Clasen's avatar
Matthias Clasen committed
483 484 485 486
/**
 * g_option_context_set_main_group:
 * @context: a #GOptionContext
 * @group: the group to set as main group
487 488 489 490
 *
 * 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
491 492 493 494
 * treated differently when generating <option>--help</option> output.
 *
 * Since: 2.6
 **/
Anders Carlsson's avatar
Anders Carlsson committed
495 496
void
g_option_context_set_main_group (GOptionContext *context,
497
                                 GOptionGroup   *group)
Anders Carlsson's avatar
Anders Carlsson committed
498 499 500 501
{
  g_return_if_fail (context != NULL);
  g_return_if_fail (group != NULL);

502 503 504 505 506 507
  if (context->main_group)
    {
      g_warning ("This GOptionContext already has a main group");

      return;
    }
508

Anders Carlsson's avatar
Anders Carlsson committed
509 510 511
  context->main_group = group;
}

Matthias Clasen's avatar
Matthias Clasen committed
512 513 514
/**
 * g_option_context_get_main_group:
 * @context: a #GOptionContext
515
 *
Matthias Clasen's avatar
Matthias Clasen committed
516
 * Returns a pointer to the main group of @context.
517
 *
Matthias Clasen's avatar
Matthias Clasen committed
518 519 520 521 522 523
 * Return value: 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.
 *
 * Since: 2.6
 **/
Anders Carlsson's avatar
Anders Carlsson committed
524 525 526 527 528 529 530 531
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
532 533 534 535
/**
 * g_option_context_add_main_entries:
 * @context: a #GOptionContext
 * @entries: a %NULL-terminated array of #GOptionEntry<!-- -->s
536
 * @translation_domain: (allow-none): a translation domain to use for translating
Matthias Clasen's avatar
Matthias Clasen committed
537 538
 *    the <option>--help</option> output for the options in @entries
 *    with gettext(), or %NULL
539 540
 *
 * A convenience function which creates a main group if it doesn't
Matthias Clasen's avatar
Matthias Clasen committed
541
 * exist, adds the @entries to it and sets the translation domain.
542
 *
Matthias Clasen's avatar
Matthias Clasen committed
543 544
 * Since: 2.6
 **/
Anders Carlsson's avatar
Anders Carlsson committed
545 546
void
g_option_context_add_main_entries (GOptionContext      *context,
547 548
                                   const GOptionEntry  *entries,
                                   const gchar         *translation_domain)
Anders Carlsson's avatar
Anders Carlsson committed
549 550 551 552 553
{
  g_return_if_fail (entries != NULL);

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

Anders Carlsson's avatar
Anders Carlsson committed
555 556 557 558
  g_option_group_add_entries (context->main_group, entries);
  g_option_group_set_translation_domain (context->main_group, translation_domain);
}

559 560 561 562 563 564 565 566 567 568 569 570 571
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)
572
        continue;
573

Matthias Clasen's avatar
Matthias Clasen committed
574
      len = _g_utf8_strwidth (entry->long_name);
575

576
      if (entry->short_name)
577 578
        len += 4;

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

582 583 584 585 586 587
      max_length = MAX (max_length, len);
    }

  return max_length;
}

Anders Carlsson's avatar
Anders Carlsson committed
588 589
static void
print_entry (GOptionGroup       *group,
590 591
             gint                max_length,
             const GOptionEntry *entry,
592
             GString            *string)
Anders Carlsson's avatar
Anders Carlsson committed
593 594
{
  GString *str;
595

Anders Carlsson's avatar
Anders Carlsson committed
596 597
  if (entry->flags & G_OPTION_FLAG_HIDDEN)
    return;
598

599 600 601
  if (entry->long_name[0] == 0)
    return;

Anders Carlsson's avatar
Anders Carlsson committed
602
  str = g_string_new (NULL);
603

Anders Carlsson's avatar
Anders Carlsson committed
604 605 606 607
  if (entry->short_name)
    g_string_append_printf (str, "  -%c, --%s", entry->short_name, entry->long_name);
  else
    g_string_append_printf (str, "  --%s", entry->long_name);
608

Anders Carlsson's avatar
Anders Carlsson committed
609 610
  if (entry->arg_description)
    g_string_append_printf (str, "=%s", TRANSLATE (group, entry->arg_description));
611

612
  g_string_append_printf (string, "%s%*s %s\n", str->str,
Matthias Clasen's avatar
Matthias Clasen committed
613
                          (int) (max_length + 4 - _g_utf8_strwidth (str->str)), "",
614 615
                          entry->description ? TRANSLATE (group, entry->description) : "");
  g_string_free (str, TRUE);
Anders Carlsson's avatar
Anders Carlsson committed
616 617
}

618
static gboolean
619 620
group_has_visible_entries (GOptionContext *context,
                           GOptionGroup *group,
621 622 623 624 625
                           gboolean      main_entries)
{
  GOptionFlags reject_filter = G_OPTION_FLAG_HIDDEN;
  GOptionEntry *entry;
  gint i, l;
626
  gboolean main_group = group == context->main_group;
627 628 629 630 631 632 633 634

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

635
      if (main_entries && !main_group && !(entry->flags & G_OPTION_FLAG_IN_MAIN))
636
        continue;
637 638
      if (entry->long_name[0] == 0) /* ignore rest entry */
        continue;
639 640 641 642 643 644 645 646
      if (!(entry->flags & reject_filter))
        return TRUE;
    }

  return FALSE;
}

static gboolean
Matthias Clasen's avatar
Matthias Clasen committed
647
group_list_has_visible_entries (GOptionContext *context,
648 649
                                GList          *group_list,
                                gboolean       main_entries)
650 651 652
{
  while (group_list)
    {
653
      if (group_has_visible_entries (context, group_list->data, main_entries))
654 655 656 657 658 659 660 661
        return TRUE;

      group_list = group_list->next;
    }

  return FALSE;
}

662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690
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;
}

691
/**
692
 * g_option_context_get_help:
693
 * @context: a #GOptionContext
694
 * @main_help: if %TRUE, only include the main group
695
 * @group: (allow-none): the #GOptionGroup to create help for, or %NULL
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710
 *
 * Returns a formatted, translated help text for the given context.
 * To obtain the text produced by <option>--help</option>, call
 * <literal>g_option_context_get_help (context, TRUE, NULL)</literal>.
 * To obtain the text produced by <option>--help-all</option>, call
 * <literal>g_option_context_get_help (context, FALSE, NULL)</literal>.
 * To obtain the help text for an option group, call
 * <literal>g_option_context_get_help (context, FALSE, group)</literal>.
 *
 * Returns: A newly allocated string containing the help text
 *
 * Since: 2.14
 */
gchar *
g_option_context_get_help (GOptionContext *context,
711 712
                           gboolean        main_help,
                           GOptionGroup   *group)
Anders Carlsson's avatar
Anders Carlsson committed
713 714 715 716
{
  GList *list;
  gint max_length, len;
  gint i;
717
  GOptionEntry *entry;
718 719
  GHashTable *shadow_map;
  gboolean seen[256];
720
  const gchar *rest_description;
721
  GString *string;
722
  guchar token;
723 724 725

  string = g_string_sized_new (1024);

726 727 728
  rest_description = NULL;
  if (context->main_group)
    {
729

730
      for (i = 0; i < context->main_group->n_entries; i++)
731 732 733 734 735 736 737 738
        {
          entry = &context->main_group->entries[i];
          if (entry->long_name[0] == 0)
            {
              rest_description = TRANSLATE (context->main_group, entry->arg_description);
              break;
            }
        }
739
    }
740

741 742
  g_string_append_printf (string, "%s\n  %s %s",
                          _("Usage:"), g_get_prgname(), _("[OPTION...]"));
743 744 745

  if (rest_description)
    {
746 747
      g_string_append (string, " ");
      g_string_append (string, rest_description);
748 749 750 751
    }

  if (context->parameter_string)
    {
752 753
      g_string_append (string, " ");
      g_string_append (string, TRANSLATE (context, context->parameter_string));
754 755 756
    }

  g_string_append (string, "\n\n");
757 758

  if (context->summary)
759 760 761 762
    {
      g_string_append (string, TRANSLATE (context, context->summary));
      g_string_append (string, "\n\n");
    }
Anders Carlsson's avatar
Anders Carlsson committed
763

764 765 766 767 768 769
  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++)
770 771 772 773 774 775 776 777 778 779 780
        {
          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;
        }
781 782 783 784 785
    }

  list = context->groups;
  while (list != NULL)
    {
Matthias Clasen's avatar
Matthias Clasen committed
786 787
      GOptionGroup *g = list->data;
      for (i = 0; i < g->n_entries; i++)
788 789 790 791 792 793 794 795 796 797 798 799 800 801
        {
          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);
          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;
        }
802 803 804 805 806
      list = list->next;
    }

  g_hash_table_destroy (shadow_map);

Anders Carlsson's avatar
Anders Carlsson committed
807 808
  list = context->groups;

Matthias Clasen's avatar
Matthias Clasen committed
809
  max_length = _g_utf8_strwidth ("-?, --help");
Anders Carlsson's avatar
Anders Carlsson committed
810 811 812

  if (list)
    {
Matthias Clasen's avatar
Matthias Clasen committed
813
      len = _g_utf8_strwidth ("--help-all");
Anders Carlsson's avatar
Anders Carlsson committed
814 815 816
      max_length = MAX (max_length, len);
    }

817 818 819 820 821 822
  if (context->main_group)
    {
      len = calculate_max_length (context->main_group);
      max_length = MAX (max_length, len);
    }

Anders Carlsson's avatar
Anders Carlsson committed
823 824
  while (list != NULL)
    {
Matthias Clasen's avatar
Matthias Clasen committed
825
      GOptionGroup *g = list->data;
826

Anders Carlsson's avatar
Anders Carlsson committed
827
      /* First, we check the --help-<groupname> options */
Matthias Clasen's avatar
Matthias Clasen committed
828
      len = _g_utf8_strwidth ("--help-") + _g_utf8_strwidth (g->name);
Anders Carlsson's avatar
Anders Carlsson committed
829 830 831
      max_length = MAX (max_length, len);

      /* Then we go through the entries */
Matthias Clasen's avatar
Matthias Clasen committed
832
      len = calculate_max_length (g);
833
      max_length = MAX (max_length, len);
834

Anders Carlsson's avatar
Anders Carlsson committed
835 836 837 838 839 840
      list = list->next;
    }

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

841
  if (!group)
Anders Carlsson's avatar
Anders Carlsson committed
842
    {
843
      list = context->groups;
844 845 846

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

847 848 849 850
      g_string_append_printf (string, "%s\n  -%c, --%-*s %s\n",
                              _("Help Options:"), token, max_length - 4, "help",
                              _("Show help options"));

851 852
      /* We only want --help-all when there are groups */
      if (list)
853 854
        g_string_append_printf (string, "  --%-*s %s\n",
                                max_length, "help-all",
855
                                _("Show all help options"));
856

857
      while (list)
858 859 860 861 862 863 864
        {
          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));
865

866 867
          list = list->next;
        }
868

869
      g_string_append (string, "\n");
870
    }
Anders Carlsson's avatar
Anders Carlsson committed
871 872 873 874

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

876
      if (group_has_visible_entries (context, group, FALSE))
877 878 879 880 881 882 883
        {
          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);
          g_string_append (string, "\n");
        }
Anders Carlsson's avatar
Anders Carlsson committed
884 885 886 887 888 889 890 891
    }
  else if (!main_help)
    {
      /* Print all groups */

      list = context->groups;

      while (list)
892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907
        {
          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);

              g_string_append (string, "\n");
            }

          list = list->next;
        }
Anders Carlsson's avatar
Anders Carlsson committed
908
    }
909

Anders Carlsson's avatar
Anders Carlsson committed
910
  /* Print application options if --help or --help-all has been specified */
911
  if ((main_help || !group) &&
912
      (group_has_visible_entries (context, context->main_group, TRUE) ||
Matthias Clasen's avatar
Matthias Clasen committed
913
       group_list_has_visible_entries (context, context->groups, TRUE)))
Anders Carlsson's avatar
Anders Carlsson committed
914 915 916
    {
      list = context->groups;

917 918
      g_string_append (string,  _("Application Options:"));
      g_string_append (string, "\n");
919
      if (context->main_group)
920 921 922
        for (i = 0; i < context->main_group->n_entries; i++)
          print_entry (context->main_group, max_length,
                       &context->main_group->entries[i], string);
Anders Carlsson's avatar
Anders Carlsson committed
923 924

      while (list != NULL)
925 926 927 928 929 930 931
        {
          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);
Anders Carlsson's avatar
Anders Carlsson committed
932

933 934
          list = list->next;
        }
Anders Carlsson's avatar
Anders Carlsson committed
935

936
      g_string_append (string, "\n");
Anders Carlsson's avatar
Anders Carlsson committed
937
    }
938

939
  if (context->description)
940 941 942 943 944 945 946 947
    {
      g_string_append (string, TRANSLATE (context, context->description));
      g_string_append (string, "\n");
    }

  return g_string_free (string, FALSE);
}

Matthias Clasen's avatar
Matthias Clasen committed
948
G_GNUC_NORETURN
949 950
static void
print_help (GOptionContext *context,
951 952
            gboolean        main_help,
            GOptionGroup   *group)
953 954 955 956
{
  gchar *help;

  help = g_option_context_get_help (context, main_help, group);
957
  g_print ("%s", help);
958 959
  g_free (help);

960
  exit (0);
Anders Carlsson's avatar
Anders Carlsson committed
961 962 963
}

static gboolean
964
parse_int (const gchar *arg_name,
965 966 967
           const gchar *arg,
           gint        *result,
           GError     **error)
Anders Carlsson's avatar
Anders Carlsson committed
968 969
{
  gchar *end;
970
  glong tmp;
Anders Carlsson's avatar
Anders Carlsson committed
971 972

  errno = 0;
973
  tmp = strtol (arg, &end, 0);
974

Anders Carlsson's avatar
Anders Carlsson committed
975 976 977
  if (*arg == '\0' || *end != '\0')
    {
      g_set_error (error,
978 979 980
                   G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
                   _("Cannot parse integer value '%s' for %s"),
                   arg, arg_name);
Anders Carlsson's avatar
Anders Carlsson committed
981 982 983 984