X-Git-Url: https://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fargp-help.c;h=150a0ad28f37e681c739653eb2e32e61676d3f5e;hb=298d8b4a29e66da0b046b64b822f97d1c8fef74b;hp=14ee68661041979600113ceebd7e10ebd99790b1;hpb=f81666cf0bfd795a33feea079c6196ed7fc39f2b;p=gnulib.git diff --git a/lib/argp-help.c b/lib/argp-help.c index 14ee68661..150a0ad28 100644 --- a/lib/argp-help.c +++ b/lib/argp-help.c @@ -1,83 +1,49 @@ /* Hierarchial argument parsing help output - Copyright (C) 1995-2000, 2001, 2002, 2003 Free Software Foundation, Inc. + Copyright (C) 1995-2005, 2007, 2009 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader . - This program is free software; you can redistribute it and/or modify + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #ifndef _GNU_SOURCE # define _GNU_SOURCE 1 #endif #ifdef HAVE_CONFIG_H -#include -#endif - -#ifndef alloca -# ifdef __GNUC__ -# define alloca __builtin_alloca -# define HAVE_ALLOCA 1 -# else -# if defined HAVE_ALLOCA_H || defined _LIBC -# include -# else -# ifdef _AIX - #pragma alloca -# else -# ifndef alloca -char *alloca (); -# endif -# endif -# endif -# endif +# include #endif +#include +#include #include #include #include #include #include #include +#include #ifdef USE_IN_LIBIO # include #endif -#ifndef _ -/* This is for other GNU distributions with internationalized messages. */ -# if defined HAVE_LIBINTL_H || defined _LIBC -# include -# ifdef _LIBC -# undef dgettext -# define dgettext(domain, msgid) \ - INTUSE(__dcgettext) (domain, msgid, LC_MESSAGES) -# endif -# else -# define dgettext(domain, msgid) (msgid) -# endif -#endif - -#ifndef _LIBC -# if HAVE_STRERROR_R -# if !HAVE_DECL_STRERROR_R -char *strerror_r (int errnum, char *buf, size_t buflen); -# endif -# else -# if !HAVE_DECL_STRERROR -char *strerror (int errnum); -# endif -# endif +#ifdef _LIBC +# include +# undef dgettext +# define dgettext(domain, msgid) \ + INTUSE(__dcgettext) (domain, msgid, LC_MESSAGES) +#else +# include "gettext.h" #endif #include "argp.h" @@ -122,15 +88,15 @@ struct uparams int dup_args_note; /* Various output columns. */ - 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; /* True when the values in here are valid. */ + int short_opt_col; /* column in which short options start */ + int long_opt_col; /* column in which long options start */ + int doc_opt_col; /* column in which doc options start */ + int opt_doc_col; /* column in which option text starts */ + int header_col; /* column in which group headers are printed */ + int usage_indent; /* indentation of wrapped usage lines */ + int rmargin; /* right margin used for wrapping */ + + int valid; /* True when the values in here are valid. */ }; /* This is a global variable, as user options are only ever read once. */ @@ -164,91 +130,126 @@ static const struct uparam_name uparam_names[] = { 0 } }; -/* Read user options from the environment, and fill in UPARAMS appropiately. */ +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; +} + +/* Read user options from the environment, and fill in UPARAMS appropiately. */ 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 (*p)) p++; } while (0); +#define SKIPWS(p) do { while (isspace ((unsigned char) *p)) p++; } while (0); if (var) - /* Parse var. */ - while (*var) - { - SKIPWS (var); - - if (isalpha (*var)) - { - size_t var_len; - const struct uparam_name *un; - int unspec = 0, val = 0; - const char *arg = var; - - while (isalnum (*arg) || *arg == '-' || *arg == '_') - arg++; - var_len = arg - var; + { + /* Parse var. */ + while (*var) + { + SKIPWS (var); - SKIPWS (arg); + if (isalpha ((unsigned char) *var)) + { + size_t var_len; + const struct uparam_name *un; + int unspec = 0, val = 0; + const char *arg = var; - if (*arg == '\0' || *arg == ',') - unspec = 1; - else if (*arg == '=') - { + while (isalnum ((unsigned char) *arg) || *arg == '-' || *arg == '_') arg++; - SKIPWS (arg); - } + var_len = arg - var; - if (unspec) - { - if (var[0] == 'n' && var[1] == 'o' && var[2] == '-') - { - val = 0; - var += 3; - var_len -= 3; - } - else - val = 1; - } - else if (isdigit (*arg)) - { - val = atoi (arg); - while (isdigit (*arg)) + SKIPWS (arg); + + if (*arg == '\0' || *arg == ',') + unspec = 1; + else if (*arg == '=') + { arg++; - SKIPWS (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) { - 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); + if (var[0] == 'n' && var[1] == 'o' && var[2] == '-') + { + val = 0; + var += 3; + var_len -= 3; + } else - *(int *)((char *)&uparams + un->uparams_offs) = val; - break; + val = 1; } - if (! un->name) - __argp_failure (state, 0, 0, - dgettext (state->root_argp->argp_domain, "\ + 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); + (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; - } - } + 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); + } } /* Returns true if OPT hasn't been marked invisible. Visibility only affects @@ -261,6 +262,9 @@ fill_in_uparams (const struct argp_state *state) /* Returns true if OPT is an documentation-only entry. */ #define odoc(opt) ((opt)->flags & OPTION_DOC) +/* Returns true if OPT should not be translated */ +#define onotrans(opt) ((opt)->flags & OPTION_NO_TRANS) + /* Returns true if OPT is the end-of-list marker for a list of options. */ #define oend(opt) __option_is_end (opt) @@ -370,6 +374,9 @@ struct hol_entry /* The argp from which this option came. */ const struct argp *argp; + + /* Position in the array */ + unsigned ord; }; /* A cluster of entries to reflect the argp tree structure. */ @@ -455,8 +462,9 @@ make_hol (const struct argp *argp, struct hol_cluster *cluster) hol->entries = malloc (sizeof (struct hol_entry) * hol->num_entries); hol->short_options = malloc (num_short_options + 1); - assert (hol->entries && hol->short_options - && hol->num_entries <= SIZE_MAX / sizeof (struct hol_entry)); + assert (hol->entries && hol->short_options); + if (SIZE_MAX <= UINT_MAX) + assert (hol->num_entries <= SIZE_MAX / sizeof (struct hol_entry)); /* Fill in the entries. */ so = hol->short_options; @@ -586,7 +594,7 @@ hol_entry_long_iterate (const struct hol_entry *entry, } /* Iterator that returns true for the first short option. */ -static inline int +static int until_short (const struct argp_option *opt, const struct argp_option *real, const char *domain, void *cookie) { @@ -667,10 +675,12 @@ static int 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. */ - while (cl1->depth < cl2->depth) + level, so that finding the common ancestor is straightforward. + + clN->depth > 0 means that clN->parent != NULL (see hol_add_cluster) */ + while (cl1->depth > cl2->depth) cl1 = cl1->parent; - while (cl2->depth < cl1->depth) + while (cl2->depth > cl1->depth) cl2 = cl2->parent; /* Now reduce both clusters to their ancestors at the point where both have @@ -708,17 +718,25 @@ static int canon_doc_option (const char **name) { int non_opt; - /* 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)++; + + 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)++; + } return non_opt; } +#define HOL_ENTRY_PTRCMP(a,b) ((a)->ord < (b)->ord ? -1 : 1) + /* Order ENTRY1 & ENTRY2 by the order which they should appear in a help listing. */ static int @@ -728,6 +746,7 @@ hol_entry_cmp (const struct hol_entry *entry1, /* 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) { @@ -744,7 +763,8 @@ hol_entry_cmp (const struct hol_entry *entry1, return group_cmp (hol_cluster_base (entry1->cluster)->group, group2, 1); else /* Both entries are in clusters, we can just compare the clusters. */ - return hol_cluster_cmp (entry1->cluster, entry2->cluster); + return (rc = hol_cluster_cmp (entry1->cluster, entry2->cluster)) ? + rc : HOL_ENTRY_PTRCMP(entry1, entry2); } else if (group1 == group2) /* The entries are both in the same cluster and group, so compare them @@ -768,7 +788,8 @@ hol_entry_cmp (const struct hol_entry *entry1, return doc1 - doc2; else if (!short1 && !short2 && long1 && long2) /* Only long options. */ - return __strcasecmp (long1, long2); + return (rc = __strcasecmp (long1, long2)) ? + rc : HOL_ENTRY_PTRCMP(entry1, entry2); else /* Compare short/short, long/short, short/long, using the first character of long options. Entries without *any* valid @@ -776,22 +797,22 @@ hol_entry_cmp (const struct hol_entry *entry1, first, but as they're not displayed, it doesn't matter where they are. */ { - char first1 = short1 ? short1 : long1 ? *long1 : 0; - char first2 = short2 ? short2 : long2 ? *long2 : 0; -#ifdef _tolower - int lower_cmp = _tolower (first1) - _tolower (first2); -#else + 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. */ 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 : first2 - first1; + return lower_cmp ? lower_cmp : + (rc = first2 - first1) ? + rc : HOL_ENTRY_PTRCMP(entry1, entry2); } } else /* Within the same cluster, but not the same group, so just compare groups. */ - return group_cmp (group1, group2, 0); + return group_cmp (group1, group2, HOL_ENTRY_PTRCMP(entry1, entry2)); } /* Version of hol_entry_cmp with correct signature for qsort. */ @@ -808,8 +829,14 @@ static void hol_sort (struct hol *hol) { if (hol->num_entries > 0) - qsort (hol->entries, hol->num_entries, sizeof (struct hol_entry), - hol_entry_qcmp); + { + 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); + } } /* Append MORE to HOL, destroying MORE in the process. Options in HOL shadow @@ -849,8 +876,9 @@ hol_append (struct hol *hol, struct hol *more) char *short_options = malloc (hol_so_len + strlen (more->short_options) + 1); - assert (entries && short_options - && num_entries <= SIZE_MAX / sizeof (struct hol_entry)); + 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)), @@ -1077,7 +1105,13 @@ hol_entry_help (struct hol_entry *entry, const struct argp_state *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 = { entry, stream, hhstate, 1, state }; + 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--) @@ -1112,13 +1146,15 @@ hol_entry_help (struct hol_entry *entry, const struct argp_state *state, { __argp_fmtstream_set_wmargin (stream, uparams.doc_opt_col); for (opt = real, num = entry->num; num > 0; opt++, num--) - if (opt->name && ovisible (opt)) + if (opt->name && *opt->name && ovisible (opt)) { comma (uparams.doc_opt_col, &pest); - /* Calling gettext here isn't quite right, since sorting will + /* 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, opt->name)); } @@ -1283,7 +1319,7 @@ usage_long_opt (const struct argp_option *opt, if (! arg) arg = real->arg; - if (! (flags & OPTION_NO_USAGE)) + if (! (flags & OPTION_NO_USAGE) && !odoc (opt)) { if (arg) { @@ -1454,46 +1490,51 @@ argp_doc (const struct argp *argp, const struct argp_state *state, { 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 (doc) + if (argp->doc) { - char *vt = strchr (doc, '\v'); - inp_text = post ? (vt ? vt + 1 : 0) : doc; - inp_text_limit = (!post && vt) ? (vt - doc) : 0; + 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 - inp_text = 0; + trans_text = 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); + trans_text, input); } else - text = (const char *) inp_text; + text = (const char *) trans_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); + __argp_fmtstream_puts (stream, text); if (__argp_fmtstream_point (stream) > __argp_fmtstream_lmargin (stream)) __argp_fmtstream_putc (stream, '\n'); @@ -1501,9 +1542,10 @@ argp_doc (const struct argp *argp, const struct argp_state *state, anything = 1; } - if (text && text != inp_text) + if (text && text != trans_text) free ((char *) text); /* Free TEXT returned from the help filter. */ - if (inp_text && inp_text_limit && argp->help_filter) + + if (inp_text && inp_text_len) free ((char *) inp_text); /* We copied INP_TEXT, so free it now. */ if (post && argp->help_filter) @@ -1683,26 +1725,21 @@ Try `%s --help' or `%s --usage' for more information.\n"), void __argp_help (const struct argp *argp, FILE *stream, unsigned flags, char *name) { - _help (argp, 0, stream, flags, 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 -#ifndef _LIBC -char *__argp_basename (char *name) -{ - char *short_name = strrchr (name, '/'); - return short_name ? short_name + 1 : name; -} - +#if ! (defined _LIBC || HAVE_DECL_PROGRAM_INVOCATION_SHORT_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); +# if HAVE_DECL_PROGRAM_INVOCATION_NAME + return __argp_base_name (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, @@ -1766,7 +1803,8 @@ __argp_error (const struct argp_state *state, const char *fmt, ...) { char *buf; - __asprintf (&buf, fmt, ap); + if (__asprintf (&buf, fmt, ap) < 0) + buf = NULL; __fwprintf (stream, L"%s: %s\n", state ? state->name : __argp_short_program_name (), @@ -1844,7 +1882,8 @@ __argp_failure (const struct argp_state *state, int status, int errnum, { char *buf; - __asprintf (&buf, fmt, ap); + if (__asprintf (&buf, fmt, ap) < 0) + buf = NULL; __fwprintf (stream, L": %s", buf); @@ -1873,13 +1912,21 @@ __argp_failure (const struct argp_state *state, int status, int errnum, else #endif { + char const *s = NULL; putc_unlocked (':', stream); putc_unlocked (' ', stream); -#if defined _LIBC || defined HAVE_STRERROR_R - fputs (__strerror_r (errnum, buf, sizeof (buf)), stream); -#else - fputs (strerror (errnum), 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); } }