mirror of
				https://sourceware.org/git/glibc.git
				synced 2025-10-30 10:45:40 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			1893 lines
		
	
	
		
			54 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1893 lines
		
	
	
		
			54 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* Hierarchial argument parsing help output
 | ||
|    Copyright (C) 1995-2017 Free Software Foundation, Inc.
 | ||
|    This file is part of the GNU C Library.
 | ||
|    Written by Miles Bader <miles@gnu.ai.mit.edu>.
 | ||
| 
 | ||
|    The GNU C Library is free software; you can redistribute it and/or
 | ||
|    modify it under the terms of the GNU Lesser General Public
 | ||
|    License as published by the Free Software Foundation; either
 | ||
|    version 2.1 of the License, or (at your option) any later version.
 | ||
| 
 | ||
|    The GNU C Library 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
 | ||
|    Lesser General Public License for more details.
 | ||
| 
 | ||
|    You should have received a copy of the GNU Lesser General Public
 | ||
|    License along with the GNU C Library; if not, see
 | ||
|    <http://www.gnu.org/licenses/>.  */
 | ||
| 
 | ||
| #ifndef _GNU_SOURCE
 | ||
| # define _GNU_SOURCE	1
 | ||
| #endif
 | ||
| 
 | ||
| #ifdef HAVE_CONFIG_H
 | ||
| #include <config.h>
 | ||
| #endif
 | ||
| 
 | ||
| /* AIX requires this to be the first thing in the file.  */
 | ||
| #ifndef __GNUC__
 | ||
| # if HAVE_ALLOCA_H || defined _LIBC
 | ||
| #  include <alloca.h>
 | ||
| # else
 | ||
| #  ifdef _AIX
 | ||
| #pragma alloca
 | ||
| #  else
 | ||
| #   ifndef alloca /* predefined by HP cc +Olibcalls */
 | ||
| char *alloca ();
 | ||
| #   endif
 | ||
| #  endif
 | ||
| # endif
 | ||
| #endif
 | ||
| 
 | ||
| #include <stdbool.h>
 | ||
| #include <stddef.h>
 | ||
| #include <stdlib.h>
 | ||
| #include <string.h>
 | ||
| #include <assert.h>
 | ||
| #include <stdarg.h>
 | ||
| #include <ctype.h>
 | ||
| #include <limits.h>
 | ||
| #ifdef _LIBC
 | ||
| # include <../libio/libioP.h>
 | ||
| # include <wchar.h>
 | ||
| #endif
 | ||
| 
 | ||
| #ifndef _
 | ||
| /* This is for other GNU distributions with internationalized messages.  */
 | ||
| # if defined HAVE_LIBINTL_H || defined _LIBC
 | ||
| #  include <libintl.h>
 | ||
| #  ifdef _LIBC
 | ||
| #   undef dgettext
 | ||
| #   define dgettext(domain, msgid) \
 | ||
|   __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
 | ||
| #endif
 | ||
| 
 | ||
| #include "argp.h"
 | ||
| #include "argp-fmtstream.h"
 | ||
| #include "argp-namefrob.h"
 | ||
| 
 | ||
| #ifndef SIZE_MAX
 | ||
| # define SIZE_MAX ((size_t) -1)
 | ||
| #endif
 | ||
| 
 | ||
| /* User-selectable (using an environment variable) formatting parameters.
 | ||
| 
 | ||
|    These may be specified in an environment variable called `ARGP_HELP_FMT',
 | ||
|    with a contents like:  VAR1=VAL1,VAR2=VAL2,BOOLVAR2,no-BOOLVAR2
 | ||
|    Where VALn must be a positive integer.  The list of variables is in the
 | ||
|    UPARAM_NAMES vector, below.  */
 | ||
| 
 | ||
| /* Default parameters.  */
 | ||
| #define DUP_ARGS      0		/* True if option argument can be duplicated. */
 | ||
| #define DUP_ARGS_NOTE 1		/* True to print a note about duplicate args. */
 | ||
| #define SHORT_OPT_COL 2		/* column in which short options start */
 | ||
| #define LONG_OPT_COL  6		/* column in which long options start */
 | ||
| #define DOC_OPT_COL   2		/* column in which doc options start */
 | ||
| #define OPT_DOC_COL  29		/* column in which option text starts */
 | ||
| #define HEADER_COL    1		/* column in which group headers are printed */
 | ||
| #define USAGE_INDENT 12		/* indentation of wrapped usage lines */
 | ||
| #define RMARGIN      79		/* right margin used for wrapping */
 | ||
| 
 | ||
| /* User-selectable (using an environment variable) formatting parameters.
 | ||
|    They must all be of type `int' for the parsing code to work.  */
 | ||
