#ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
#endif
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <alloca.h>
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdarg.h>
#include <ctype.h>
#include <limits.h>
#ifdef USE_IN_LIBIO
# include <wchar.h>
#endif
#ifdef _LIBC
# include <libintl.h>
# undef dgettext
# define dgettext(domain, msgid) \
INTUSE(__dcgettext) (domain, msgid, LC_MESSAGES)
#else
# include "gettext.h"
#endif
#include "argp.h"
#include "argp-fmtstream.h"
#include "argp-namefrob.h"
#ifndef SIZE_MAX
# define SIZE_MAX ((size_t) -1)
#endif
#define DUP_ARGS 0
#define DUP_ARGS_NOTE 1
#define SHORT_OPT_COL 2
#define LONG_OPT_COL 6
#define DOC_OPT_COL 2
#define OPT_DOC_COL 29
#define HEADER_COL 1
#define USAGE_INDENT 12
#define RMARGIN 79
struct uparams
{
int dup_args;
int dup_args_note;
int short_opt_col;
int long_opt_col;
int doc_opt_col;
int opt_doc_col;
int header_col;
int usage_indent;
int rmargin;
int valid;
};
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
};
struct uparam_name
{
const char *name;
int is_bool;
size_t uparams_offs;
};
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 }
};
static void
validate_uparams (const struct argp_state *state, struct uparams *upptr)
{
const struct uparam_name *up;
for (up = uparam_names; up->name; up++)
{
if (up->is_bool
|| up->uparams_offs == offsetof (struct uparams, rmargin))
continue;
if (*(int *)((char *)upptr + up->uparams_offs) >= upptr->rmargin)
{
__argp_failure (state, 0, 0,
dgettext (state->root_argp->argp_domain,
"\
ARGP_HELP_FMT: %s value is less than or equal to %s"),
"rmargin", up->name);
return;
}
}
uparams = *upptr;
uparams.valid = 1;
}
static void
fill_in_uparams (const struct argp_state *state)
{
const char *var = getenv ("ARGP_HELP_FMT");
struct uparams new_params = uparams;
#define SKIPWS(p) do { while (isspace ((unsigned char) *p)) p++; } while (0);
if (var)
{
while (*var)
{
SKIPWS (var);
if (isalpha ((unsigned char) *var))
{
size_t var_len;
const struct uparam_name *un;
int unspec = 0, val = 0;
const char *arg = var;
while (isalnum ((unsigned char) *arg) || *arg == '-' || *arg == '_')
arg++;
var_len = arg - var;
SKIPWS (arg);
if (*arg == '\0' || *arg == ',')
unspec = 1;
else if (*arg == '=')
{
arg++;
SKIPWS (arg);
}
if (unspec)
{
if (var[0] == 'n' && var[1] == 'o' && var[2] == '-')
{
val = 0;
var += 3;
var_len -= 3;
}
else
val = 1;
}
else if (isdigit ((unsigned char) *arg))
{
val = atoi (arg);
while (isdigit ((unsigned char) *arg))
arg++;
SKIPWS (arg);
}
for (un = uparam_names; un->name; 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,
"\
%.*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)
__argp_failure (state, 0, 0,
dgettext (state->root_argp->argp_domain, "\
%.*s: Unknown ARGP_HELP_FMT parameter"),
(int) var_len, var);
var = arg;
if (*var == ',')
var++;
}
else if (*var)
{
__argp_failure (state, 0, 0,
dgettext (state->root_argp->argp_domain,
"Garbage in ARGP_HELP_FMT: %s"), var);
break;
}
}
validate_uparams (state, &new_params);
}
}
#define ovisible(opt) (! ((opt)->flags & OPTION_HIDDEN))
#define oalias(opt) ((opt)->flags & OPTION_ALIAS)
#define odoc(opt) ((opt)->flags & OPTION_DOC)
#define onotrans(opt) ((opt)->flags & OPTION_NO_TRANS)
#define oend(opt) __option_is_end (opt)
#define oshort(opt) __option_is_short (opt)
static int
find_char (char ch, char *beg, char *end)
{
while (beg < end)
if (*beg == ch)
return 1;
else
beg++;
return 0;
}
struct hol_cluster;
struct hol_entry
{
const struct argp_option *opt;
unsigned num;
char *short_options;
int group;
struct hol_cluster *cluster;
const struct argp *argp;
unsigned ord;
};
struct hol_cluster
{
const char *header;
int index;
int group;
struct hol_cluster *parent;
const struct argp *argp;
int depth;
struct hol_cluster *next;
};
struct hol
{
struct hol_entry *entries;
unsigned num_entries;
char *short_options;
struct hol_cluster *clusters;
};
static struct hol *
make_hol (const struct argp *argp, struct hol_cluster *cluster)
{
char *so;
const struct argp_option *o;
const struct argp_option *opts = argp->options;
struct hol_entry *entry;
unsigned num_short_options = 0;
struct hol *hol = malloc (sizeof (struct hol));
assert (hol);
hol->num_entries = 0;
hol->clusters = 0;
if (opts)
{
int cur_group = 0;
assert (! oalias (opts));
for (o = opts; ! oend (o); o++)
{
if (! oalias (o))
hol->num_entries++;
if (oshort (o))
num_short_options++;
}
hol->entries = malloc (sizeof (struct hol_entry) * hol->num_entries);
hol->short_options = malloc (num_short_options + 1);
assert (hol->entries && hol->short_options);
if (SIZE_MAX <= UINT_MAX)
assert (hol->num_entries <= SIZE_MAX / sizeof (struct hol_entry));
so = hol->short_options;
for (o = opts, entry = hol->entries; ! oend (o); entry++)
{
entry->opt = o;
entry->num = 0;
entry->short_options = so;
entry->group = cur_group =
o->group
? o->group
: ((!o->name && !o->key)
? cur_group + 1
: cur_group);
entry->cluster = cluster;
entry->argp = argp;
do
{
entry->num++;
if (oshort (o) && ! find_char (o->key, hol->short_options, so))
*so++ = o->key;
o++;
}
while (! oend (o) && oalias (o));
}
*so = '\0';
}
return hol;
}
static struct hol_cluster *
hol_add_cluster (struct hol *hol, int group, const char *header, int index,
struct hol_cluster *parent, const struct argp *argp)
{
struct hol_cluster *cl = malloc (sizeof (struct hol_cluster));
if (cl)
{
cl->group = group;
cl->header = header;
cl->index = index;
cl->parent = parent;
cl->argp = argp;
cl->depth = parent ? parent->depth + 1 : 0;
cl->next = hol->clusters;
hol->clusters = cl;
}
return cl;
}
static void
hol_free (struct hol *hol)
{
struct hol_cluster *cl = hol->clusters;
while (cl)
{
struct hol_cluster *next = cl->next;
free (cl);
cl = next;
}
if (hol->num_entries > 0)
{
free (hol->entries);
free (hol->short_options);
}
free (hol);
}
static int
hol_entry_short_iterate (const struct hol_entry *entry,
int (*func)(const struct argp_option *opt,
const struct argp_option *real,
const char *domain, void *cookie),
const char *domain, void *cookie)
{
unsigned nopts;
int val = 0;
const struct argp_option *opt, *real = entry->opt;
char *so = entry->short_options;
for (opt = real, nopts = entry->num; nopts > 0 && !val; opt++, nopts--)
if (oshort (opt) && *so == opt->key)
{
if (!oalias (opt))
real = opt;
if (ovisible (opt))
val = (*func)(opt, real, domain, cookie);
so++;
}
return val;
}
static inline int
__attribute__ ((always_inline))
hol_entry_long_iterate (const struct hol_entry *entry,
int (*func)(const struct argp_option *opt,
const struct argp_option *real,
const char *domain, void *cookie),
const char *domain, void *cookie)
{
unsigned nopts;
int val = 0;
const struct argp_option *opt, *real = entry->opt;
for (opt = real, nopts = entry->num; nopts > 0 && !val; opt++, nopts--)
if (opt->name)
{
if (!oalias (opt))
real = opt;
if (ovisible (opt))
val = (*func)(opt, real, domain, cookie);
}
return val;
}
static inline int
until_short (const struct argp_option *opt, const struct argp_option *real,
const char *domain, void *cookie)
{
return oshort (opt) ? opt->key : 0;
}
static char
hol_entry_first_short (const struct hol_entry *entry)
{
return hol_entry_short_iterate (entry, until_short,
entry->argp->argp_domain, 0);
}
static const char *
hol_entry_first_long (const struct hol_entry *entry)
{
const struct argp_option *opt;
unsigned num;
for (opt = entry->opt, num = entry->num; num > 0; opt++, num--)
if (opt->name && ovisible (opt))
return opt->name;
return 0;
}
static struct hol_entry *
hol_find_entry (struct hol *hol, const char *name)
{
struct hol_entry *entry = hol->entries;
unsigned num_entries = hol->num_entries;
while (num_entries-- > 0)
{
const struct argp_option *opt = entry->opt;
unsigned num_opts = entry->num;
while (num_opts-- > 0)
if (opt->name && ovisible (opt) && strcmp (opt->name, name) == 0)
return entry;
else
opt++;
entry++;
}
return 0;
}
static void
hol_set_group (struct hol *hol, const char *name, int group)
{
struct hol_entry *entry = hol_find_entry (hol, name);
if (entry)
entry->group = group;
}
static int
group_cmp (int group1, int group2, int eq)
{
if (group1 == group2)
return eq;
else if ((group1 < 0 && group2 < 0) || (group1 >= 0 && group2 >= 0))
return group1 - group2;
else
return group2 - group1;
}
static int
hol_cluster_cmp (const struct hol_cluster *cl1, const struct hol_cluster *cl2)
{
while (cl1->depth > cl2->depth)
cl1 = cl1->parent;
while (cl2->depth > cl1->depth)
cl2 = cl2->parent;
while (cl1->parent != cl2->parent)
cl1 = cl1->parent, cl2 = cl2->parent;
return group_cmp (cl1->group, cl2->group, cl2->index - cl1->index);
}
static struct hol_cluster *
hol_cluster_base (struct hol_cluster *cl)
{
while (cl->parent)
cl = cl->parent;
return cl;
}
static int
hol_cluster_is_child (const struct hol_cluster *cl1,
const struct hol_cluster *cl2)
{
while (cl1 && cl1 != cl2)
cl1 = cl1->parent;
return cl1 == cl2;
}
static int
canon_doc_option (const char **name)
{
int non_opt;
if (!*name)
non_opt = 1;
else
{
while (isspace ((unsigned char) **name))
(*name)++;
non_opt = (**name != '-');
while (**name && !isalnum ((unsigned char) **name))
(*name)++;
}
return non_opt;
}
#define HOL_ENTRY_PTRCMP(a,b) ((a)->ord < (b)->ord ? -1 : 1)
static int
hol_entry_cmp (const struct hol_entry *entry1,
const struct hol_entry *entry2)
{
int group1 = entry1->group, group2 = entry2->group;
int rc;
if (entry1->cluster != entry2->cluster)
{
if (! entry1->cluster)
return group_cmp (group1, hol_cluster_base (entry2->cluster)->group, -1);
else if (! entry2->cluster)
return group_cmp (hol_cluster_base (entry1->cluster)->group, group2, 1);
else
return (rc = hol_cluster_cmp (entry1->cluster, entry2->cluster)) ?
rc : HOL_ENTRY_PTRCMP(entry1, entry2);
}
else if (group1 == group2)
{
int short1 = hol_entry_first_short (entry1);
int short2 = hol_entry_first_short (entry2);
int doc1 = odoc (entry1->opt);
int doc2 = odoc (entry2->opt);
const char *long1 = hol_entry_first_long (entry1);
const char *long2 = hol_entry_first_long (entry2);
if (doc1)
doc1 = canon_doc_option (&long1);
if (doc2)
doc2 = canon_doc_option (&long2);
if (doc1 != doc2)
return doc1 - doc2;
else if (!short1 && !short2 && long1 && long2)
return (rc = __strcasecmp (long1, long2)) ?
rc : HOL_ENTRY_PTRCMP(entry1, entry2);
else
{
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
return lower_cmp ? lower_cmp :
(rc = first2 - first1) ?
rc : HOL_ENTRY_PTRCMP(entry1, entry2);
}
}
else
return group_cmp (group1, group2, HOL_ENTRY_PTRCMP(entry1, entry2));
}
static int
hol_entry_qcmp (const void *entry1_v, const void *entry2_v)
{
return hol_entry_cmp (entry1_v, entry2_v);
}
static void
hol_sort (struct hol *hol)
{
if (hol->num_entries > 0)
{
unsigned i;
struct hol_entry *e;
for (i = 0, e = hol->entries; i < hol->num_entries; i++, e++)
e->ord = i;
qsort (hol->entries, hol->num_entries, sizeof (struct hol_entry),
hol_entry_qcmp);
}
}
static void
hol_append (struct hol *hol, struct hol *more)
{
struct hol_cluster **cl_end = &hol->clusters;
while (*cl_end)
cl_end = &(*cl_end)->next;
*cl_end = more->clusters;
more->clusters = 0;
if (more->num_entries > 0)
{
if (hol->num_entries == 0)
{
hol->num_entries = more->num_entries;
hol->entries = more->entries;
hol->short_options = more->short_options;
more->num_entries = 0;
}
else
{
unsigned left;
char *so, *more_so;
struct hol_entry *e;
unsigned num_entries = hol->num_entries + more->num_entries;
struct hol_entry *entries =
malloc (num_entries * sizeof (struct hol_entry));
unsigned hol_so_len = strlen (hol->short_options);
char *short_options =
malloc (hol_so_len + strlen (more->short_options) + 1);
assert (entries && short_options);
if (SIZE_MAX <= UINT_MAX)
assert (num_entries <= SIZE_MAX / sizeof (struct hol_entry));
__mempcpy (__mempcpy (entries, hol->entries,
hol->num_entries * sizeof (struct hol_entry)),
more->entries,
more->num_entries * sizeof (struct hol_entry));
__mempcpy (short_options, hol->short_options, hol_so_len);
for (e = entries, left = hol->num_entries; left > 0; e++, left--)
e->short_options += (short_options - hol->short_options);
so = short_options + hol_so_len;
more_so = more->short_options;
for (left = more->num_entries; left > 0; e++, left--)
{
int opts_left;
const struct argp_option *opt;
e->short_options = so;
for (opts_left = e->num, opt = e->opt; opts_left; opt++, opts_left--)
{
int ch = *more_so;
if (oshort (opt) && ch == opt->key)
{
if (! find_char (ch, short_options,
short_options + hol_so_len))
*so++ = ch;
more_so++;
}
}
}
*so = '\0';
free (hol->entries);
free (hol->short_options);
hol->entries = entries;
hol->num_entries = num_entries;
hol->short_options = short_options;
}
}
hol_free (more);
}
static void
indent_to (argp_fmtstream_t stream, unsigned col)
{
int needed = col - __argp_fmtstream_point (stream);
while (needed-- > 0)
__argp_fmtstream_putc (stream, ' ');
}
static void
space (argp_fmtstream_t stream, size_t ensure)
{
if (__argp_fmtstream_point (stream) + ensure
>= __argp_fmtstream_rmargin (stream))
__argp_fmtstream_putc (stream, '\n');
else
__argp_fmtstream_putc (stream, ' ');
}
static void
arg (const struct argp_option *real, const char *req_fmt, const char *opt_fmt,
const char *domain, argp_fmtstream_t stream)
{
if (real->arg)
{
if (real->flags & OPTION_ARG_OPTIONAL)
__argp_fmtstream_printf (stream, opt_fmt,
dgettext (domain, real->arg));
else
__argp_fmtstream_printf (stream, req_fmt,
dgettext (domain, real->arg));
}
}
struct hol_help_state
{
struct hol_entry *prev_entry;
int sep_groups;
int suppressed_dup_arg;
};
struct pentry_state
{
const struct hol_entry *entry;
argp_fmtstream_t stream;
struct hol_help_state *hhstate;
int first;
const struct argp_state *state;
};
static const char *
filter_doc (const char *doc, int key, const struct argp *argp,
const struct argp_state *state)
{
if (argp->help_filter)
{
void *input = __argp_input (argp, state);
return (*argp->help_filter) (key, doc, input);
}
else
return doc;
}
static void
print_header (const char *str, const struct argp *argp,
struct pentry_state *pest)
{
const char *tstr = dgettext (argp->argp_domain, str);
const char *fstr = filter_doc (tstr, ARGP_KEY_HELP_HEADER, argp, pest->state);
if (fstr)
{
if (*fstr)
{
if (pest->hhstate->prev_entry)
__argp_fmtstream_putc (pest->stream, '\n');
indent_to (pest->stream, uparams.header_col);
__argp_fmtstream_set_lmargin (pest->stream, uparams.header_col);
__argp_fmtstream_set_wmargin (pest->stream, uparams.header_col);
__argp_fmtstream_puts (pest->stream, fstr);
__argp_fmtstream_set_lmargin (pest->stream, 0);
__argp_fmtstream_putc (pest->stream, '\n');
}
pest->hhstate->sep_groups = 1;
}
if (fstr != tstr)
free ((char *) fstr);
}
static void
comma (unsigned col, struct pentry_state *pest)
{
if (pest->first)
{
const struct hol_entry *pe = pest->hhstate->prev_entry;
const struct hol_cluster *cl = pest->entry->cluster;
if (pest->hhstate->sep_groups && pe && pest->entry->group != pe->group)
__argp_fmtstream_putc (pest->stream, '\n');
if (cl && cl->header && *cl->header
&& (!pe
|| (pe->cluster != cl
&& !hol_cluster_is_child (pe->cluster, cl))))
{
int old_wm = __argp_fmtstream_wmargin (pest->stream);
print_header (cl->header, cl->argp, pest);
__argp_fmtstream_set_wmargin (pest->stream, old_wm);
}
pest->first = 0;
}
else
__argp_fmtstream_puts (pest->stream, ", ");
indent_to (pest->stream, col);
}
static void
hol_entry_help (struct hol_entry *entry, const struct argp_state *state,
argp_fmtstream_t stream, struct hol_help_state *hhstate)
{
unsigned num;
const struct argp_option *real = entry->opt, *opt;
char *so = entry->short_options;
int have_long_opt = 0;
int old_lm = __argp_fmtstream_set_lmargin (stream, 0);
int old_wm = __argp_fmtstream_wmargin (stream);
struct pentry_state pest;
pest.entry = entry;
pest.stream = stream;
pest.hhstate = hhstate;
pest.first = 1;
pest.state = state;
if (! odoc (real))
for (opt = real, num = entry->num; num > 0; opt++, num--)
if (opt->name && ovisible (opt))
{
have_long_opt = 1;
break;
}
__argp_fmtstream_set_wmargin (stream, uparams.short_opt_col);
for (opt = real, num = entry->num; num > 0; opt++, num--)
if (oshort (opt) && opt->key == *so)
{
if (ovisible (opt))
{
comma (uparams.short_opt_col, &pest);
__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);
else if (real->arg)
hhstate->suppressed_dup_arg = 1;
}
so++;
}
if (odoc (real))
{
__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))
{
comma (uparams.doc_opt_col, &pest);
__argp_fmtstream_puts (stream,
onotrans (opt) ?
opt->name :
dgettext (state->root_argp->argp_domain,
opt->name));
}
}
else
{
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;
}
}
__argp_fmtstream_set_lmargin (stream, 0);
if (pest.first)
{
if (!oshort (real) && !real->name)
print_header (real->doc, entry->argp, &pest);
else
goto cleanup;
}
else
{
const char *tstr = real->doc ? dgettext (state->root_argp->argp_domain,
real->doc) : 0;
const char *fstr = filter_doc (tstr, real->key, entry->argp, state);
if (fstr && *fstr)
{
unsigned int col = __argp_fmtstream_point (stream);
__argp_fmtstream_set_lmargin (stream, uparams.opt_doc_col);
__argp_fmtstream_set_wmargin (stream, uparams.opt_doc_col);
if (col > (unsigned int) (uparams.opt_doc_col + 3))
__argp_fmtstream_putc (stream, '\n');
else if (col >= (unsigned int) uparams.opt_doc_col)
__argp_fmtstream_puts (stream, " ");
else
indent_to (stream, uparams.opt_doc_col);
__argp_fmtstream_puts (stream, fstr);
}
if (fstr && fstr != tstr)
free ((char *) fstr);
__argp_fmtstream_set_lmargin (stream, 0);
__argp_fmtstream_putc (stream, '\n');
}
hhstate->prev_entry = entry;
cleanup:
__argp_fmtstream_set_lmargin (stream, old_lm);
__argp_fmtstream_set_wmargin (stream, old_wm);
}
static void
hol_help (struct hol *hol, const struct argp_state *state,
argp_fmtstream_t stream)
{
unsigned num;
struct hol_entry *entry;
struct hol_help_state hhstate = { 0, 0, 0 };
for (entry = hol->entries, num = hol->num_entries; num > 0; entry++, num--)
hol_entry_help (entry, state, stream, &hhstate);
if (hhstate.suppressed_dup_arg && uparams.dup_args_note)
{
const char *tstr = dgettext (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,
state ? state->root_argp : 0, state);
if (fstr && *fstr)
{
__argp_fmtstream_putc (stream, '\n');
__argp_fmtstream_puts (stream, fstr);
__argp_fmtstream_putc (stream, '\n');
}
if (fstr && fstr != tstr)
free ((char *) fstr);
}
}
static int
add_argless_short_opt (const struct argp_option *opt,
const struct argp_option *real,
const char *domain, void *cookie)
{
char **snao_end = cookie;
if (!(opt->arg || real->arg)
&& !((opt->flags | real->flags) & OPTION_NO_USAGE))
*(*snao_end)++ = opt->key;
return 0;
}
static int
usage_argful_short_opt (const struct argp_option *opt,
const struct argp_option *real,
const char *domain, void *cookie)
{
argp_fmtstream_t stream = cookie;
const char *arg = opt->arg;
int flags = opt->flags | real->flags;
if (! arg)
arg = real->arg;
if (arg && !(flags & OPTION_NO_USAGE))
{
arg = dgettext (domain, arg);
if (flags & OPTION_ARG_OPTIONAL)
__argp_fmtstream_printf (stream, " [-%c[%s]]", opt->key, arg);
else
{
space (stream, 6 + strlen (arg));
__argp_fmtstream_printf (stream, "[-%c %s]", opt->key, arg);
}
}
return 0;
}
static int
usage_long_opt (const struct argp_option *opt,
const struct argp_option *real,
const char *domain, void *cookie)
{
argp_fmtstream_t stream = cookie;
const char *arg = opt->arg;
int flags = opt->flags | real->flags;
if (! arg)
arg = real->arg;
if (! (flags & OPTION_NO_USAGE) && !odoc (opt))
{
if (arg)
{
arg = dgettext (domain, arg);
if (flags & OPTION_ARG_OPTIONAL)
__argp_fmtstream_printf (stream, " [--%s[=%s]]", opt->name, arg);
else
__argp_fmtstream_printf (stream, " [--%s=%s]", opt->name, arg);
}
else
__argp_fmtstream_printf (stream, " [--%s]", opt->name);
}
return 0;
}
static void
hol_usage (struct hol *hol, argp_fmtstream_t stream)
{
if (hol->num_entries > 0)
{
unsigned nentries;
struct hol_entry *entry;
char *short_no_arg_opts = alloca (strlen (hol->short_options) + 1);
char *snao_end = short_no_arg_opts;
for (entry = hol->entries, nentries = hol->num_entries
; nentries > 0
; entry++, nentries--)
hol_entry_short_iterate (entry, add_argless_short_opt,
entry->argp->argp_domain, &snao_end);
if (snao_end > short_no_arg_opts)
{
*snao_end++ = 0;
__argp_fmtstream_printf (stream, " [-%s]", short_no_arg_opts);
}
for (entry = hol->entries, nentries = hol->num_entries
; nentries > 0
; entry++, nentries--)
hol_entry_short_iterate (entry, usage_argful_short_opt,
entry->argp->argp_domain, stream);
for (entry = hol->entries, nentries = hol->num_entries
; nentries > 0
; entry++, nentries--)
hol_entry_long_iterate (entry, usage_long_opt,
entry->argp->argp_domain, stream);
}
}
static struct hol *
argp_hol (const struct argp *argp, struct hol_cluster *cluster)
{
const struct argp_child *child = argp->children;
struct hol *hol = make_hol (argp, cluster);
if (child)
while (child->argp)
{
struct hol_cluster *child_cluster =
((child->group || child->header)
? hol_add_cluster (hol, child->group, child->header,
child - argp->children, cluster, argp)
: cluster);
hol_append (hol, argp_hol (child->argp, child_cluster)) ;
child++;
}
return hol;
}
static size_t
argp_args_levels (const struct argp *argp)
{
size_t levels = 0;
const struct argp_child *child = argp->children;
if (argp->args_doc && strchr (argp->args_doc, '\n'))
levels++;
if (child)
while (child->argp)
levels += argp_args_levels ((child++)->argp);
return levels;
}
static int
argp_args_usage (const struct argp *argp, const struct argp_state *state,
char **levels, int advance, argp_fmtstream_t stream)
{
char *our_level = *levels;
int multiple = 0;
const struct argp_child *child = argp->children;
const char *tdoc = dgettext (argp->argp_domain, argp->args_doc), *nl = 0;
const char *fdoc = filter_doc (tdoc, ARGP_KEY_HELP_ARGS_DOC, argp, state);
if (fdoc)
{
const char *cp = fdoc;
nl = __strchrnul (cp, '\n');
if (*nl != '\0')
{
int i;
multiple = 1;
for (i = 0; i < *our_level; i++)
cp = nl + 1, nl = __strchrnul (cp, '\n');
(*levels)++;
}
space (stream, 1 + nl - cp);
__argp_fmtstream_write (stream, cp, nl - cp);
}
if (fdoc && fdoc != tdoc)
free ((char *)fdoc);
if (child)
while (child->argp)
advance = !argp_args_usage ((child++)->argp, state, levels, advance, stream);
if (advance && multiple)
{
if (*nl)
{
(*our_level)++;
advance = 0;
}
else if (*our_level > 0)
*our_level = 0;
}
return !advance;
}
static int
argp_doc (const struct argp *argp, const struct argp_state *state,
int post, int pre_blank, int first_only,
argp_fmtstream_t stream)
{
const char *text;
const char *inp_text;
size_t inp_text_len = 0;
const char *trans_text;
void *input = 0;
int anything = 0;
const struct argp_child *child = argp->children;
if (argp->doc)
{
char *vt = strchr (argp->doc, '\v');
if (vt)
{
if (post)
inp_text = vt + 1;
else
{
inp_text_len = vt - argp->doc;
inp_text = __strndup (argp->doc, inp_text_len);
}
}
else
inp_text = post ? 0 : argp->doc;
trans_text = inp_text ? dgettext (argp->argp_domain, inp_text) : NULL;
}
else
trans_text = inp_text = 0;
if (argp->help_filter)
{
input = __argp_input (argp, state);
text =
(*argp->help_filter) (post
? ARGP_KEY_HELP_POST_DOC
: ARGP_KEY_HELP_PRE_DOC,
trans_text, input);
}
else
text = (const char *) trans_text;
if (text)
{
if (pre_blank)
__argp_fmtstream_putc (stream, '\n');
__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)
free ((char *) text);
if (inp_text && inp_text_len)
free ((char *) inp_text);
if (post && argp->help_filter)
{
text = (*argp->help_filter) (ARGP_KEY_HELP_EXTRA, 0, input);
if (text)
{
if (anything || pre_blank)
__argp_fmtstream_putc (stream, '\n');
__argp_fmtstream_puts (stream, text);
free ((char *) text);
if (__argp_fmtstream_point (stream)
> __argp_fmtstream_lmargin (stream))
__argp_fmtstream_putc (stream, '\n');
anything = 1;
}
}
if (child)
while (child->argp && !(first_only && anything))
anything |=
argp_doc ((child++)->argp, state,
post, anything || pre_blank, first_only,
stream);
return anything;
}
static void
_help (const struct argp *argp, const struct argp_state *state, FILE *stream,
unsigned flags, char *name)
{
int anything = 0;
struct hol *hol = 0;
argp_fmtstream_t fs;
if (! stream)
return;
#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
__flockfile (stream);
#endif
if (! uparams.valid)
fill_in_uparams (state);
fs = __argp_make_fmtstream (stream, 0, uparams.rmargin, 0);
if (! fs)
{
#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
__funlockfile (stream);
#endif
return;
}
if (flags & (ARGP_HELP_USAGE | ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG))
{
hol = argp_hol (argp, 0);
hol_set_group (hol, "help", -1);
hol_set_group (hol, "version", -1);
hol_sort (hol);
}
if (flags & (ARGP_HELP_USAGE | ARGP_HELP_SHORT_USAGE))
{
int first_pattern = 1, more_patterns;
size_t num_pattern_levels = argp_args_levels (argp);
char *pattern_levels = alloca (num_pattern_levels);
memset (pattern_levels, 0, num_pattern_levels);
do
{
int old_lm;
int old_wm = __argp_fmtstream_set_wmargin (fs, uparams.usage_indent);
char *levels = pattern_levels;
if (first_pattern)
__argp_fmtstream_printf (fs, "%s %s",
dgettext (argp->argp_domain, "Usage:"),
name);
else
__argp_fmtstream_printf (fs, "%s %s",
dgettext (argp->argp_domain, " or: "),
name);
old_lm = __argp_fmtstream_set_lmargin (fs, uparams.usage_indent);
if (flags & ARGP_HELP_SHORT_USAGE)
{
if (hol->num_entries > 0)
__argp_fmtstream_puts (fs, dgettext (argp->argp_domain,
" [OPTION...]"));
}
else
{
hol_usage (hol, fs);
flags |= ARGP_HELP_SHORT_USAGE;
}
more_patterns = argp_args_usage (argp, state, &levels, 1, fs);
__argp_fmtstream_set_wmargin (fs, old_wm);
__argp_fmtstream_set_lmargin (fs, old_lm);
__argp_fmtstream_putc (fs, '\n');
anything = 1;
first_pattern = 0;
}
while (more_patterns);
}
if (flags & ARGP_HELP_PRE_DOC)
anything |= argp_doc (argp, state, 0, 0, 1, fs);
if (flags & ARGP_HELP_SEE)
{
__argp_fmtstream_printf (fs, dgettext (argp->argp_domain, "\
Try `%s --help' or `%s --usage' for more information.\n"),
name, name);
anything = 1;
}
if (flags & ARGP_HELP_LONG)
{
if (hol->num_entries > 0)
{
if (anything)
__argp_fmtstream_putc (fs, '\n');
hol_help (hol, state, fs);
anything = 1;
}
}
if (flags & ARGP_HELP_POST_DOC)
anything |= argp_doc (argp, state, 1, anything, 0, fs);
if ((flags & ARGP_HELP_BUG_ADDR) && argp_program_bug_address)
{
if (anything)
__argp_fmtstream_putc (fs, '\n');
__argp_fmtstream_printf (fs, dgettext (argp->argp_domain,
"Report bugs to %s.\n"),
argp_program_bug_address);
anything = 1;
}
#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
__funlockfile (stream);
#endif
if (hol)
hol_free (hol);
__argp_fmtstream_free (fs);
}
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);
}
#ifdef weak_alias
weak_alias (__argp_help, argp_help)
#endif
#if ! (defined _LIBC || HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME)
char *
__argp_short_program_name (void)
{
# if HAVE_DECL_PROGRAM_INVOCATION_NAME
return __argp_base_name (program_invocation_name);
# else
# if __GNUC__
# warning No reasonable value to return
# endif
return "";
# endif
}
#endif
void
__argp_state_help (const struct argp_state *state, FILE *stream, unsigned flags)
{
if ((!state || ! (state->flags & ARGP_NO_ERRS)) && stream)
{
if (state && (state->flags & ARGP_LONG_ONLY))
flags |= ARGP_HELP_LONG_ONLY;
_help (state ? state->root_argp : 0, state, stream, flags,
state ? state->name : __argp_short_program_name ());
if (!state || ! (state->flags & ARGP_NO_EXIT))
{
if (flags & ARGP_HELP_EXIT_ERR)
exit (argp_err_exit_status);
if (flags & ARGP_HELP_EXIT_OK)
exit (0);
}
}
}
#ifdef weak_alias
weak_alias (__argp_state_help, argp_state_help)
#endif
void
__argp_error (const struct argp_state *state, const char *fmt, ...)
{
if (!state || !(state->flags & ARGP_NO_ERRS))
{
FILE *stream = state ? state->err_stream : stderr;
if (stream)
{
va_list ap;
#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
__flockfile (stream);
#endif
va_start (ap, fmt);
#ifdef USE_IN_LIBIO
if (_IO_fwide (stream, 0) > 0)
{
char *buf;
if (__asprintf (&buf, fmt, ap) < 0)
buf = NULL;
__fwprintf (stream, L"%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);
vfprintf (stream, fmt, ap);
putc_unlocked ('\n', stream);
}
__argp_state_help (state, stream, ARGP_HELP_STD_ERR);
va_end (ap);
#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
__funlockfile (stream);
#endif
}
}
}
#ifdef weak_alias
weak_alias (__argp_error, argp_error)
#endif
void
__argp_failure (const struct argp_state *state, int status, int errnum,
const char *fmt, ...)
{
if (!state || !(state->flags & ARGP_NO_ERRS))
{
FILE *stream = state ? state->err_stream : stderr;
if (stream)
{
#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
__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
#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;
if (__asprintf (&buf, fmt, ap) < 0)
buf = NULL;
__fwprintf (stream, L": %s", buf);
free (buf);
}
else
#endif
{
putc_unlocked (':', stream);
putc_unlocked (' ', stream);
vfprintf (stream, fmt, ap);
}
va_end (ap);
}
if (errnum)
{
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)
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");
#endif
fputs (s, stream);
}
}
#ifdef USE_IN_LIBIO
if (_IO_fwide (stream, 0) > 0)
putwc_unlocked (L'\n', stream);
else
#endif
putc_unlocked ('\n', stream);
#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
__funlockfile (stream);
#endif
if (status && (!state || !(state->flags & ARGP_NO_EXIT)))
exit (status);
}
}
}
#ifdef weak_alias
weak_alias (__argp_failure, argp_failure)
#endif