/* Make a HOL containing all levels of options in ARGP. CLUSTER is the
cluster in which ARGP's entries should be clustered, or 0. */
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)
/* Put CHILD->argp within its own cluster. */
? hol_add_cluster (hol, child->group, child->header,
child - argp->children, cluster, argp)
/* Just merge it into the parent's cluster. */
: cluster);
hol_append (hol, argp_hol (child->argp, child_cluster)) ;
child++;
}
return hol;
}
/* Calculate how many different levels with alternative args strings exist in
ARGP. */
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;
}
/* Print all the non-option args do ented in ARGP to STREAM. Any output is
preceded by a space. LEVELS is a pointer to a byte vector the length
returned by argp_args_levels; it should be initialized to zero, and
updated by this routine for the next call if ADVANCE is true. True is
returned as long as there are more patterns to output. */
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')
/* This is a `multi-level' args doc; advance to the correct position
as determined by our state in LEVELS, and update LEVELS. */
{
int i;
multiple = 1;
for (i = 0; i < *our_level; i++)
cp = nl + 1, nl = __strchrnul (cp, '\n');
(*levels)++;
}
/* Manually do line wrapping so that it (probably) won't get wrapped at
any embedded spaces. */
space (stream, 1 + nl - cp);
__argp_fmtstream_write (stream, cp, nl - cp);
}
if (fdoc && fdoc != tdoc)
free ((char *)fdoc); /* Free user's modified doc string. */
if (child)
while (child->argp)
advance = !argp_args_usage ((child++)->argp, state, levels, advance, stream);
if (advance && multiple)
{
/* Need to increment our level. */
if (*nl)
/* There's more we can do here. */
{
(*our_level)++;
advance = 0; /* Our parent shouldn't advance also. */
}
else if (*our_level > 0)
/* We had multiple levels, but used them up; reset to zero. */
*our_level = 0;
}
return !advance;
}
/* Print the do entation for ARGP to STREAM; if POST is false, then
everything preceeding a `\v' character in the do entation 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 do entation is separated a blank line, and if PRE_BLANK is true,
then the first is as well. If FIRST_ONLY is true, only the first
occurrence is output. Returns true if anything was output. */
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;
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 (doc)
{
char *vt = strchr (doc, '\v');
inp_text = post ? (vt ? vt + 1 : 0) : doc;
inp_text_limit = (!post && vt) ? (vt - doc) : 0;
}
else
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,
inp_text, input);
}
else
text = (const char *) inp_text;
if (text)
{
if (pre_blank)
__argp_fmtstream_putc (stream, '\n');
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 != inp_text)
free ((char *) text); /* Free TEXT returned from the help filter. */
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 a ARGP_KEY_HELP_EXTRA text. */
{
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;
}
/* Output a usage message for ARGP to STREAM. If called from
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,
unsigned flags, char *name)
{
int anything = 0; /* Whether we've output anything. */
struct hol *hol = 0;
argp_fmtstream_t fs;
if (! stream)
return;
#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
__flockfile (stream);
#endif
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);
/* If present, these options always come last. */
hol_set_group (hol, "help", -1);
hol_set_group (hol, "version", -1);
hol_sort (hol);
}
if (flags & (ARGP_HELP_USAGE | ARGP_HELP_SHORT_USAGE))
/* Print a short `Usage:' message. */
{
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);
/* We set the lmargin as well as the wmargin, because hol_usage
manually wraps options with newline to avoid annoying breaks. */
old_lm = __argp_fmtstream_set_lmargin (fs, uparams.usage_indent);
if (flags & ARGP_HELP_SHORT_USAGE)
/* Just show where the options go. */
{
if (hol->num_entries > 0)
__argp_fmtstream_puts (fs, dgettext (argp->argp_domain,
" [OPTION...]"));
}
else
/* Actually print the options. */
{
hol_usage (hol, fs);
flags |= ARGP_HELP_SHORT_USAGE; /* But only do so once. */
}
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)
/* Print a long, detailed help message. */
{
/* Print info about all the options. */
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)
/* Print any do entation strings at the end. */
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);
}
/* 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. */
void __argp_help (const struct argp *argp, FILE *stream,
unsigned flags, char *name)
{
_help (argp, 0, stream, flags, name);
}
#ifdef weak_alias
weak_alias (__argp_help, argp_help)
#endif
#ifndef _LIBC
char *__argp_basename (char *name)
{
char *short_name = strrchr (name, '/');
return short_name ? short_name + 1 : name;
}
char *
__argp_short_program_name (void)
{
# if HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME
return program_invocation_short_name;
# elif HAVE_DECL_PROGRAM_INVOCATION_NAME
return __argp_basename (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,
so that requires more changes. */
# if __GNUC__
# warning No reasonable value to return
# endif /* __GNUC__ */
return "";
# endif
}
#endif
/* Output, if appropriate, a usage message for STATE to STREAM. FLAGS are
from the set ARGP_HELP_*. */
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
/* If appropriate, print the printf string FMT and following args, preceded
by the program name and `:', to stderr, and followed by a `Try ... --help'
message, then exit (1). */
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 _LIBC
char *buf;
if (_IO_vasprintf (&buf, fmt, ap) < 0)
buf = NULL;
__fxprintf (stream, "%s: %s\n",
state ? state->name : __argp_short_program_name (), buf);
free (buf);
#else
fputs_unlocked (state ? state->name : __argp_short_program_name (),
stream);
putc_unlocked (':', stream);
putc_unlocked (' ', stream);
vfprintf (stream, fmt, ap);
putc_unlocked ('\n', stream);
#endif
__argp_state_help (state, stream, ARGP_HELP_STD_ERR);
va_end (ap);

Reply With Quote