| struct uparams
 | ||
| {
 | ||
|   /* If true, arguments for an option are shown with both short and long
 | ||
|      options, even when a given option has both, e.g. `-x ARG, --longx=ARG'.
 | ||
|      If false, then if an option has both, the argument is only shown with
 | ||
|      the long one, e.g., `-x, --longx=ARG', and a message indicating that
 | ||
|      this really means both is printed below the options.  */
 | ||
|   int dup_args;
 | ||
| 
 | ||
|   /* This is true if when DUP_ARGS is false, and some duplicate arguments have
 | ||
|      been suppressed, an explanatory message should be printed.  */
 | ||
|   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;
 | ||
| };
 | ||
| 
 | ||
| /* This is a global variable, as user options are only ever read once.  */
 | ||
| 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
 | ||
| };
 | ||
| 
 | ||
| /* A particular uparam, and what the user name is.  */
 | ||
| struct uparam_name
 | ||
| {
 | ||
|   const char name[14];		/* User name.  */
 | ||
|   bool is_bool;			/* Whether it's `boolean'.  */
 | ||
|   uint8_t 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",       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]))
 | ||
| 
 | ||
| /* Read user options from the environment, and fill in UPARAMS appropriately.  */
 | ||
| static void
 | ||
| fill_in_uparams (const struct argp_state *state)
 | ||
| {
 | ||
|   const char *var = getenv ("ARGP_HELP_FMT");
 | ||
| 
 | ||
| #define SKIPWS(p) do { while (isspace (*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;
 | ||
| 
 | ||
| 	    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 (*arg))
 | ||
| 	      {
 | ||
| 		val = atoi (arg);
 | ||
| 		while (isdigit (*arg))
 | ||
| 		  arg++;
 | ||
| 		SKIPWS (arg);
 | ||
| 	      }
 | ||
| 
 | ||
| 	    un = uparam_names;
 | ||
| 	    size_t u;
 | ||
| 	    for (u = 0; u < nuparam_names; ++un, ++u)
 | ||
| 	      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 == NULL ? NULL
 | ||
| 					      : state->root_argp->argp_domain,
 | ||
| 					      "\
 | ||
| %.*s: ARGP_HELP_FMT parameter requires a value"),
 | ||
| 				    (int) var_len, var);
 | ||
| 		  else
 | ||
| 		    *(int *)((char *)&uparams + un->uparams_offs) = val;
 | ||
| 		  break;
 | ||
| 		}
 | ||
| 	    if (u == nuparam_names)
 | ||
| 	      __argp_failure (state, 0, 0,
 | ||
| 			      dgettext (state == NULL ? NULL
 | ||
| 					: 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 == NULL ? NULL
 | ||
| 				      : state->root_argp->argp_domain,
 | ||
| 				      "Garbage in ARGP_HELP_FMT: %s"), var);
 | ||
| 	    break;
 | ||
| 	  }
 | ||
|       }
 | ||
| }
 | ||
| 
 | ||
| /* Returns true if OPT hasn't been marked invisible.  Visibility only affects
 | ||
|    whether OPT is displayed or used in sorting, not option shadowing.  */
 | ||
| #define ovisible(opt) (! ((opt)->flags & OPTION_HIDDEN))
 | ||
| 
 | ||
| /* Returns true if OPT is an alias for an earlier option.  */
 | ||
| #define oalias(opt) ((opt)->flags & OPTION_ALIAS)
 | ||
| 
 | ||
| /* Returns true if OPT is an documentation-only entry.  */
 | ||
| #define odoc(opt) ((opt)->flags & OPTION_DOC)
 | ||
| 
 | ||
| /* Returns true if OPT is the end-of-list marker for a list of options.  */
 | ||
| #define oend(opt) __option_is_end (opt)
 | ||
| 
 | ||
| /* Returns true if OPT has a short option.  */
 | ||
| #define oshort(opt) __option_is_short (opt)
 | ||
| 
 | ||
