xref: /dflybsd-src/contrib/grep/lib/argmatch.h (revision 91b9ed38d3db6a8a8ac5b66da1d43e6e331e259a)
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