195b7b453SJohn Marino /* argmatch.h -- definitions and prototypes for argmatch.c 295b7b453SJohn Marino 3*09d4459fSDaniel Fojt Copyright (C) 1990, 1998-1999, 2001-2002, 2004-2005, 2009-2020 Free Software 4200fbe8dSJohn Marino Foundation, Inc. 595b7b453SJohn Marino 695b7b453SJohn Marino This program is free software: you can redistribute it and/or modify 795b7b453SJohn Marino it under the terms of the GNU General Public License as published by 895b7b453SJohn Marino the Free Software Foundation; either version 3 of the License, or 995b7b453SJohn Marino (at your option) any later version. 1095b7b453SJohn Marino 1195b7b453SJohn Marino This program is distributed in the hope that it will be useful, 1295b7b453SJohn Marino but WITHOUT ANY WARRANTY; without even the implied warranty of 1395b7b453SJohn Marino MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1495b7b453SJohn Marino GNU General Public License for more details. 1595b7b453SJohn Marino 1695b7b453SJohn Marino You should have received a copy of the GNU General Public License 17*09d4459fSDaniel Fojt along with this program. If not, see <https://www.gnu.org/licenses/>. */ 1895b7b453SJohn Marino 1995b7b453SJohn Marino /* Written by David MacKenzie <djm@ai.mit.edu> 2095b7b453SJohn Marino Modified by Akim Demaille <demaille@inf.enst.fr> */ 2195b7b453SJohn Marino 2295b7b453SJohn Marino #ifndef ARGMATCH_H_ 2395b7b453SJohn Marino # define ARGMATCH_H_ 1 2495b7b453SJohn Marino 25*09d4459fSDaniel Fojt # include <limits.h> 26*09d4459fSDaniel Fojt # include <stdbool.h> 2795b7b453SJohn Marino # include <stddef.h> 28*09d4459fSDaniel Fojt # include <stdio.h> 29*09d4459fSDaniel Fojt # include <string.h> /* memcmp */ 3095b7b453SJohn Marino 31*09d4459fSDaniel Fojt # include "gettext.h" 32*09d4459fSDaniel Fojt # include "quote.h" 3395b7b453SJohn Marino # include "verify.h" 3495b7b453SJohn Marino 35680a9cb8SJohn Marino # ifdef __cplusplus 36680a9cb8SJohn Marino extern "C" { 37680a9cb8SJohn Marino # endif 38680a9cb8SJohn Marino 3995b7b453SJohn Marino # define ARRAY_CARDINALITY(Array) (sizeof (Array) / sizeof *(Array)) 4095b7b453SJohn Marino 4195b7b453SJohn Marino /* Assert there are as many real arguments as there are values 4295b7b453SJohn Marino (argument list ends with a NULL guard). */ 4395b7b453SJohn Marino 4495b7b453SJohn Marino # define ARGMATCH_VERIFY(Arglist, Vallist) \ 4595b7b453SJohn Marino verify (ARRAY_CARDINALITY (Arglist) == ARRAY_CARDINALITY (Vallist) + 1) 4695b7b453SJohn Marino 4795b7b453SJohn Marino /* Return the index of the element of ARGLIST (NULL terminated) that 4895b7b453SJohn Marino matches with ARG. If VALLIST is not NULL, then use it to resolve 4995b7b453SJohn Marino false ambiguities (i.e., different matches of ARG but corresponding 5095b7b453SJohn Marino to the same values in VALLIST). */ 5195b7b453SJohn Marino 5295b7b453SJohn Marino ptrdiff_t argmatch (char const *arg, char const *const *arglist, 53*09d4459fSDaniel Fojt void const *vallist, size_t valsize) _GL_ATTRIBUTE_PURE; 5495b7b453SJohn Marino 5595b7b453SJohn Marino # define ARGMATCH(Arg, Arglist, Vallist) \ 56*09d4459fSDaniel Fojt argmatch (Arg, Arglist, (void const *) (Vallist), sizeof *(Vallist)) 5795b7b453SJohn Marino 5895b7b453SJohn Marino /* xargmatch calls this function when it fails. This function should not 5995b7b453SJohn Marino return. By default, this is a function that calls ARGMATCH_DIE which 60cf28ed85SJohn Marino in turn defaults to 'exit (exit_failure)'. */ 6195b7b453SJohn Marino typedef void (*argmatch_exit_fn) (void); 6295b7b453SJohn Marino extern argmatch_exit_fn argmatch_die; 6395b7b453SJohn Marino 6495b7b453SJohn Marino /* Report on stderr why argmatch failed. Report correct values. */ 6595b7b453SJohn Marino 6695b7b453SJohn Marino void argmatch_invalid (char const *context, char const *value, 6795b7b453SJohn Marino ptrdiff_t problem); 6895b7b453SJohn Marino 6995b7b453SJohn Marino /* Left for compatibility with the old name invalid_arg */ 7095b7b453SJohn Marino 7195b7b453SJohn Marino # define invalid_arg(Context, Value, Problem) \ 7295b7b453SJohn Marino argmatch_invalid (Context, Value, Problem) 7395b7b453SJohn Marino 7495b7b453SJohn Marino 7595b7b453SJohn Marino 7695b7b453SJohn Marino /* Report on stderr the list of possible arguments. */ 7795b7b453SJohn Marino 7895b7b453SJohn Marino void argmatch_valid (char const *const *arglist, 79*09d4459fSDaniel Fojt void const *vallist, size_t valsize); 8095b7b453SJohn Marino 8195b7b453SJohn Marino # define ARGMATCH_VALID(Arglist, Vallist) \ 82*09d4459fSDaniel Fojt argmatch_valid (Arglist, (void const *) (Vallist), sizeof *(Vallist)) 8395b7b453SJohn Marino 8495b7b453SJohn Marino 8595b7b453SJohn Marino 86200fbe8dSJohn Marino /* Same as argmatch, but upon failure, report an explanation of the 87200fbe8dSJohn Marino failure, and exit using the function EXIT_FN. */ 8895b7b453SJohn Marino 8995b7b453SJohn Marino ptrdiff_t __xargmatch_internal (char const *context, 9095b7b453SJohn Marino char const *arg, char const *const *arglist, 91*09d4459fSDaniel Fojt void const *vallist, size_t valsize, 9295b7b453SJohn Marino argmatch_exit_fn exit_fn); 9395b7b453SJohn Marino 9495b7b453SJohn Marino /* Programmer friendly interface to __xargmatch_internal. */ 9595b7b453SJohn Marino 9695b7b453SJohn Marino # define XARGMATCH(Context, Arg, Arglist, Vallist) \ 9795b7b453SJohn Marino ((Vallist) [__xargmatch_internal (Context, Arg, Arglist, \ 98*09d4459fSDaniel Fojt (void const *) (Vallist), \ 9995b7b453SJohn Marino sizeof *(Vallist), \ 10095b7b453SJohn Marino argmatch_die)]) 10195b7b453SJohn Marino 10295b7b453SJohn Marino /* Convert a value into a corresponding argument. */ 10395b7b453SJohn Marino 104*09d4459fSDaniel Fojt char const *argmatch_to_argument (void const *value, 10595b7b453SJohn Marino char const *const *arglist, 106*09d4459fSDaniel Fojt void const *vallist, size_t valsize) 107cf28ed85SJohn Marino _GL_ATTRIBUTE_PURE; 10895b7b453SJohn Marino 10995b7b453SJohn Marino # define ARGMATCH_TO_ARGUMENT(Value, Arglist, Vallist) \ 11095b7b453SJohn Marino argmatch_to_argument (Value, Arglist, \ 111*09d4459fSDaniel Fojt (void const *) (Vallist), sizeof *(Vallist)) 112*09d4459fSDaniel Fojt 113*09d4459fSDaniel Fojt # define ARGMATCH_DEFINE_GROUP(Name, Type) \ 114*09d4459fSDaniel Fojt /* The type of the values of this group. */ \ 115*09d4459fSDaniel Fojt typedef Type argmatch_##Name##_type; \ 116*09d4459fSDaniel Fojt \ 117*09d4459fSDaniel Fojt /* The size of the type of the values of this group. */ \ 118*09d4459fSDaniel Fojt enum argmatch_##Name##_size_enum \ 119*09d4459fSDaniel Fojt { \ 120*09d4459fSDaniel Fojt argmatch_##Name##_size = sizeof (argmatch_##Name##_type) \ 121*09d4459fSDaniel Fojt }; \ 122*09d4459fSDaniel Fojt \ 123*09d4459fSDaniel Fojt /* Argument mapping of this group. */ \ 124*09d4459fSDaniel Fojt typedef struct \ 125*09d4459fSDaniel Fojt { \ 126*09d4459fSDaniel Fojt /* Argument (e.g., "simple"). */ \ 127*09d4459fSDaniel Fojt const char *arg; \ 128*09d4459fSDaniel Fojt /* Value (e.g., simple_backups). */ \ 129*09d4459fSDaniel Fojt const argmatch_##Name##_type val; \ 130*09d4459fSDaniel Fojt } argmatch_##Name##_arg; \ 131*09d4459fSDaniel Fojt \ 132*09d4459fSDaniel Fojt /* Documentation of this group. */ \ 133*09d4459fSDaniel Fojt typedef struct \ 134*09d4459fSDaniel Fojt { \ 135*09d4459fSDaniel Fojt /* Argument (e.g., "simple"). */ \ 136*09d4459fSDaniel Fojt const char *arg; \ 137*09d4459fSDaniel Fojt /* Documentation (e.g., N_("always make simple backups")). */ \ 138*09d4459fSDaniel Fojt const char *doc; \ 139*09d4459fSDaniel Fojt } argmatch_##Name##_doc; \ 140*09d4459fSDaniel Fojt \ 141*09d4459fSDaniel Fojt /* All the features of an argmatch group. */ \ 142*09d4459fSDaniel Fojt typedef struct \ 143*09d4459fSDaniel Fojt { \ 144*09d4459fSDaniel Fojt const argmatch_##Name##_arg* args; \ 145*09d4459fSDaniel Fojt const argmatch_##Name##_doc* docs; \ 146*09d4459fSDaniel Fojt \ 147*09d4459fSDaniel Fojt /* Printed before the usage message. */ \ 148*09d4459fSDaniel Fojt const char *doc_pre; \ 149*09d4459fSDaniel Fojt /* Printed after the usage message. */ \ 150*09d4459fSDaniel Fojt const char *doc_post; \ 151*09d4459fSDaniel Fojt } argmatch_##Name##_group_type; \ 152*09d4459fSDaniel Fojt \ 153*09d4459fSDaniel Fojt /* The structure the user must build. */ \ 154*09d4459fSDaniel Fojt extern const argmatch_##Name##_group_type argmatch_##Name##_group; \ 155*09d4459fSDaniel Fojt \ 156*09d4459fSDaniel Fojt /* Print the documentation of this group. */ \ 157*09d4459fSDaniel Fojt void argmatch_##Name##_usage (FILE *out); \ 158*09d4459fSDaniel Fojt \ 159*09d4459fSDaniel Fojt /* If nonnegative, the index I of ARG in ARGS, i.e, \ 160*09d4459fSDaniel Fojt ARGS[I] == ARG. \ 161*09d4459fSDaniel Fojt Return -1 for invalid argument, -2 for ambiguous argument. */ \ 162*09d4459fSDaniel Fojt ptrdiff_t argmatch_##Name##_choice (const char *arg); \ 163*09d4459fSDaniel Fojt \ 164*09d4459fSDaniel Fojt /* A pointer to the corresponding value if it exists, or \ 165*09d4459fSDaniel Fojt report an error and exit with failure if the argument was \ 166*09d4459fSDaniel Fojt not recognized. */ \ 167*09d4459fSDaniel Fojt const argmatch_##Name##_type* \ 168*09d4459fSDaniel Fojt argmatch_##Name##_value (const char *context, const char *arg); \ 169*09d4459fSDaniel Fojt \ 170*09d4459fSDaniel Fojt /* The first argument in ARGS that matches this value, or NULL. */ \ 171*09d4459fSDaniel Fojt const char * \ 172*09d4459fSDaniel Fojt argmatch_##Name##_argument (const argmatch_##Name##_type *val); \ 173*09d4459fSDaniel Fojt \ 174*09d4459fSDaniel Fojt ptrdiff_t \ 175*09d4459fSDaniel Fojt argmatch_##Name##_choice (const char *arg) \ 176*09d4459fSDaniel Fojt { \ 177*09d4459fSDaniel Fojt const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \ 178*09d4459fSDaniel Fojt size_t size = argmatch_##Name##_size; \ 179*09d4459fSDaniel Fojt ptrdiff_t res = -1; /* Index of first nonexact match. */ \ 180*09d4459fSDaniel Fojt bool ambiguous = false; /* Whether multiple nonexact match(es). */ \ 181*09d4459fSDaniel Fojt size_t arglen = strlen (arg); \ 182*09d4459fSDaniel Fojt \ 183*09d4459fSDaniel Fojt /* Test all elements for either exact match or abbreviated \ 184*09d4459fSDaniel Fojt matches. */ \ 185*09d4459fSDaniel Fojt for (size_t i = 0; g->args[i].arg; i++) \ 186*09d4459fSDaniel Fojt if (!strncmp (g->args[i].arg, arg, arglen)) \ 187*09d4459fSDaniel Fojt { \ 188*09d4459fSDaniel Fojt if (strlen (g->args[i].arg) == arglen) \ 189*09d4459fSDaniel Fojt /* Exact match found. */ \ 190*09d4459fSDaniel Fojt return i; \ 191*09d4459fSDaniel Fojt else if (res == -1) \ 192*09d4459fSDaniel Fojt /* First nonexact match found. */ \ 193*09d4459fSDaniel Fojt res = i; \ 194*09d4459fSDaniel Fojt else if (memcmp (&g->args[res].val, &g->args[i].val, size)) \ 195*09d4459fSDaniel Fojt /* Second nonexact match found. */ \ 196*09d4459fSDaniel Fojt /* There is a real ambiguity, or we could not \ 197*09d4459fSDaniel Fojt disambiguate. */ \ 198*09d4459fSDaniel Fojt ambiguous = true; \ 199*09d4459fSDaniel Fojt } \ 200*09d4459fSDaniel Fojt return ambiguous ? -2 : res; \ 201*09d4459fSDaniel Fojt } \ 202*09d4459fSDaniel Fojt \ 203*09d4459fSDaniel Fojt const char * \ 204*09d4459fSDaniel Fojt argmatch_##Name##_argument (const argmatch_##Name##_type *val) \ 205*09d4459fSDaniel Fojt { \ 206*09d4459fSDaniel Fojt const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \ 207*09d4459fSDaniel Fojt size_t size = argmatch_##Name##_size; \ 208*09d4459fSDaniel Fojt for (size_t i = 0; g->args[i].arg; i++) \ 209*09d4459fSDaniel Fojt if (!memcmp (val, &g->args[i].val, size)) \ 210*09d4459fSDaniel Fojt return g->args[i].arg; \ 211*09d4459fSDaniel Fojt return NULL; \ 212*09d4459fSDaniel Fojt } \ 213*09d4459fSDaniel Fojt \ 214*09d4459fSDaniel Fojt /* List the valid values of this group. */ \ 215*09d4459fSDaniel Fojt static void \ 216*09d4459fSDaniel Fojt argmatch_##Name##_valid (FILE *out) \ 217*09d4459fSDaniel Fojt { \ 218*09d4459fSDaniel Fojt const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \ 219*09d4459fSDaniel Fojt size_t size = argmatch_##Name##_size; \ 220*09d4459fSDaniel Fojt \ 221*09d4459fSDaniel Fojt /* Try to put synonyms on the same line. Synonyms are expected \ 222*09d4459fSDaniel Fojt to follow each other. */ \ 223*09d4459fSDaniel Fojt fputs (gettext ("Valid arguments are:"), out); \ 224*09d4459fSDaniel Fojt for (int i = 0; g->args[i].arg; i++) \ 225*09d4459fSDaniel Fojt if (i == 0 \ 226*09d4459fSDaniel Fojt || memcmp (&g->args[i-1].val, &g->args[i].val, size)) \ 227*09d4459fSDaniel Fojt fprintf (out, "\n - %s", quote (g->args[i].arg)); \ 228*09d4459fSDaniel Fojt else \ 229*09d4459fSDaniel Fojt fprintf (out, ", %s", quote (g->args[i].arg)); \ 230*09d4459fSDaniel Fojt putc ('\n', out); \ 231*09d4459fSDaniel Fojt } \ 232*09d4459fSDaniel Fojt \ 233*09d4459fSDaniel Fojt const argmatch_##Name##_type* \ 234*09d4459fSDaniel Fojt argmatch_##Name##_value (const char *context, const char *arg) \ 235*09d4459fSDaniel Fojt { \ 236*09d4459fSDaniel Fojt const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \ 237*09d4459fSDaniel Fojt ptrdiff_t res = argmatch_##Name##_choice (arg); \ 238*09d4459fSDaniel Fojt if (res < 0) \ 239*09d4459fSDaniel Fojt { \ 240*09d4459fSDaniel Fojt argmatch_invalid (context, arg, res); \ 241*09d4459fSDaniel Fojt argmatch_##Name##_valid (stderr); \ 242*09d4459fSDaniel Fojt argmatch_die (); \ 243*09d4459fSDaniel Fojt } \ 244*09d4459fSDaniel Fojt return &g->args[res].val; \ 245*09d4459fSDaniel Fojt } \ 246*09d4459fSDaniel Fojt \ 247*09d4459fSDaniel Fojt /* The column in which the documentation is displayed. \ 248*09d4459fSDaniel Fojt The leftmost possible, but no more than 20. */ \ 249*09d4459fSDaniel Fojt static int \ 250*09d4459fSDaniel Fojt argmatch_##Name##_doc_col (void) \ 251*09d4459fSDaniel Fojt { \ 252*09d4459fSDaniel Fojt const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \ 253*09d4459fSDaniel Fojt size_t size = argmatch_##Name##_size; \ 254*09d4459fSDaniel Fojt int res = 0; \ 255*09d4459fSDaniel Fojt for (int i = 0; g->docs[i].arg; ++i) \ 256*09d4459fSDaniel Fojt { \ 257*09d4459fSDaniel Fojt int col = 4; \ 258*09d4459fSDaniel Fojt int ival = argmatch_##Name##_choice (g->docs[i].arg); \ 259*09d4459fSDaniel Fojt if (ival < 0) \ 260*09d4459fSDaniel Fojt /* Pseudo argument, display it. */ \ 261*09d4459fSDaniel Fojt col += strlen (g->docs[i].arg); \ 262*09d4459fSDaniel Fojt else \ 263*09d4459fSDaniel Fojt /* Genuine argument, display it with its synonyms. */ \ 264*09d4459fSDaniel Fojt for (int j = 0; g->args[j].arg; ++j) \ 265*09d4459fSDaniel Fojt if (! memcmp (&g->args[ival].val, &g->args[j].val, size)) \ 266*09d4459fSDaniel Fojt col += (col == 4 ? 0 : 2) + strlen (g->args[j].arg); \ 267*09d4459fSDaniel Fojt if (res <= col) \ 268*09d4459fSDaniel Fojt res = col <= 20 ? col : 20; \ 269*09d4459fSDaniel Fojt } \ 270*09d4459fSDaniel Fojt return res ? res : 20; \ 271*09d4459fSDaniel Fojt } \ 272*09d4459fSDaniel Fojt \ 273*09d4459fSDaniel Fojt void \ 274*09d4459fSDaniel Fojt argmatch_##Name##_usage (FILE *out) \ 275*09d4459fSDaniel Fojt { \ 276*09d4459fSDaniel Fojt const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \ 277*09d4459fSDaniel Fojt size_t size = argmatch_##Name##_size; \ 278*09d4459fSDaniel Fojt /* Width of the screen. Help2man does not seem to support \ 279*09d4459fSDaniel Fojt arguments on several lines, so in that case pretend a very \ 280*09d4459fSDaniel Fojt large width. */ \ 281*09d4459fSDaniel Fojt const int screen_width = getenv ("HELP2MAN") ? INT_MAX : 80; \ 282*09d4459fSDaniel Fojt if (g->doc_pre) \ 283*09d4459fSDaniel Fojt fprintf (out, "%s\n", gettext (g->doc_pre)); \ 284*09d4459fSDaniel Fojt int doc_col = argmatch_##Name##_doc_col (); \ 285*09d4459fSDaniel Fojt for (int i = 0; g->docs[i].arg; ++i) \ 286*09d4459fSDaniel Fojt { \ 287*09d4459fSDaniel Fojt int col = 0; \ 288*09d4459fSDaniel Fojt bool first = true; \ 289*09d4459fSDaniel Fojt int ival = argmatch_##Name##_choice (g->docs[i].arg); \ 290*09d4459fSDaniel Fojt if (ival < 0) \ 291*09d4459fSDaniel Fojt /* Pseudo argument, display it. */ \ 292*09d4459fSDaniel Fojt col += fprintf (out, " %s", g->docs[i].arg); \ 293*09d4459fSDaniel Fojt else \ 294*09d4459fSDaniel Fojt /* Genuine argument, display it with its synonyms. */ \ 295*09d4459fSDaniel Fojt for (int j = 0; g->args[j].arg; ++j) \ 296*09d4459fSDaniel Fojt if (! memcmp (&g->args[ival].val, &g->args[j].val, size)) \ 297*09d4459fSDaniel Fojt { \ 298*09d4459fSDaniel Fojt if (!first \ 299*09d4459fSDaniel Fojt && screen_width < col + 2 + strlen (g->args[j].arg)) \ 300*09d4459fSDaniel Fojt { \ 301*09d4459fSDaniel Fojt fprintf (out, ",\n"); \ 302*09d4459fSDaniel Fojt col = 0; \ 303*09d4459fSDaniel Fojt first = true; \ 304*09d4459fSDaniel Fojt } \ 305*09d4459fSDaniel Fojt if (first) \ 306*09d4459fSDaniel Fojt { \ 307*09d4459fSDaniel Fojt col += fprintf (out, " "); \ 308*09d4459fSDaniel Fojt first = false; \ 309*09d4459fSDaniel Fojt } \ 310*09d4459fSDaniel Fojt else \ 311*09d4459fSDaniel Fojt col += fprintf (out, ","); \ 312*09d4459fSDaniel Fojt col += fprintf (out, " %s", g->args[j].arg); \ 313*09d4459fSDaniel Fojt } \ 314*09d4459fSDaniel Fojt /* The doc. Separated by at least two spaces. */ \ 315*09d4459fSDaniel Fojt if (doc_col < col + 2) \ 316*09d4459fSDaniel Fojt { \ 317*09d4459fSDaniel Fojt fprintf (out, "\n"); \ 318*09d4459fSDaniel Fojt col = 0; \ 319*09d4459fSDaniel Fojt } \ 320*09d4459fSDaniel Fojt fprintf (out, "%*s%s\n", \ 321*09d4459fSDaniel Fojt doc_col - col, "", gettext (g->docs[i].doc)); \ 322*09d4459fSDaniel Fojt } \ 323*09d4459fSDaniel Fojt if (g->doc_post) \ 324*09d4459fSDaniel Fojt fprintf (out, "%s\n", gettext (g->doc_post)); \ 325*09d4459fSDaniel Fojt } 32695b7b453SJohn Marino 327680a9cb8SJohn Marino # ifdef __cplusplus 328680a9cb8SJohn Marino } 329680a9cb8SJohn Marino # endif 330680a9cb8SJohn Marino 33195b7b453SJohn Marino #endif /* ARGMATCH_H_ */ 332