| /*
 | ||
|    The help format for a particular option is like:
 | ||
| 
 | ||
|      -xARG, -yARG, --long1=ARG, --long2=ARG        Documentation...
 | ||
| 
 | ||
|    Where ARG will be omitted if there's no argument, for this option, or
 | ||
|    will be surrounded by "[" and "]" appropriately if the argument is
 | ||
|    optional.  The documentation string is word-wrapped appropriately, and if
 | ||
|    the list of options is long enough, it will be started on a separate line.
 | ||
|    If there are no short options for a given option, the first long option is
 | ||
|    indented slightly in a way that's supposed to make most long options appear
 | ||
|    to be in a separate column.
 | ||
| 
 | ||
|    For example, the following output (from ps):
 | ||
| 
 | ||
|      -p PID, --pid=PID          List the process PID
 | ||
| 	 --pgrp=PGRP            List processes in the process group PGRP
 | ||
|      -P, -x, --no-parent        Include processes without parents
 | ||
|      -Q, --all-fields           Don't elide unusable fields (normally if there's
 | ||
| 				some reason ps can't print a field for any
 | ||
| 				process, it's removed from the output entirely)
 | ||
|      -r, --reverse, --gratuitously-long-reverse-option
 | ||
| 				Reverse the order of any sort
 | ||
| 	 --session[=SID]        Add the processes from the session SID (which
 | ||
| 				defaults to the sid of the current process)
 | ||
| 
 | ||
|     Here are some more options:
 | ||
|      -f ZOT, --foonly=ZOT       Glork a foonly
 | ||
|      -z, --zaza                 Snit a zar
 | ||
| 
 | ||
|      -?, --help                 Give this help list
 | ||
| 	 --usage                Give a short usage message
 | ||
|      -V, --version              Print program version
 | ||
| 
 | ||
|    The struct argp_option array for the above could look like:
 | ||
| 
 | ||
|    {
 | ||
|      {"pid",       'p',      "PID",  0, "List the process PID"},
 | ||
|      {"pgrp",      OPT_PGRP, "PGRP", 0, "List processes in the process group PGRP"},
 | ||
|      {"no-parent", 'P',	      0,     0, "Include processes without parents"},
 | ||
|      {0,           'x',       0,     OPTION_ALIAS},
 | ||
|      {"all-fields",'Q',       0,     0, "Don't elide unusable fields (normally"
 | ||
| 					" if there's some reason ps can't"
 | ||
| 					" print a field for any process, it's"
 | ||
| 					" removed from the output entirely)" },
 | ||
|      {"reverse",   'r',       0,     0, "Reverse the order of any sort"},
 | ||
|      {"gratuitously-long-reverse-option", 0, 0, OPTION_ALIAS},
 | ||
|      {"session",   OPT_SESS,  "SID", OPTION_ARG_OPTIONAL,
 | ||
| 					"Add the processes from the session"
 | ||
| 					" SID (which defaults to the sid of"
 | ||
| 					" the current process)" },
 | ||
| 
 | ||
|      {0,0,0,0, "Here are some more options:"},
 | ||
|      {"foonly", 'f', "ZOT", 0, "Glork a foonly"},
 | ||
|      {"zaza", 'z', 0, 0, "Snit a zar"},
 | ||
| 
 | ||
|      {0}
 | ||
|    }
 | ||
| 
 | ||
|    Note that the last three options are automatically supplied by argp_parse,
 | ||
|    unless you tell it not to with ARGP_NO_HELP.
 | ||
| 
 | ||
| */
 | ||
| 
 | ||
| /* Returns true if CH occurs between BEG and END.  */
 | ||
| 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;		/* fwd decl */
 | ||
| 
 | ||
| struct hol_entry
 | ||
| {
 | ||
|   /* First option.  */
 | ||
|   const struct argp_option *opt;
 | ||
|   /* Number of options (including aliases).  */
 | ||
|   unsigned num;
 | ||
| 
 | ||
|   /* A pointers into the HOL's short_options field, to the first short option
 | ||
|      letter for this entry.  The order of the characters following this point
 | ||
|      corresponds to the order of options pointed to by OPT, and there are at
 | ||
|      most NUM.  A short option recorded in an option following OPT is only
 | ||
|      valid if it occurs in the right place in SHORT_OPTIONS (otherwise it's
 | ||
|      probably been shadowed by some other entry).  */
 | ||
|   char *short_options;
 | ||
| 
 | ||
|   /* Entries are sorted by their group first, in the order:
 | ||
|        1, 2, ..., n, 0, -m, ..., -2, -1
 | ||
|      and then alphabetically within each group.  The default is 0.  */
 | ||
|   int group;
 | ||
| 
 | ||
|   /* The cluster of options this entry belongs to, or 0 if none.  */
 | ||
|   struct hol_cluster *cluster;
 | ||
| 
 | ||
|   /* The argp from which this option came.  */
 | ||
|   const struct argp *argp;
 | ||
| };
 | ||
| 
 | ||
| /* A cluster of entries to reflect the argp tree structure.  */
 | ||
| struct hol_cluster
 | ||
