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