root/maint/gnulib/lib/argmatch.h

/* [previous][next][first][last][top][bottom][index][help] */

INCLUDED FROM


   1 /* argmatch.h -- definitions and prototypes for argmatch.c
   2 
   3    Copyright (C) 1990, 1998-1999, 2001-2002, 2004-2005, 2009-2021 Free Software
   4    Foundation, Inc.
   5 
   6    This program is free software: you can redistribute it and/or modify
   7    it under the terms of the GNU General Public License as published by
   8    the Free Software Foundation; either version 3 of the License, or
   9    (at your option) any later version.
  10 
  11    This program is distributed in the hope that it will be useful,
  12    but WITHOUT ANY WARRANTY; without even the implied warranty of
  13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14    GNU General Public License for more details.
  15 
  16    You should have received a copy of the GNU General Public License
  17    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
  18 
  19 /* Written by David MacKenzie <djm@ai.mit.edu>
  20    Modified by Akim Demaille <demaille@inf.enst.fr> */
  21 
  22 #ifndef ARGMATCH_H_
  23 # define ARGMATCH_H_ 1
  24 
  25 # include <limits.h>
  26 # include <stdbool.h>
  27 # include <stddef.h>
  28 # include <stdio.h>
  29 # include <string.h> /* memcmp */
  30 
  31 # include "gettext.h"
  32 # include "quote.h"
  33 # include "verify.h"
  34 
  35 # ifdef  __cplusplus
  36 extern "C" {
  37 # endif
  38 
  39 # define ARRAY_CARDINALITY(Array) (sizeof (Array) / sizeof *(Array))
  40 
  41 /* Assert there are as many real arguments as there are values
  42    (argument list ends with a NULL guard).  */
  43 
  44 # define ARGMATCH_VERIFY(Arglist, Vallist) \
  45     verify (ARRAY_CARDINALITY (Arglist) == ARRAY_CARDINALITY (Vallist) + 1)
  46 
  47 /* Return the index of the element of ARGLIST (NULL terminated) that
  48    matches with ARG.  If VALLIST is not NULL, then use it to resolve
  49    false ambiguities (i.e., different matches of ARG but corresponding
  50    to the same values in VALLIST).  */
  51 
  52 ptrdiff_t argmatch (char const *arg, char const *const *arglist,
  53                     void const *vallist, size_t valsize) _GL_ATTRIBUTE_PURE;
  54 
  55 # define ARGMATCH(Arg, Arglist, Vallist) \
  56   argmatch (Arg, Arglist, (void const *) (Vallist), sizeof *(Vallist))
  57 
  58 /* xargmatch calls this function when it fails.  This function should not
  59    return.  By default, this is a function that calls ARGMATCH_DIE which
  60    in turn defaults to 'exit (exit_failure)'.  */
  61 typedef void (*argmatch_exit_fn) (void);
  62 extern argmatch_exit_fn argmatch_die;
  63 
  64 /* Report on stderr why argmatch failed.  Report correct values. */
  65 
  66 void argmatch_invalid (char const *context, char const *value,
  67                        ptrdiff_t problem);
  68 
  69 /* Left for compatibility with the old name invalid_arg */
  70 
  71 # define invalid_arg(Context, Value, Problem) \
  72   argmatch_invalid (Context, Value, Problem)
  73 
  74 
  75 
  76 /* Report on stderr the list of possible arguments.  */
  77 
  78 void argmatch_valid (char const *const *arglist,
  79                      void const *vallist, size_t valsize);
  80 
  81 # define ARGMATCH_VALID(Arglist, Vallist) \
  82   argmatch_valid (Arglist, (void const *) (Vallist), sizeof *(Vallist))
  83 
  84 
  85 
  86 /* Same as argmatch, but upon failure, report an explanation of the
  87    failure, and exit using the function EXIT_FN. */
  88 
  89 ptrdiff_t __xargmatch_internal (char const *context,
  90                                 char const *arg, char const *const *arglist,
  91                                 void const *vallist, size_t valsize,
  92                                 argmatch_exit_fn exit_fn);
  93 
  94 /* Programmer friendly interface to __xargmatch_internal. */
  95 
  96 # define XARGMATCH(Context, Arg, Arglist, Vallist)              \
  97   ((Vallist) [__xargmatch_internal (Context, Arg, Arglist,      \
  98                                     (void const *) (Vallist),   \
  99                                     sizeof *(Vallist),          \
 100                                     argmatch_die)])
 101 
 102 /* Convert a value into a corresponding argument. */
 103 
 104 char const *argmatch_to_argument (void const *value,
 105                                   char const *const *arglist,
 106                                   void const *vallist, size_t valsize)
 107   _GL_ATTRIBUTE_PURE;
 108 
 109 # define ARGMATCH_TO_ARGUMENT(Value, Arglist, Vallist)                  \
 110   argmatch_to_argument (Value, Arglist,                                 \
 111                         (void const *) (Vallist), sizeof *(Vallist))
 112 
 113 # define ARGMATCH_DEFINE_GROUP(Name, Type)                              \
 114   /* The type of the values of this group.  */                          \
 115   typedef Type argmatch_##Name##_type;                                  \
 116                                                                         \
 117   /* The size of the type of the values of this group. */               \
 118   enum argmatch_##Name##_size_enum                                      \
 119   {                                                                     \
 120     argmatch_##Name##_size = sizeof (argmatch_##Name##_type)            \
 121   };                                                                    \
 122                                                                         \
 123   /* Argument mapping of this group.  */                                \
 124   typedef struct                                                        \
 125   {                                                                     \
 126     /* Argument (e.g., "simple").  */                                   \
 127     const char *arg;                                                    \
 128     /* Value (e.g., simple_backups).  */                                \
 129     const argmatch_##Name##_type val;                                   \
 130   } argmatch_##Name##_arg;                                              \
 131                                                                         \
 132   /* Documentation of this group.  */                                   \
 133   typedef struct                                                        \
 134   {                                                                     \
 135     /* Argument (e.g., "simple").  */                                   \
 136     const char *arg;                                                    \
 137     /* Documentation (e.g., N_("always make simple backups")).  */      \
 138     const char *doc;                                                    \
 139   } argmatch_##Name##_doc;                                              \
 140                                                                         \
 141   /* All the features of an argmatch group.  */                         \
 142   typedef struct                                                        \
 143   {                                                                     \
 144     const argmatch_##Name##_arg* args;                                  \
 145     const argmatch_##Name##_doc* docs;                                  \
 146                                                                         \
 147     /* Printed before the usage message.  */                            \
 148     const char *doc_pre;                                                \
 149     /* Printed after the usage message.  */                             \
 150     const char *doc_post;                                               \
 151   } argmatch_##Name##_group_type;                                       \
 152                                                                         \
 153   /* The structure the user must build.  */                             \
 154   extern const argmatch_##Name##_group_type argmatch_##Name##_group;    \
 155                                                                         \
 156   /* Print the documentation of this group.  */                         \
 157   void argmatch_##Name##_usage (FILE *out);                             \
 158                                                                         \
 159   /* If nonnegative, the index I of ARG in ARGS, i.e,                   \
 160      ARGS[I] == ARG.                                                    \
 161      Return -1 for invalid argument, -2 for ambiguous argument. */      \
 162   ptrdiff_t argmatch_##Name##_choice (const char *arg);                 \
 163                                                                         \
 164   /* A pointer to the corresponding value if it exists, or              \
 165      report an error and exit with failure if the argument was          \
 166      not recognized. */                                                 \
 167   const argmatch_##Name##_type*                                         \
 168   argmatch_##Name##_value (const char *context, const char *arg);       \
 169                                                                         \
 170   /* The first argument in ARGS that matches this value, or NULL.  */   \
 171   const char *                                                          \
 172   argmatch_##Name##_argument (const argmatch_##Name##_type *val);       \
 173                                                                         \
 174   ptrdiff_t                                                             \
 175   argmatch_##Name##_choice (const char *arg)                            \
 176   {                                                                     \
 177     const argmatch_##Name##_group_type *g = &argmatch_##Name##_group;   \
 178     size_t size = argmatch_##Name##_size;                               \
 179     ptrdiff_t res = -1;      /* Index of first nonexact match.  */      \
 180     bool ambiguous = false;  /* Whether multiple nonexact match(es). */ \
 181     size_t arglen = strlen (arg);                                       \
 182                                                                         \
 183     /* Test all elements for either exact match or abbreviated          \
 184        matches.  */                                                     \
 185     for (size_t i = 0; g->args[i].arg; i++)                             \
 186       if (!strncmp (g->args[i].arg, arg, arglen))                       \
 187         {                                                               \
 188           if (strlen (g->args[i].arg) == arglen)                        \
 189             /* Exact match found.  */                                   \
 190             return i;                                                   \
 191           else if (res == -1)                                           \
 192             /* First nonexact match found.  */                          \
 193             res = i;                                                    \
 194           else if (memcmp (&g->args[res].val, &g->args[i].val, size))   \
 195             /* Second nonexact match found.  */                         \
 196             /* There is a real ambiguity, or we could not               \
 197                disambiguate. */                                         \
 198             ambiguous = true;                                           \
 199         }                                                               \
 200     return ambiguous ? -2 : res;                                        \
 201   }                                                                     \
 202                                                                         \
 203   const char *                                                          \
 204   argmatch_##Name##_argument (const argmatch_##Name##_type *val)        \
 205   {                                                                     \
 206     const argmatch_##Name##_group_type *g = &argmatch_##Name##_group;   \
 207     size_t size = argmatch_##Name##_size;                               \
 208     for (size_t i = 0; g->args[i].arg; i++)                             \
 209       if (!memcmp (val, &g->args[i].val, size))                         \
 210         return g->args[i].arg;                                          \
 211     return NULL;                                                        \
 212   }                                                                     \
 213                                                                         \
 214   /* List the valid values of this group. */                            \
 215   static void                                                           \
 216   argmatch_##Name##_valid (FILE *out)                                   \
 217   {                                                                     \
 218     const argmatch_##Name##_group_type *g = &argmatch_##Name##_group;   \
 219     size_t size = argmatch_##Name##_size;                               \
 220                                                                         \
 221     /* Try to put synonyms on the same line.  Synonyms are expected     \
 222        to follow each other. */                                         \
 223     fputs (gettext ("Valid arguments are:"), out);                      \
 224     for (int i = 0; g->args[i].arg; i++)                                \
 225       if (i == 0                                                        \
 226           || memcmp (&g->args[i-1].val, &g->args[i].val, size))         \
 227         fprintf (out, "\n  - %s", quote (g->args[i].arg));              \
 228       else                                                              \
 229         fprintf (out, ", %s", quote (g->args[i].arg));                  \
 230     putc ('\n', out);                                                   \
 231   }                                                                     \
 232                                                                         \
 233   const argmatch_##Name##_type*                                         \
 234   argmatch_##Name##_value (const char *context, const char *arg)        \
 235   {                                                                     \
 236     const argmatch_##Name##_group_type *g = &argmatch_##Name##_group;   \
 237     ptrdiff_t res = argmatch_##Name##_choice (arg);                     \
 238     if (res < 0)                                                        \
 239       {                                                                 \
 240         argmatch_invalid (context, arg, res);                           \
 241         argmatch_##Name##_valid (stderr);                               \
 242         argmatch_die ();                                                \
 243       }                                                                 \
 244     return &g->args[res].val;                                           \
 245   }                                                                     \
 246                                                                         \
 247   /* The column in which the documentation is displayed.                \
 248      The leftmost possible, but no more than 20. */                     \
 249   static int                                                            \
 250   argmatch_##Name##_doc_col (void)                                      \
 251   {                                                                     \
 252     const argmatch_##Name##_group_type *g = &argmatch_##Name##_group;   \
 253     size_t size = argmatch_##Name##_size;                               \
 254     int res = 0;                                                        \
 255     for (int i = 0; g->docs[i].arg; ++i)                                \
 256       {                                                                 \
 257         int col = 4;                                                    \
 258         int ival = argmatch_##Name##_choice (g->docs[i].arg);           \
 259         if (ival < 0)                                                   \
 260           /* Pseudo argument, display it. */                            \
 261           col += strlen (g->docs[i].arg);                               \
 262         else                                                            \
 263           /* Genuine argument, display it with its synonyms. */         \
 264           for (int j = 0; g->args[j].arg; ++j)                          \
 265             if (! memcmp (&g->args[ival].val, &g->args[j].val, size))   \
 266               col += (col == 4 ? 0 : 2) + strlen (g->args[j].arg);      \
 267         if (res <= col)                                                 \
 268           res = col <= 20 ? col : 20;                                   \
 269       }                                                                 \
 270     return res ? res : 20;                                              \
 271   }                                                                     \
 272                                                                         \
 273   void                                                                  \
 274   argmatch_##Name##_usage (FILE *out)                                   \
 275   {                                                                     \
 276     const argmatch_##Name##_group_type *g = &argmatch_##Name##_group;   \
 277     size_t size = argmatch_##Name##_size;                               \
 278     /* Width of the screen.  Help2man does not seem to support          \
 279        arguments on several lines, so in that case pretend a very       \
 280        large width. */                                                  \
 281     const int screen_width = getenv ("HELP2MAN") ? INT_MAX : 80;        \
 282     if (g->doc_pre)                                                     \
 283       fprintf (out, "%s\n", gettext (g->doc_pre));                      \
 284     int doc_col = argmatch_##Name##_doc_col ();                         \
 285     for (int i = 0; g->docs[i].arg; ++i)                                \
 286       {                                                                 \
 287         int col = 0;                                                    \
 288         bool first = true;                                              \
 289         int ival = argmatch_##Name##_choice (g->docs[i].arg);           \
 290         if (ival < 0)                                                   \
 291           /* Pseudo argument, display it. */                            \
 292           col += fprintf (out,  "  %s", g->docs[i].arg);                \
 293         else                                                            \
 294           /* Genuine argument, display it with its synonyms. */         \
 295           for (int j = 0; g->args[j].arg; ++j)                          \
 296             if (! memcmp (&g->args[ival].val, &g->args[j].val, size))   \
 297               {                                                         \
 298                 if (!first                                              \
 299                     && screen_width < col + 2 + strlen (g->args[j].arg)) \
 300                   {                                                     \
 301                     fprintf (out, ",\n");                               \
 302                     col = 0;                                            \
 303                     first = true;                                       \
 304                   }                                                     \
 305                 if (first)                                              \
 306                   {                                                     \
 307                     col += fprintf (out, " ");                          \
 308                     first = false;                                      \
 309                   }                                                     \
 310                 else                                                    \
 311                   col += fprintf (out, ",");                            \
 312                 col += fprintf (out,  " %s", g->args[j].arg);           \
 313               }                                                         \
 314         /* The doc.  Separated by at least two spaces. */               \
 315         if (doc_col < col + 2)                                          \
 316           {                                                             \
 317             fprintf (out, "\n");                                        \
 318             col = 0;                                                    \
 319           }                                                             \
 320         fprintf (out, "%*s%s\n",                                        \
 321                  doc_col - col, "", gettext (g->docs[i].doc));          \
 322       }                                                                 \
 323     if (g->doc_post)                                                    \
 324       fprintf (out, "%s\n", gettext (g->doc_post));                     \
 325   }
 326 
 327 # ifdef  __cplusplus
 328 }
 329 # endif
 330 
 331 #endif /* ARGMATCH_H_ */

/* [previous][next][first][last][top][bottom][index][help] */