| {
 | ||
|   /* A descriptive header printed before options in this cluster.  */
 | ||
|   const char *header;
 | ||
| 
 | ||
|   /* Used to order clusters within the same group with the same parent,
 | ||
|      according to the order in which they occurred in the parent argp's child
 | ||
|      list.  */
 | ||
|   int index;
 | ||
| 
 | ||
|   /* How to sort this cluster with respect to options and other clusters at the
 | ||
|      same depth (clusters always follow options in the same group).  */
 | ||
|   int group;
 | ||
| 
 | ||
|   /* The cluster to which this cluster belongs, or 0 if it's at the base
 | ||
|      level.  */
 | ||
|   struct hol_cluster *parent;
 | ||
| 
 | ||
|   /* The argp from which this cluster is (eventually) derived.  */
 | ||
|   const struct argp *argp;
 | ||
| 
 | ||
|   /* The distance this cluster is from the root.  */
 | ||
|   int depth;
 | ||
| 
 | ||
|   /* Clusters in a given hol are kept in a linked list, to make freeing them
 | ||
|      possible.  */
 | ||
|   struct hol_cluster *next;
 | ||
| };
 | ||
| 
 | ||
| /* A list of options for help.  */
 | ||
| struct hol
 | ||
| {
 | ||
|   /* An array of hol_entry's.  */
 | ||
|   struct hol_entry *entries;
 | ||
|   /* The number of entries in this hol.  If this field is zero, the others
 | ||
|      are undefined.  */
 | ||
|   unsigned num_entries;
 | ||
| 
 | ||
|   /* A string containing all short options in this HOL.  Each entry contains
 | ||
|      pointers into this string, so the order can't be messed with blindly.  */
 | ||
|   char *short_options;
 | ||
| 
 | ||
|   /* Clusters of entries in this hol.  */
 | ||
|   struct hol_cluster *clusters;
 | ||
| };
 | ||
| 
 | ||
| /* Create a struct hol from the options in ARGP.  CLUSTER is the
 | ||
|    hol_cluster in which these entries occur, or 0, if at the root.  */
 | ||
| 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;
 | ||
| 
 | ||
|       /* The first option must not be an alias.  */
 | ||
|       assert (! oalias (opts));
 | ||
| 
 | ||
|       /* Calculate the space needed.  */
 | ||
|       for (o = opts; ! oend (o); o++)
 | ||
| 	{
 | ||
| 	  if (! oalias (o))
 | ||
| 	    hol->num_entries++;
 | ||
| 	  if (oshort (o))
 | ||
| 	    num_short_options++;	/* This is an upper bound.  */
 | ||
| 	}
 | ||
| 
 | ||
|       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));
 | ||
| #endif
 | ||
| 
 | ||
|       /* Fill in the entries.  */
 | ||
|       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))
 | ||
| 		/* O has a valid short option which hasn't already been used.*/
 | ||
| 		*so++ = o->key;
 | ||
| 	      o++;
 | ||
| 	    }
 | ||
| 	  while (! oend (o) && oalias (o));
 | ||
| 	}
 | ||
|       *so = '\0';		/* null terminated so we can find the length */
 | ||
|     }
 | ||
| 
 | ||
|   return hol;
 | ||
| }
 | ||
| 
 | ||
| /* Add a new cluster to HOL, with the given GROUP and HEADER (taken from the
 | ||
|    associated argp child list entry), INDEX, and PARENT, and return a pointer
 | ||
|    to it.  ARGP is the argp that this cluster results from.  */
 | ||
| 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;
 | ||
| }
 | ||
| 
 | ||
| /* Free HOL and any resources it uses.  */
 | ||
| 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;
 | ||
| }
 | ||
| 
 | ||
| /* Iterator that returns true for the first short option.  */
 | ||
| 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;
 | ||
| }
 | ||
| 
 | ||
| /* Returns the first valid short option in ENTRY, or 0 if there is none.  */
 | ||
| static char
 | ||
| hol_entry_first_short (const struct hol_entry *entry)
 | ||
| {
 | ||
|   return hol_entry_short_iterate (entry, until_short,
 | ||
| 				  entry->argp->argp_domain, 0);
 | ||
| }
 | ||
| 
 | ||
| /* Returns the first valid long option in ENTRY, or 0 if there is none.  */
 | ||
| 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;
 | ||
| }
 | ||
| 
 | ||
| /* Returns the entry in HOL with the long option name NAME, or 0 if there is
 | ||
|    none.  */
 | ||
| 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;
 | ||
| }
 | ||
| 
 | ||
| /* If an entry with the long option NAME occurs in HOL, set it's special
 | ||
|    sort position to GROUP.  */
 | ||
| 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;
 | ||
| }
 | ||
| 
 | ||
| /* Order by group:  0, 1, 2, ..., n, -m, ..., -2, -1.
 | ||
|    EQ is what to return if GROUP1 and GROUP2 are the same.  */
 | ||
