(root)/
findutils-4.9.0/
gl/
lib/
argmatch.h
       1  /* argmatch.h -- definitions and prototypes for argmatch.c
       2  
       3     Copyright (C) 1990, 1998-1999, 2001-2002, 2004-2005, 2009-2022 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  ptrdiff_t argmatch_exact (char const *arg, char const *const *arglist)
      56    _GL_ATTRIBUTE_PURE;
      57  
      58  # define ARGMATCH(Arg, Arglist, Vallist) \
      59    argmatch (Arg, Arglist, (void const *) (Vallist), sizeof *(Vallist))
      60  
      61  # define ARGMATCH_EXACT(Arg, Arglist) \
      62    argmatch_exact (Arg, Arglist)
      63  
      64  /* xargmatch calls this function when it fails.  This function should not
      65     return.  By default, this is a function that calls ARGMATCH_DIE which
      66     in turn defaults to 'exit (exit_failure)'.  */
      67  typedef void (*argmatch_exit_fn) (void);
      68  extern argmatch_exit_fn argmatch_die;
      69  
      70  /* Report on stderr why argmatch failed.  Report correct values. */
      71  
      72  void argmatch_invalid (char const *context, char const *value,
      73                         ptrdiff_t problem);
      74  
      75  /* Left for compatibility with the old name invalid_arg */
      76  
      77  # define invalid_arg(Context, Value, Problem) \
      78    argmatch_invalid (Context, Value, Problem)
      79  
      80  
      81  
      82  /* Report on stderr the list of possible arguments.  */
      83  
      84  void argmatch_valid (char const *const *arglist,
      85                       void const *vallist, size_t valsize);
      86  
      87  # define ARGMATCH_VALID(Arglist, Vallist) \
      88    argmatch_valid (Arglist, (void const *) (Vallist), sizeof *(Vallist))
      89  
      90  
      91  
      92  /* Like argmatch/argmatch_exact, but upon failure, report an explanation
      93     of the failure, and exit using the function EXIT_FN. */
      94  
      95  ptrdiff_t __xargmatch_internal (char const *context,
      96                                  char const *arg, char const *const *arglist,
      97                                  void const *vallist, size_t valsize,
      98                                  argmatch_exit_fn exit_fn,
      99                                  bool allow_abbreviation);
     100  
     101  /* Programmer friendly interface to __xargmatch_internal. */
     102  
     103  # define XARGMATCH(Context, Arg, Arglist, Vallist)              \
     104    ((Vallist) [__xargmatch_internal (Context, Arg, Arglist,      \
     105                                      (void const *) (Vallist),   \
     106                                      sizeof *(Vallist),          \
     107                                      argmatch_die,               \
     108                                      true)])
     109  
     110  # define XARGMATCH_EXACT(Context, Arg, Arglist, Vallist)        \
     111    ((Vallist) [__xargmatch_internal (Context, Arg, Arglist,      \
     112                                      (void const *) (Vallist),   \
     113                                      sizeof *(Vallist),          \
     114                                      argmatch_die,               \
     115                                      false)])
     116  
     117  /* Convert a value into a corresponding argument. */
     118  
     119  char const *argmatch_to_argument (void const *value,
     120                                    char const *const *arglist,
     121                                    void const *vallist, size_t valsize)
     122    _GL_ATTRIBUTE_PURE;
     123  
     124  # define ARGMATCH_TO_ARGUMENT(Value, Arglist, Vallist)                  \
     125    argmatch_to_argument (Value, Arglist,                                 \
     126                          (void const *) (Vallist), sizeof *(Vallist))
     127  
     128  # define ARGMATCH_DEFINE_GROUP(Name, Type)                              \
     129    /* The type of the values of this group.  */                          \
     130    typedef Type argmatch_##Name##_type;                                  \
     131                                                                          \
     132    /* The size of the type of the values of this group. */               \
     133    enum argmatch_##Name##_size_enum                                      \
     134    {                                                                     \
     135      argmatch_##Name##_size = sizeof (argmatch_##Name##_type)            \
     136    };                                                                    \
     137                                                                          \
     138    /* Argument mapping of this group.  */                                \
     139    typedef struct                                                        \
     140    {                                                                     \
     141      /* Argument (e.g., "simple").  */                                   \
     142      const char *arg;                                                    \
     143      /* Value (e.g., simple_backups).  */                                \
     144      const argmatch_##Name##_type val;                                   \
     145    } argmatch_##Name##_arg;                                              \
     146                                                                          \
     147    /* Documentation of this group.  */                                   \
     148    typedef struct                                                        \
     149    {                                                                     \
     150      /* Argument (e.g., "simple").  */                                   \
     151      const char *arg;                                                    \
     152      /* Documentation (e.g., N_("always make simple backups")).  */      \
     153      const char *doc;                                                    \
     154    } argmatch_##Name##_doc;                                              \
     155                                                                          \
     156    /* All the features of an argmatch group.  */                         \
     157    typedef struct                                                        \
     158    {                                                                     \
     159      const argmatch_##Name##_arg* args;                                  \
     160      const argmatch_##Name##_doc* docs;                                  \
     161                                                                          \
     162      /* Printed before the usage message.  */                            \
     163      const char *doc_pre;                                                \
     164      /* Printed after the usage message.  */                             \
     165      const char *doc_post;                                               \
     166    } argmatch_##Name##_group_type;                                       \
     167                                                                          \
     168    /* The structure the user must build.  */                             \
     169    extern const argmatch_##Name##_group_type argmatch_##Name##_group;    \
     170                                                                          \
     171    /* Print the documentation of this group.  */                         \
     172    void argmatch_##Name##_usage (FILE *out);                             \
     173                                                                          \
     174    /* If nonnegative, the index I of ARG in ARGS, i.e,                   \
     175       ARGS[I] == ARG.                                                    \
     176       Return -1 for invalid argument, -2 for ambiguous argument. */      \
     177    ptrdiff_t argmatch_##Name##_choice (const char *arg);                 \
     178                                                                          \
     179    /* A pointer to the corresponding value if it exists, or              \
     180       report an error and exit with failure if the argument was          \
     181       not recognized. */                                                 \
     182    const argmatch_##Name##_type*                                         \
     183    argmatch_##Name##_value (const char *context, const char *arg);       \
     184                                                                          \
     185    /* The first argument in ARGS that matches this value, or NULL.  */   \
     186    const char *                                                          \
     187    argmatch_##Name##_argument (const argmatch_##Name##_type *val);       \
     188                                                                          \
     189    ptrdiff_t                                                             \
     190    argmatch_##Name##_choice (const char *arg)                            \
     191    {                                                                     \
     192      const argmatch_##Name##_group_type *g = &argmatch_##Name##_group;   \
     193      size_t size = argmatch_##Name##_size;                               \
     194      ptrdiff_t res = -1;      /* Index of first nonexact match.  */      \
     195      bool ambiguous = false;  /* Whether multiple nonexact match(es). */ \
     196      size_t arglen = strlen (arg);                                       \
     197                                                                          \
     198      /* Test all elements for either exact match or abbreviated          \
     199         matches.  */                                                     \
     200      for (size_t i = 0; g->args[i].arg; i++)                             \
     201        if (!strncmp (g->args[i].arg, arg, arglen))                       \
     202          {                                                               \
     203            if (strlen (g->args[i].arg) == arglen)                        \
     204              /* Exact match found.  */                                   \
     205              return i;                                                   \
     206            else if (res == -1)                                           \
     207              /* First nonexact match found.  */                          \
     208              res = i;                                                    \
     209            else if (memcmp (&g->args[res].val, &g->args[i].val, size))   \
     210              /* Second nonexact match found.  */                         \
     211              /* There is a real ambiguity, or we could not               \
     212                 disambiguate. */                                         \
     213              ambiguous = true;                                           \
     214          }                                                               \
     215      return ambiguous ? -2 : res;                                        \
     216    }                                                                     \
     217                                                                          \
     218    const char *                                                          \
     219    argmatch_##Name##_argument (const argmatch_##Name##_type *val)        \
     220    {                                                                     \
     221      const argmatch_##Name##_group_type *g = &argmatch_##Name##_group;   \
     222      size_t size = argmatch_##Name##_size;                               \
     223      for (size_t i = 0; g->args[i].arg; i++)                             \
     224        if (!memcmp (val, &g->args[i].val, size))                         \
     225          return g->args[i].arg;                                          \
     226      return NULL;                                                        \
     227    }                                                                     \
     228                                                                          \
     229    /* List the valid values of this group. */                            \
     230    static void                                                           \
     231    argmatch_##Name##_valid (FILE *out)                                   \
     232    {                                                                     \
     233      const argmatch_##Name##_group_type *g = &argmatch_##Name##_group;   \
     234      size_t size = argmatch_##Name##_size;                               \
     235                                                                          \
     236      /* Try to put synonyms on the same line.  Synonyms are expected     \
     237         to follow each other. */                                         \
     238      fputs (gettext ("Valid arguments are:"), out);                      \
     239      for (int i = 0; g->args[i].arg; i++)                                \
     240        if (i == 0                                                        \
     241            || memcmp (&g->args[i-1].val, &g->args[i].val, size))         \
     242          fprintf (out, "\n  - %s", quote (g->args[i].arg));              \
     243        else                                                              \
     244          fprintf (out, ", %s", quote (g->args[i].arg));                  \
     245      putc ('\n', out);                                                   \
     246    }                                                                     \
     247                                                                          \
     248    const argmatch_##Name##_type*                                         \
     249    argmatch_##Name##_value (const char *context, const char *arg)        \
     250    {                                                                     \
     251      const argmatch_##Name##_group_type *g = &argmatch_##Name##_group;   \
     252      ptrdiff_t res = argmatch_##Name##_choice (arg);                     \
     253      if (res < 0)                                                        \
     254        {                                                                 \
     255          argmatch_invalid (context, arg, res);                           \
     256          argmatch_##Name##_valid (stderr);                               \
     257          argmatch_die ();                                                \
     258        }                                                                 \
     259      return &g->args[res].val;                                           \
     260    }                                                                     \
     261                                                                          \
     262    /* The column in which the documentation is displayed.                \
     263       The leftmost possible, but no more than 20. */                     \
     264    static int                                                            \
     265    argmatch_##Name##_doc_col (void)                                      \
     266    {                                                                     \
     267      const argmatch_##Name##_group_type *g = &argmatch_##Name##_group;   \
     268      size_t size = argmatch_##Name##_size;                               \
     269      int res = 0;                                                        \
     270      for (int i = 0; g->docs[i].arg; ++i)                                \
     271        {                                                                 \
     272          int col = 4;                                                    \
     273          int ival = argmatch_##Name##_choice (g->docs[i].arg);           \
     274          if (ival < 0)                                                   \
     275            /* Pseudo argument, display it. */                            \
     276            col += strlen (g->docs[i].arg);                               \
     277          else                                                            \
     278            /* Genuine argument, display it with its synonyms. */         \
     279            for (int j = 0; g->args[j].arg; ++j)                          \
     280              if (! memcmp (&g->args[ival].val, &g->args[j].val, size))   \
     281                col += (col == 4 ? 0 : 2) + strlen (g->args[j].arg);      \
     282          if (res <= col)                                                 \
     283            res = col <= 20 ? col : 20;                                   \
     284        }                                                                 \
     285      return res ? res : 20;                                              \
     286    }                                                                     \
     287                                                                          \
     288    void                                                                  \
     289    argmatch_##Name##_usage (FILE *out)                                   \
     290    {                                                                     \
     291      const argmatch_##Name##_group_type *g = &argmatch_##Name##_group;   \
     292      size_t size = argmatch_##Name##_size;                               \
     293      /* Width of the screen.  Help2man does not seem to support          \
     294         arguments on several lines, so in that case pretend a very       \
     295         large width. */                                                  \
     296      const int screen_width = getenv ("HELP2MAN") ? INT_MAX : 80;        \
     297      if (g->doc_pre)                                                     \
     298        fprintf (out, "%s\n", gettext (g->doc_pre));                      \
     299      int doc_col = argmatch_##Name##_doc_col ();                         \
     300      for (int i = 0; g->docs[i].arg; ++i)                                \
     301        {                                                                 \
     302          int col = 0;                                                    \
     303          bool first = true;                                              \
     304          int ival = argmatch_##Name##_choice (g->docs[i].arg);           \
     305          if (ival < 0)                                                   \
     306            /* Pseudo argument, display it. */                            \
     307            col += fprintf (out,  "  %s", g->docs[i].arg);                \
     308          else                                                            \
     309            /* Genuine argument, display it with its synonyms. */         \
     310            for (int j = 0; g->args[j].arg; ++j)                          \
     311              if (! memcmp (&g->args[ival].val, &g->args[j].val, size))   \
     312                {                                                         \
     313                  if (!first                                              \
     314                      && screen_width < col + 2 + strlen (g->args[j].arg)) \
     315                    {                                                     \
     316                      fprintf (out, ",\n");                               \
     317                      col = 0;                                            \
     318                      first = true;                                       \
     319                    }                                                     \
     320                  if (first)                                              \
     321                    {                                                     \
     322                      col += fprintf (out, " ");                          \
     323                      first = false;                                      \
     324                    }                                                     \
     325                  else                                                    \
     326                    col += fprintf (out, ",");                            \
     327                  col += fprintf (out,  " %s", g->args[j].arg);           \
     328                }                                                         \
     329          /* The doc.  Separated by at least two spaces. */               \
     330          if (doc_col < col + 2)                                          \
     331            {                                                             \
     332              fprintf (out, "\n");                                        \
     333              col = 0;                                                    \
     334            }                                                             \
     335          fprintf (out, "%*s%s\n",                                        \
     336                   doc_col - col, "", gettext (g->docs[i].doc));          \
     337        }                                                                 \
     338      if (g->doc_post)                                                    \
     339        fprintf (out, "%s\n", gettext (g->doc_post));                     \
     340    }
     341  
     342  # ifdef  __cplusplus
     343  }
     344  # endif
     345  
     346  #endif /* ARGMATCH_H_ */