/* Hierarchical argument parsing help output
- Copyright (C) 1995-2005, 2007, 2009-2016 Free Software Foundation, Inc.
+ Copyright (C) 1995-2016 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Written by Miles Bader <miles@gnu.ai.mit.edu>.
#include <alloca.h>
#include <errno.h>
+#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
-#include <strings.h>
#include <assert.h>
#include <stdarg.h>
#include <ctype.h>
#include <limits.h>
-#ifdef USE_IN_LIBIO
+#ifdef _LIBC
+# include <../libio/libioP.h>
# include <wchar.h>
#endif
# include <libintl.h>
# undef dgettext
# define dgettext(domain, msgid) \
- INTUSE(__dcgettext) (domain, msgid, LC_MESSAGES)
+ __dcgettext (domain, msgid, LC_MESSAGES)
#else
# include "gettext.h"
#endif
static struct uparams uparams = {
DUP_ARGS, DUP_ARGS_NOTE,
SHORT_OPT_COL, LONG_OPT_COL, DOC_OPT_COL, OPT_DOC_COL, HEADER_COL,
- USAGE_INDENT, RMARGIN,
- 0
+ USAGE_INDENT, RMARGIN
};
/* A particular uparam, and what the user name is. */
struct uparam_name
{
- const char *name; /* User name. */
- int is_bool; /* Whether it's 'boolean'. */
- size_t uparams_offs; /* Location of the (int) field in UPARAMS. */
+ const char name[14]; /* User name. */
+ bool is_bool; /* Whether it's 'boolean'. */
+ unsigned char uparams_offs; /* Location of the (int) field in UPARAMS. */
};
/* The name-field mappings we know about. */
static const struct uparam_name uparam_names[] =
{
- { "dup-args", 1, offsetof (struct uparams, dup_args) },
- { "dup-args-note", 1, offsetof (struct uparams, dup_args_note) },
- { "short-opt-col", 0, offsetof (struct uparams, short_opt_col) },
- { "long-opt-col", 0, offsetof (struct uparams, long_opt_col) },
- { "doc-opt-col", 0, offsetof (struct uparams, doc_opt_col) },
- { "opt-doc-col", 0, offsetof (struct uparams, opt_doc_col) },
- { "header-col", 0, offsetof (struct uparams, header_col) },
- { "usage-indent", 0, offsetof (struct uparams, usage_indent) },
- { "rmargin", 0, offsetof (struct uparams, rmargin) },
- { 0 }
+ { "dup-args", true, offsetof (struct uparams, dup_args) },
+ { "dup-args-note", true, offsetof (struct uparams, dup_args_note) },
+ { "short-opt-col", false, offsetof (struct uparams, short_opt_col) },
+ { "long-opt-col", false, offsetof (struct uparams, long_opt_col) },
+ { "doc-opt-col", false, offsetof (struct uparams, doc_opt_col) },
+ { "opt-doc-col", false, offsetof (struct uparams, opt_doc_col) },
+ { "header-col", false, offsetof (struct uparams, header_col) },
+ { "usage-indent", false, offsetof (struct uparams, usage_indent) },
+ { "rmargin", false, offsetof (struct uparams, rmargin) }
};
+#define nuparam_names (sizeof (uparam_names) / sizeof (uparam_names[0]))
static void
validate_uparams (const struct argp_state *state, struct uparams *upptr)
{
const struct uparam_name *up;
- for (up = uparam_names; up->name; up++)
+ for (up = uparam_names; up < uparam_names + nuparam_names; up++)
{
if (up->is_bool
|| up->uparams_offs == offsetof (struct uparams, rmargin))
uparams.valid = 1;
}
-/* Read user options from the environment, and fill in UPARAMS appropriately. */
+/* Read user options from the environment, and fill in UPARAMS appropriately. */
static void
fill_in_uparams (const struct argp_state *state)
{
SKIPWS (arg);
}
- for (un = uparam_names; un->name; un++)
+ for (un = uparam_names;
+ un < uparam_names + nuparam_names;
+ un++)
if (strlen (un->name) == var_len
&& strncmp (var, un->name, var_len) == 0)
{
if (unspec && !un->is_bool)
__argp_failure (state, 0, 0,
- dgettext (state->root_argp->argp_domain,
+ dgettext (state == NULL ? NULL
+ : state->root_argp->argp_domain,
"\
%.*s: ARGP_HELP_FMT parameter requires a value"),
(int) var_len, var);
- else if (val < 0)
- __argp_failure (state, 0, 0,
- dgettext (state->root_argp->argp_domain,
- "\
-%.*s: ARGP_HELP_FMT parameter must be positive"),
- (int) var_len, var);
else
*(int *)((char *)&new_params + un->uparams_offs) = val;
break;
}
- if (! un->name)
+ if (un == uparam_names + nuparam_names)
__argp_failure (state, 0, 0,
- dgettext (state->root_argp->argp_domain, "\
+ dgettext (state == NULL ? NULL
+ : state->root_argp->argp_domain, "\
%.*s: Unknown ARGP_HELP_FMT parameter"),
(int) var_len, var);
else if (*var)
{
__argp_failure (state, 0, 0,
- dgettext (state->root_argp->argp_domain,
+ dgettext (state == NULL ? NULL
+ : state->root_argp->argp_domain,
"Garbage in ARGP_HELP_FMT: %s"), var);
break;
}
hol_cluster_cmp (const struct hol_cluster *cl1, const struct hol_cluster *cl2)
{
/* If one cluster is deeper than the other, use its ancestor at the same
- level, so that finding the common ancestor is straightforward.
-
- clN->depth > 0 means that clN->parent != NULL (see hol_add_cluster) */
+ level, so that finding the common ancestor is straightforward. */
while (cl1->depth > cl2->depth)
cl1 = cl1->parent;
while (cl2->depth > cl1->depth)
canon_doc_option (const char **name)
{
int non_opt;
-
- if (!*name)
- non_opt = 1;
- else
- {
- /* Skip initial whitespace. */
- while (isspace ((unsigned char) **name))
- (*name)++;
- /* Decide whether this looks like an option (leading '-') or not. */
- non_opt = (**name != '-');
- /* Skip until part of name used for sorting. */
- while (**name && !isalnum ((unsigned char) **name))
- (*name)++;
- }
+ /* Skip initial whitespace. */
+ while (isspace (**name))
+ (*name)++;
+ /* Decide whether this looks like an option (leading '-') or not. */
+ non_opt = (**name != '-');
+ /* Skip until part of name used for sorting. */
+ while (**name && !isalnum (**name))
+ (*name)++;
return non_opt;
}
/* The group numbers by which the entries should be ordered; if either is
in a cluster, then this is just the group within the cluster. */
int group1 = entry1->group, group2 = entry2->group;
- int rc;
if (entry1->cluster != entry2->cluster)
{
/* The entries are not within the same cluster, so we can't compare them
directly, we have to use the appropriate clustering level too. */
if (! entry1->cluster)
- /* ENTRY1 is at the "base level", not in a cluster, so we have to
+ /* ENTRY1 is at the 'base level', not in a cluster, so we have to
compare it's group number with that of the base cluster in which
ENTRY2 resides. Note that if they're in the same group, the
- clustered option always comes laster. */
+ clustered option always comes last. */
return group_cmp (group1, hol_cluster_base (entry2->cluster)->group, -1);
else if (! entry2->cluster)
/* Likewise, but ENTRY2's not in a cluster. */
return group_cmp (hol_cluster_base (entry1->cluster)->group, group2, 1);
else
/* Both entries are in clusters, we can just compare the clusters. */
- return (rc = hol_cluster_cmp (entry1->cluster, entry2->cluster)) ?
- rc : HOL_ENTRY_PTRCMP (entry1, entry2);
+ return hol_cluster_cmp (entry1->cluster, entry2->cluster);
}
else if (group1 == group2)
/* The entries are both in the same cluster and group, so compare them
const char *long2 = hol_entry_first_long (entry2);
if (doc1)
- doc1 = canon_doc_option (&long1);
+ doc1 = long1 != NULL && canon_doc_option (&long1);
if (doc2)
- doc2 = canon_doc_option (&long2);
+ doc2 = long2 != NULL && canon_doc_option (&long2);
if (doc1 != doc2)
- /* "documentation" options always follow normal options (or
+ /* 'documentation' options always follow normal options (or
documentation options that *look* like normal options). */
return doc1 - doc2;
else if (!short1 && !short2 && long1 && long2)
/* Only long options. */
- return (rc = __strcasecmp (long1, long2)) ?
- rc : HOL_ENTRY_PTRCMP (entry1, entry2);
+ return __strcasecmp (long1, long2);
else
/* Compare short/short, long/short, short/long, using the first
character of long options. Entries without *any* valid
first, but as they're not displayed, it doesn't matter where
they are. */
{
- unsigned char first1 = short1 ? short1 : long1 ? *long1 : 0;
- unsigned char first2 = short2 ? short2 : long2 ? *long2 : 0;
- /* Use tolower, not _tolower, since only the former is
- guaranteed to work on something already lower case. */
+ char first1 = short1 ? short1 : long1 ? *long1 : 0;
+ char first2 = short2 ? short2 : long2 ? *long2 : 0;
+#ifdef _tolower
+ int lower_cmp = _tolower (first1) - _tolower (first2);
+#else
int lower_cmp = tolower (first1) - tolower (first2);
+#endif
/* Compare ignoring case, except when the options are both the
same letter, in which case lower-case always comes first. */
- return lower_cmp ? lower_cmp :
- (rc = first2 - first1) ?
- rc : HOL_ENTRY_PTRCMP (entry1, entry2);
+ return lower_cmp ? lower_cmp : first2 - first1;
}
}
else
/* Within the same cluster, but not the same group, so just compare
groups. */
- return group_cmp (group1, group2, HOL_ENTRY_PTRCMP (entry1, entry2));
+ return group_cmp (group1, group2, 0);
}
/* Version of hol_entry_cmp with correct signature for qsort. */
/* Fix up the short options pointers from HOL. */
for (e = entries, left = hol->num_entries; left > 0; e++, left--)
- e->short_options =
- short_options + (e->short_options - hol->short_options);
+ e->short_options += (short_options - hol->short_options);
/* Now add the short options from MORE, fixing up its entries
too. */
filter_doc (const char *doc, int key, const struct argp *argp,
const struct argp_state *state)
{
- if (argp->help_filter)
+ if (argp && argp->help_filter)
/* We must apply a user filter to this output. */
{
void *input = __argp_input (argp, state);
int old_wm = __argp_fmtstream_wmargin (stream);
/* PEST is a state block holding some of our variables that we'd like to
share with helper functions. */
- struct pentry_state pest;
-
- pest.entry = entry;
- pest.stream = stream;
- pest.hhstate = hhstate;
- pest.first = 1;
- pest.state = state;
+ struct pentry_state pest = { entry, stream, hhstate, 1, state };
if (! odoc (real))
for (opt = real, num = entry->num; num > 0; opt++, num--)
__argp_fmtstream_putc (stream, '-');
__argp_fmtstream_putc (stream, *so);
if (!have_long_opt || uparams.dup_args)
- arg (real, " %s", "[%s]", state->root_argp->argp_domain, stream);
+ arg (real, " %s", "[%s]",
+ state == NULL ? NULL : state->root_argp->argp_domain,
+ stream);
else if (real->arg)
hhstate->suppressed_dup_arg = 1;
}
{
__argp_fmtstream_set_wmargin (stream, uparams.doc_opt_col);
for (opt = real, num = entry->num; num > 0; opt++, num--)
- if (opt->name && *opt->name && ovisible (opt))
+ if (opt->name && ovisible (opt))
{
comma (uparams.doc_opt_col, &pest);
/* Calling dgettext here isn't quite right, since sorting will
have been done on the original; but documentation options
should be pretty rare anyway... */
__argp_fmtstream_puts (stream,
- onotrans (opt) ?
- opt->name :
- dgettext (state->root_argp->argp_domain,
+ dgettext (state == NULL ? NULL
+ : state->root_argp->argp_domain,
opt->name));
}
}
else
/* A real long option. */
{
- int first_long_opt = 1;
-
__argp_fmtstream_set_wmargin (stream, uparams.long_opt_col);
for (opt = real, num = entry->num; num > 0; opt++, num--)
if (opt->name && ovisible (opt))
{
comma (uparams.long_opt_col, &pest);
__argp_fmtstream_printf (stream, "--%s", opt->name);
- if (first_long_opt || uparams.dup_args)
- arg (real, "=%s", "[=%s]", state->root_argp->argp_domain,
- stream);
- else if (real->arg)
- hhstate->suppressed_dup_arg = 1;
+ arg (real, "=%s", "[=%s]",
+ state == NULL ? NULL : state->root_argp->argp_domain, stream);
}
}
}
else
{
- const char *tstr = real->doc ? dgettext (state->root_argp->argp_domain,
+ const char *tstr = real->doc ? dgettext (state == NULL ? NULL
+ : state->root_argp->argp_domain,
real->doc) : 0;
const char *fstr = filter_doc (tstr, real->key, entry->argp, state);
if (fstr && *fstr)
if (hhstate.suppressed_dup_arg && uparams.dup_args_note)
{
- const char *tstr = dgettext (state->root_argp->argp_domain, "\
+ const char *tstr = dgettext (state == NULL ? NULL
+ : state->root_argp->argp_domain, "\
Mandatory or optional arguments to long options are also mandatory or \
optional for any corresponding short options.");
const char *fstr = filter_doc (tstr, ARGP_KEY_HELP_DUP_ARGS_NOTE,
if (! arg)
arg = real->arg;
- if (! (flags & OPTION_NO_USAGE) && !odoc (opt))
+ if (! (flags & OPTION_NO_USAGE))
{
if (arg)
{
const char *cp = fdoc;
nl = __strchrnul (cp, '\n');
if (*nl != '\0')
- /* This is a "multi-level" args doc; advance to the correct position
+ /* This is a 'multi-level' args doc; advance to the correct position
as determined by our state in LEVELS, and update LEVELS. */
{
int i;
}
\f
/* Print the documentation for ARGP to STREAM; if POST is false, then
- everything preceding a '\v' character in the documentation strings (or
+ everything preceeding a '\v' character in the documentation strings (or
the whole string, for those with none) is printed, otherwise, everything
following the '\v' character (nothing for strings without). Each separate
bit of documentation is separated a blank line, and if PRE_BLANK is true,
{
const char *text;
const char *inp_text;
- size_t inp_text_len = 0;
- const char *trans_text;
void *input = 0;
int anything = 0;
+ size_t inp_text_limit = 0;
+ const char *doc = dgettext (argp->argp_domain, argp->doc);
const struct argp_child *child = argp->children;
- if (argp->doc)
+ if (doc)
{
- char *vt = strchr (argp->doc, '\v');
- if (vt)
- {
- if (post)
- {
- inp_text = vt + 1;
- if (! *inp_text)
- inp_text = 0;
- }
- else
- {
- inp_text_len = vt - argp->doc;
- inp_text = inp_text_len ? __strndup (argp->doc, inp_text_len) : 0;
- }
- }
- else
- inp_text = post ? 0 : argp->doc;
- trans_text = inp_text ? dgettext (argp->argp_domain, inp_text) : NULL;
+ char *vt = strchr (doc, '\v');
+ inp_text = post ? (vt ? vt + 1 : 0) : doc;
+ inp_text_limit = (!post && vt) ? (vt - doc) : 0;
}
else
- trans_text = inp_text = 0;
+ inp_text = 0;
if (argp->help_filter)
/* We have to filter the doc strings. */
{
+ if (inp_text_limit)
+ /* Copy INP_TEXT so that it's nul-terminated. */
+ inp_text = __strndup (inp_text, inp_text_limit);
input = __argp_input (argp, state);
text =
(*argp->help_filter) (post
? ARGP_KEY_HELP_POST_DOC
: ARGP_KEY_HELP_PRE_DOC,
- trans_text, input);
+ inp_text, input);
}
else
- text = (const char *) trans_text;
+ text = (const char *) inp_text;
if (text)
{
if (pre_blank)
__argp_fmtstream_putc (stream, '\n');
- __argp_fmtstream_puts (stream, text);
+ if (text == inp_text && inp_text_limit)
+ __argp_fmtstream_write (stream, inp_text, inp_text_limit);
+ else
+ __argp_fmtstream_puts (stream, text);
if (__argp_fmtstream_point (stream) > __argp_fmtstream_lmargin (stream))
__argp_fmtstream_putc (stream, '\n');
anything = 1;
}
- if (text && text != trans_text)
+ if (text && text != inp_text)
free ((char *) text); /* Free TEXT returned from the help filter. */
-
- if (inp_text && inp_text_len)
+ if (inp_text && inp_text_limit && argp->help_filter)
free ((char *) inp_text); /* We copied INP_TEXT, so free it now. */
if (post && argp->help_filter)
- /* Now see if we have to output an ARGP_KEY_HELP_EXTRA text. */
+ /* Now see if we have to output a ARGP_KEY_HELP_EXTRA text. */
{
text = (*argp->help_filter) (ARGP_KEY_HELP_EXTRA, 0, input);
if (text)
}
\f
/* Output a usage message for ARGP to STREAM. If called from
- argp_state_help, STATE is the relevant parsing state. FLAGS are from the
- set ARGP_HELP_*. NAME is what to use wherever a "program name" is
+ argp_state_help, STATE is the relevent parsing state. FLAGS are from the
+ set ARGP_HELP_*. NAME is what to use wherever a 'program name' is
needed. */
static void
_help (const struct argp *argp, const struct argp_state *state, FILE *stream,
}
\f
/* Output a usage message for ARGP to STREAM. FLAGS are from the set
- ARGP_HELP_*. NAME is what to use wherever a "program name" is needed. */
+ ARGP_HELP_*. NAME is what to use wherever a 'program name' is needed. */
void __argp_help (const struct argp *argp, FILE *stream,
unsigned flags, char *name)
{
- struct argp_state state;
- memset (&state, 0, sizeof state);
- state.root_argp = argp;
- _help (argp, &state, stream, flags, name);
+ _help (argp, 0, stream, flags, name);
}
#ifdef weak_alias
weak_alias (__argp_help, argp_help)
__argp_short_program_name (void)
{
# if HAVE_DECL_PROGRAM_INVOCATION_NAME
- return __argp_base_name (program_invocation_name);
+ char *name = strrchr (program_invocation_name, '/');
+ return name ? name + 1 : program_invocation_name;
# else
/* FIXME: What now? Miles suggests that it is better to use NULL,
but currently the value is passed on directly to fputs_unlocked,
va_start (ap, fmt);
-#ifdef USE_IN_LIBIO
- if (_IO_fwide (stream, 0) > 0)
- {
- char *buf;
+#ifdef _LIBC
+ char *buf;
- if (__asprintf (&buf, fmt, ap) < 0)
- buf = NULL;
+ if (_IO_vasprintf (&buf, fmt, ap) < 0)
+ buf = NULL;
- __fwprintf (stream, L"%s: %s\n",
- state ? state->name : __argp_short_program_name (),
- buf);
+ __fxprintf (stream, "%s: %s\n",
+ state ? state->name : __argp_short_program_name (), buf);
- free (buf);
- }
- else
-#endif
- {
- fputs_unlocked (state
- ? state->name : __argp_short_program_name (),
- stream);
- putc_unlocked (':', stream);
- putc_unlocked (' ', stream);
+ free (buf);
+#else
+ fputs_unlocked (state ? state->name : __argp_short_program_name (),
+ stream);
+ putc_unlocked (':', stream);
+ putc_unlocked (' ', stream);
- vfprintf (stream, fmt, ap);
+ vfprintf (stream, fmt, ap);
- putc_unlocked ('\n', stream);
- }
+ putc_unlocked ('\n', stream);
+#endif
__argp_state_help (state, stream, ARGP_HELP_STD_ERR);
__flockfile (stream);
#endif
-#ifdef USE_IN_LIBIO
- if (_IO_fwide (stream, 0) > 0)
- __fwprintf (stream, L"%s",
- state ? state->name : __argp_short_program_name ());
- else
+#ifdef _LIBC
+ __fxprintf (stream, "%s",
+ state ? state->name : __argp_short_program_name ());
+#else
+ fputs_unlocked (state ? state->name : __argp_short_program_name (),
+ stream);
#endif
- fputs_unlocked (state
- ? state->name : __argp_short_program_name (),
- stream);
if (fmt)
{
va_list ap;
va_start (ap, fmt);
-#ifdef USE_IN_LIBIO
- if (_IO_fwide (stream, 0) > 0)
- {
- char *buf;
+#ifdef _LIBC
+ char *buf;
- if (__asprintf (&buf, fmt, ap) < 0)
- buf = NULL;
+ if (_IO_vasprintf (&buf, fmt, ap) < 0)
+ buf = NULL;
- __fwprintf (stream, L": %s", buf);
+ __fxprintf (stream, ": %s", buf);
- free (buf);
- }
- else
-#endif
- {
- putc_unlocked (':', stream);
- putc_unlocked (' ', stream);
+ free (buf);
+#else
+ putc_unlocked (':', stream);
+ putc_unlocked (' ', stream);
- vfprintf (stream, fmt, ap);
- }
+ vfprintf (stream, fmt, ap);
+#endif
va_end (ap);
}
{
char buf[200];
-#ifdef USE_IN_LIBIO
- if (_IO_fwide (stream, 0) > 0)
- __fwprintf (stream, L": %s",
- __strerror_r (errnum, buf, sizeof (buf)));
- else
-#endif
- {
- char const *s = NULL;
- putc_unlocked (':', stream);
- putc_unlocked (' ', stream);
-#if _LIBC || (HAVE_DECL_STRERROR_R && STRERROR_R_CHAR_P && !defined strerror_r)
- s = __strerror_r (errnum, buf, sizeof buf);
-#elif HAVE_DECL_STRERROR_R
- if (__strerror_r (errnum, buf, sizeof buf) == 0)
- s = buf;
-#endif
-#if !_LIBC
- if (! s && ! (s = strerror (errnum)))
- s = dgettext (state->root_argp->argp_domain,
- "Unknown system error");
+#ifdef _LIBC
+ __fxprintf (stream, ": %s",
+ __strerror_r (errnum, buf, sizeof (buf)));
+#else
+ char const *s = NULL;
+ putc_unlocked (':', stream);
+ putc_unlocked (' ', stream);
+# if HAVE_DECL_STRERROR_R
+# if STRERROR_R_CHAR_P
+ s = __strerror_r (errnum, buf, sizeof buf);
+# else
+ if (__strerror_r (errnum, buf, sizeof buf) == 0)
+ s = buf;
+# endif
+# endif
+ if (! s && ! (s = strerror (errnum)))
+ s = dgettext (state->root_argp->argp_domain,
+ "Unknown system error");
+ fputs_unlocked (s, stream);
#endif
- fputs (s, stream);
- }
}
-#ifdef USE_IN_LIBIO
+#if _LIBC
if (_IO_fwide (stream, 0) > 0)
putwc_unlocked (L'\n', stream);
else