| 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;
 | ||
| }
 | ||
| 
 | ||
| /* Compare clusters CL1 & CL2 by the order that they should appear in
 | ||
|    output.  */
 | ||
| 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)
 | ||
|     cl1 = cl1->parent;
 | ||
|   while (cl2->depth > cl1->depth)
 | ||
|     cl2 = cl2->parent;
 | ||
| 
 | ||
|   /* Now reduce both clusters to their ancestors at the point where both have
 | ||
|      a common parent; these can be directly compared.  */
 | ||
|   while (cl1->parent != cl2->parent)
 | ||
|     cl1 = cl1->parent, cl2 = cl2->parent;
 | ||
| 
 | ||
|   return group_cmp (cl1->group, cl2->group, cl2->index - cl1->index);
 | ||
| }
 | ||
| 
 | ||
| /* Return the ancestor of CL that's just below the root (i.e., has a parent
 | ||
|    of 0).  */
 | ||
| static struct hol_cluster *
 | ||
| hol_cluster_base (struct hol_cluster *cl)
 | ||
| {
 | ||
|   while (cl->parent)
 | ||
|     cl = cl->parent;
 | ||
|   return cl;
 | ||
| }
 | ||
| 
 | ||
| /* Return true if CL1 is a child of CL2.  */
 | ||
| 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;
 | ||
| }
 | ||
| 
 | ||
| /* Given the name of a OPTION_DOC option, modifies NAME to start at the tail
 | ||
|    that should be used for comparisons, and returns true iff it should be
 | ||
|    treated as a non-option.  */
 | ||
| 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)++;
 | ||
|   return non_opt;
 | ||
| }
 | ||
| 
 | ||
| /* Order ENTRY1 & ENTRY2 by the order which they should appear in a help
 | ||
|    listing.  */
 | ||
| static int
 | ||
| hol_entry_cmp (const struct hol_entry *entry1,
 | ||
| 	       const struct hol_entry *entry2)
 | ||
| {
 | ||
|   /* 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;
 | ||
| 
 | ||
|   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
 | ||
| 	   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 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 hol_cluster_cmp (entry1->cluster, entry2->cluster);
 | ||
|     }
 | ||
|   else if (group1 == group2)
 | ||
|     /* The entries are both in the same cluster and group, so compare them
 | ||
|        alphabetically.  */
 | ||
|     {
 | ||
|       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 = long1 != NULL && canon_doc_option (&long1);
 | ||
|       if (doc2)
 | ||
| 	doc2 = long2 != NULL && canon_doc_option (&long2);
 | ||
| 
 | ||
|       if (doc1 != doc2)
 | ||
| 	/* `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 __strcasecmp (long1, long2);
 | ||
|       else
 | ||
| 	/* Compare short/short, long/short, short/long, using the first
 | ||
| 	   character of long options.  Entries without *any* valid
 | ||
| 	   options (such as options with OPTION_HIDDEN set) will be put
 | ||
| 	   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
 | ||
| 	  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;
 | ||
| 	}
 | ||
|     }
 | ||
|   else
 | ||
|     /* Within the same cluster, but not the same group, so just compare
 | ||
|        groups.  */
 | ||
|     return group_cmp (group1, group2, 0);
 | ||
| }
 | ||
| 
 | ||
| /* Version of hol_entry_cmp with correct signature for qsort.  */
 | ||
| static int
 | ||
| hol_entry_qcmp (const void *entry1_v, const void *entry2_v)
 | ||
| {
 | ||
|   return hol_entry_cmp (entry1_v, entry2_v);
 | ||
| }
 | ||
| 
 | ||
| /* Sort HOL by group and alphabetically by option name (with short options
 | ||
|    taking precedence over long).  Since the sorting is for display purposes
 | ||
|    only, the shadowing of options isn't effected.  */
 | ||
| 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);
 | ||
| }
 | ||
| 
 | ||
| /* Append MORE to HOL, destroying MORE in the process.  Options in HOL shadow
 | ||
|    any in MORE with the same name.  */
 | ||
| static void
 | ||
| hol_append (struct hol *hol, struct hol *more)
 | ||
| {
 | ||
|   struct hol_cluster **cl_end = &hol->clusters;
 | ||
| 
 | ||
|   /* Steal MORE's cluster list, and add it to the end of HOL's.  */
 | ||
|   while (*cl_end)
 | ||
|     cl_end = &(*cl_end)->next;
 | ||
|   *cl_end = more->clusters;
 | ||
|   more->clusters = 0;
 | ||
| 
 | ||
|   /* Merge entries.  */
 | ||
|   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;	/* Mark MORE's fields as invalid.  */
 | ||
| 	}
 | ||
