(root)/
bison-3.8.2/
lib/
argmatch.h
       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_ */