|       else
 | ||
| 	/* Append the entries in MORE to those in HOL, taking care to only add
 | ||
| 	   non-shadowed SHORT_OPTIONS values.  */
 | ||
| 	{
 | ||
| 	  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));
 | ||
| #endif
 | ||
| 
 | ||
| 	  __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);
 | ||
| 
 | ||
| 	  /* Fix up the short options pointers from HOL.  */
 | ||
| 	  for (e = entries, left = hol->num_entries; left > 0; e++, left--)
 | ||
| 	    e->short_options += (short_options - hol->short_options);
 | ||
| 
 | ||
| 	  /* Now add the short options from MORE, fixing up its entries
 | ||
| 	     too.  */
 | ||
| 	  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)
 | ||
| 		    /* The next short option in MORE_SO, CH, is from OPT.  */
 | ||
| 		    {
 | ||
| 		      if (! find_char (ch, short_options,
 | ||
| 				       short_options + hol_so_len))
 | ||
| 			/* The short option CH isn't shadowed by HOL's options,
 | ||
| 			   so add it to the sum.  */
 | ||
| 			*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);
 | ||
| }
 | ||
| 
 | ||
| /* Inserts enough spaces to make sure STREAM is at column COL.  */
 | ||
| static void
 | ||
| indent_to (argp_fmtstream_t stream, unsigned col)
 | ||
| {
 | ||
|   int needed = col - __argp_fmtstream_point (stream);
 | ||
|   while (needed-- > 0)
 | ||
|     __argp_fmtstream_putc (stream, ' ');
 | ||
| }
 | ||
| 
 | ||
| /* Output to STREAM either a space, or a newline if there isn't room for at
 | ||
|    least ENSURE characters before the right margin.  */
 | ||
| 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, ' ');
 | ||
| }
 | ||
| 
 | ||
| /* If the option REAL has an argument, we print it in using the printf
 | ||
|    format REQ_FMT or OPT_FMT depending on whether it's a required or
 | ||
|    optional argument.  */
 | ||
| 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));
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* Helper functions for hol_entry_help.  */
 | ||
| 
 | ||
| /* State used during the execution of hol_help.  */
 | ||
| struct hol_help_state
 | ||
| {
 | ||
|   /* PREV_ENTRY should contain the previous entry printed, or 0.  */
 | ||
|   struct hol_entry *prev_entry;
 | ||
| 
 | ||
|   /* If an entry is in a different group from the previous one, and SEP_GROUPS
 | ||
|      is true, then a blank line will be printed before any output. */
 | ||
|   int sep_groups;
 | ||
| 
 | ||
|   /* True if a duplicate option argument was suppressed (only ever set if
 | ||
|      UPARAMS.dup_args is false).  */
 | ||
|   int suppressed_dup_arg;
 | ||
| };
 | ||
| 
 | ||
| /* Some state used while printing a help entry (used to communicate with
 | ||
|    helper functions).  See the doc for hol_entry_help for more info, as most
 | ||
|    of the fields are copied from its arguments.  */
 | ||
| struct pentry_state
 | ||
| {
 | ||
|   const struct hol_entry *entry;
 | ||
|   argp_fmtstream_t stream;
 | ||
|   struct hol_help_state *hhstate;
 | ||
| 
 | ||
|   /* True if nothing's been printed so far.  */
 | ||
|   int first;
 | ||
| 
 | ||
|   /* If non-zero, the state that was used to print this help.  */
 | ||
|   const struct argp_state *state;
 | ||
| };
 | ||
| 
 | ||
| /* If a user doc filter should be applied to DOC, do so.  */
 | ||
| static const char *
 | ||
| filter_doc (const char *doc, int key, const struct argp *argp,
 | ||
| 	    const struct argp_state *state)
 | ||
| {
 | ||
|   if (argp && argp->help_filter)
 | ||
|     /* We must apply a user filter to this output.  */
 | ||
|     {
 | ||
|       void *input = __argp_input (argp, state);
 | ||
|       return (*argp->help_filter) (key, doc, input);
 | ||
|     }
 | ||
|   else
 | ||
|     /* No filter.  */
 | ||
|     return doc;
 | ||
| }
 | ||
| 
 | ||
| /* Prints STR as a header line, with the margin lines set appropriately, and
 | ||
|    notes the fact that groups should be separated with a blank line.  ARGP is
 | ||
|    the argp that should dictate any user doc filtering to take place.  Note
 | ||
|    that the previous wrap margin isn't restored, but the left margin is reset
 | ||
|    to 0.  */
 | ||
| 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)
 | ||
| 	    /* Precede with a blank line.  */
 | ||
| 	    __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; /* Separate subsequent groups. */
 | ||
|     }
 | ||
| 
 | ||
|   if (fstr != tstr)
 | ||
|     free ((char *) fstr);
 | ||
| }
 | ||
| 
 | ||
| /* Inserts a comma if this isn't the first item on the line, and then makes
 | ||
|    sure we're at least to column COL.  If this *is* the first item on a line,
 | ||
|    prints any pending whitespace/headers that should precede this line. Also
 | ||
|    clears FIRST.  */
 | ||
| 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))))
 | ||
| 	/* If we're changing clusters, then this must be the start of the
 | ||
| 	   ENTRY's cluster unless that is an ancestor of the previous one
 | ||
| 	   (in which case we had just popped into a sub-cluster for a bit).
 | ||
| 	   If so, then print the cluster's header line.  */
 | ||
| 	{
 | ||
| 	  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);
 | ||
| }
 | ||
| 
 | ||
| /* Print help for ENTRY to STREAM.  */
 | ||
| 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;	/* We have any long options.  */
 | ||
|   /* Saved margins.  */
 | ||
|   int old_lm = __argp_fmtstream_set_lmargin (stream, 0);
 | ||
|   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 };
 | ||
| 
 | ||
|   if (! odoc (real))
 | ||
|     for (opt = real, num = entry->num; num > 0; opt++, num--)
 | ||
|       if (opt->name && ovisible (opt))
 | ||
| 	{
 | ||
| 	  have_long_opt = 1;
 | ||
| 	  break;
 | ||
| 	}
 | ||
| 
 | ||
|   /* First emit short options.  */
 | ||
|   __argp_fmtstream_set_wmargin (stream, uparams.short_opt_col); /* For truly bizarre cases. */
 | ||
|   for (opt = real, num = entry->num; num > 0; opt++, num--)
 | ||
|     if (oshort (opt) && opt->key == *so)
 | ||
|       /* OPT has a valid (non shadowed) short option.  */
 | ||
|       {
 | ||
| 	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 == NULL ? NULL : state->root_argp->argp_domain,
 | ||
| 		   stream);
 | ||
| 	    else if (real->arg)
 | ||
| 	      hhstate->suppressed_dup_arg = 1;
 | ||
| 	  }
 | ||
| 	so++;
 | ||
|       }
 | ||
| 
 | ||
|   /* Now, long options.  */
 | ||
|   if (odoc (real))
 | ||
|     /* A `documentation' option.  */
 | ||
|     {
 | ||
|       __argp_fmtstream_set_wmargin (stream, uparams.doc_opt_col);
 | ||
|       for (opt = real, num = entry->num; num > 0; opt++, num--)
 | ||
| 	if (opt->name && ovisible (opt))
 | ||
| 	  {
 | ||
| 	    comma (uparams.doc_opt_col, &pest);
 | ||
| 	    /* Calling gettext 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,
 | ||
| 				   dgettext (state == NULL ? NULL
 | ||
| 					     : state->root_argp->argp_domain,
 | ||
| 					     opt->name));
 | ||
| 	  }
 | ||
|     }
 | ||
|   else
 | ||
|     /* A real long option.  */
 | ||
|     {
 | ||
|       __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);
 | ||
| 	    arg (real, "=%s", "[=%s]",
 | ||
| 		 state == NULL ? NULL : state->root_argp->argp_domain, stream);
 | ||
| 	  }
 | ||
|     }
 | ||
| 
 | ||
|   /* Next, documentation strings.  */
 | ||
|   __argp_fmtstream_set_lmargin (stream, 0);
 | ||
| 
 | ||
|   if (pest.first)
 | ||
|     {
 | ||
|       /* Didn't print any switches, what's up?  */
 | ||
|       if (!oshort (real) && !real->name)
 | ||
| 	/* This is a group header, print it nicely.  */
 | ||
| 	print_header (real->doc, entry->argp, &pest);
 | ||
|       else
 | ||
| 	/* Just a totally shadowed option or null header; print nothing.  */
 | ||
| 	goto cleanup;		/* Just return, after cleaning up.  */
 | ||
|     }
 | ||
|   else
 | ||
|     {
 | ||
|       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)
 | ||
| 	{
 | ||
| 	  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);
 | ||
| 
 | ||
|       /* Reset the left margin.  */
 | ||
|       __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);
 | ||
| }
 | ||
| 
 | ||
| /* Output a long help message about the options in HOL to STREAM.  */
 | ||
| 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 == 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,
 | ||
| 				     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);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* Helper functions for hol_usage.  */
 | ||
| 
 | ||
| /* If OPT is a short option without an arg, append its key to the string
 | ||
|    pointer pointer to by COOKIE, and advance the pointer.  */
 | ||
| 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;
 | ||
| }
 | ||
| 
 | ||
| /* If OPT is a short option with an arg, output a usage entry for it to the
 | ||
|    stream pointed at by COOKIE.  */
 | ||
| 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
 | ||
| 	{
 | ||
| 	  /* Manually do line wrapping so that it (probably) won't
 | ||
| 	     get wrapped at the embedded space.  */
 | ||
| 	  space (stream, 6 + strlen (arg));
 | ||
| 	  __argp_fmtstream_printf (stream, "[-%c %s]", opt->key, arg);
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| /* Output a usage entry for the long option opt to the stream pointed at by
 | ||
|    COOKIE.  */
 | ||
| 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))
 | ||
|     {
 | ||
|       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;
 | ||
| }
 | ||
| 
 | ||
| /* Print a short usage description for the arguments in HOL to STREAM.  */
 | ||
| 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;
 | ||
| 
 | ||
|       /* First we put a list of short options without arguments.  */
 | ||
|       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);
 | ||
| 	}
 | ||
| 
 | ||
|       /* Now a list of short options *with* arguments.  */
 | ||
|       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);
 | ||
| 
 | ||
|       /* Finally, a list of long options (whew!).  */
 | ||
|       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);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* 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 documented 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 documentation for ARGP to STREAM; if POST is false, then
 | ||
|    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,
 | ||
|    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 documentation 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);
 | ||
| 
 | ||
| #if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
 | ||
| 	  __funlockfile (stream);
 | ||
| #endif
 | ||
| 	}
 | ||
|     }
 | ||
| }
 | ||
| #ifdef weak_alias
 | ||
| weak_alias (__argp_error, argp_error)
 | ||
| #endif
 | ||
| 
 | ||
| /* Similar to the standard gnu error-reporting function error(), but will
 | ||
|    respect the ARGP_NO_EXIT and ARGP_NO_ERRS flags in STATE, and will print
 | ||
|    to STATE->err_stream.  This is useful for argument parsing code that is
 | ||
|    shared between program startup (when exiting is desired) and runtime
 | ||
|    option parsing (when typically an error code is returned instead).  The
 | ||
|    difference between this function and argp_error is that the latter is for
 | ||
|    *parsing errors*, and the former is for other problems that occur during
 | ||
|    parsing but don't reflect a (syntactic) problem with the input.  */
 | ||
| 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 _LIBC
 | ||
| 	  __fxprintf (stream, "%s",
 | ||
| 		      state ? state->name : __argp_short_program_name ());
 | ||
| #else
 | ||
| 	  fputs_unlocked (state ? state->name : __argp_short_program_name (),
 | ||
| 			  stream);
 | ||
| #endif
 | ||
| 
 | ||
| 	  if (fmt)
 | ||
| 	    {
 | ||
| 	      va_list ap;
 | ||
| 
 | ||
| 	      va_start (ap, fmt);
 | ||
| #ifdef _LIBC
 | ||
| 	      char *buf;
 | ||
| 
 | ||
| 	      if (_IO_vasprintf (&buf, fmt, ap) < 0)
 | ||
| 		buf = NULL;
 | ||
| 
 | ||
| 	      __fxprintf (stream, ": %s", buf);
 | ||
| 
 | ||
| 	      free (buf);
 | ||
| #else
 | ||
| 	      putc_unlocked (':', stream);
 | ||
| 	      putc_unlocked (' ', stream);
 | ||
| 
 | ||
| 	      vfprintf (stream, fmt, ap);
 | ||
| #endif
 | ||
| 
 | ||
| 	      va_end (ap);
 | ||
| 	    }
 | ||
| 
 | ||
| 	  if (errnum)
 | ||
| 	    {
 | ||
| 	      char buf[200];
 | ||
| 
 | ||
| #ifdef _LIBC
 | ||
| 	      __fxprintf (stream, ": %s",
 | ||
| 			  __strerror_r (errnum, buf, sizeof (buf)));
 | ||
| #else
 | ||
| 	      putc_unlocked (':', stream);
 | ||
| 	      putc_unlocked (' ', stream);
 | ||
| # ifdef HAVE_STRERROR_R
 | ||
| 	      fputs (__strerror_r (errnum, buf, sizeof (buf)), stream);
 | ||
| # else
 | ||
| 	      fputs (strerror (errnum), stream);
 | ||
| # endif
 | ||
| #endif
 | ||
| 	    }
 | ||
| 
 | ||
| 	  if (_IO_fwide (stream, 0) > 0)
 | ||
| 	    putwc_unlocked (L'\n', stream);
 | ||
| 	  else
 | ||
| 	    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
 |