libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

options-generator.c (33972B)


      1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */
      2 /*
      3   This file is part of GNU libmicrohttpd.
      4   Copyright (C) 2024 Christian Grothoff (and other contributing authors)
      5 
      6   GNU libmicrohttpd is free software; you can redistribute it and/or
      7   modify it under the terms of the GNU Lesser General Public
      8   License as published by the Free Software Foundation; either
      9   version 2.1 of the License, or (at your option) any later version.
     10 
     11   GNU libmicrohttpd 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 GNU
     14   Lesser General Public License for more details.
     15 
     16   Alternatively, you can redistribute GNU libmicrohttpd and/or
     17   modify it under the terms of the GNU General Public License as
     18   published by the Free Software Foundation; either version 2 of
     19   the License, or (at your option) any later version, together
     20   with the eCos exception, as follows:
     21 
     22     As a special exception, if other files instantiate templates or
     23     use macros or inline functions from this file, or you compile this
     24     file and link it with other works to produce a work based on this
     25     file, this file does not by itself cause the resulting work to be
     26     covered by the GNU General Public License. However the source code
     27     for this file must still be made available in accordance with
     28     section (3) of the GNU General Public License v2.
     29 
     30     This exception does not invalidate any other reasons why a work
     31     based on this file might be covered by the GNU General Public
     32     License.
     33 
     34   You should have received copies of the GNU Lesser General Public
     35   License and the GNU General Public License along with this library;
     36   if not, see <https://www.gnu.org/licenses/>.
     37 */
     38 /**
     39  * @file options-generator.c
     40  * @brief Generates code based on Recutils database
     41  * @author Christian Grothoff
     42  */
     43 #include <stdio.h>
     44 #include <stdlib.h>
     45 #include <stdarg.h>
     46 #include <string.h>
     47 #include <ctype.h>
     48 #if !defined(_WIN32) || defined(__CYGWIN__)
     49 #  include <unistd.h>
     50 #endif
     51 #include <sys/stat.h>
     52 #include <errno.h>
     53 
     54 #define MAX_ARGS 3
     55 
     56 struct Option
     57 {
     58   struct Option *next;
     59   char *name;
     60   unsigned int value;
     61   char *type;
     62   char *comment;
     63   char *custom_setter;
     64   unsigned int argc;
     65   char *arguments[MAX_ARGS];
     66   unsigned int desc;
     67   char *descriptions[MAX_ARGS];
     68   char *conditional;
     69 };
     70 
     71 static FILE *f;
     72 
     73 static char *category;
     74 
     75 typedef void
     76 (*Callback)(const char *name,
     77             unsigned int value,
     78             const char *comment,
     79             const char *type,
     80             const char *conditional,
     81             const char *custom_setter,
     82             unsigned int argc,
     83             char **arguments,
     84             unsigned int desc,
     85             char **descriptions);
     86 
     87 
     88 static int
     89 my_asprintf (char **buf,
     90              const char *format,
     91              ...)
     92 {
     93   int ret;
     94   va_list args;
     95 
     96   va_start (args,
     97             format);
     98   ret = vsnprintf (NULL,
     99                    0,
    100                    format,
    101                    args);
    102   va_end (args);
    103   *buf = (char *)malloc (ret + 1);
    104   va_start (args,
    105             format);
    106   ret = vsprintf (*buf,
    107                   format,
    108                   args);
    109   va_end (args);
    110   return ret;
    111 }
    112 
    113 
    114 static void
    115 iterate (struct Option *head,
    116          Callback cb)
    117 {
    118   for (struct Option *o = head; NULL != o; o = o->next)
    119   {
    120     if (0 == strcmp ("end",
    121                      o->name))
    122       continue;
    123     cb (o->name,
    124         o->value,
    125         o->comment,
    126         o->type,
    127         o->conditional,
    128         o->custom_setter,
    129         o->argc,
    130         o->arguments,
    131         o->desc,
    132         o->descriptions);
    133   }
    134 }
    135 
    136 
    137 static void
    138 check (const char *name,
    139        unsigned int value,
    140        const char *comment,
    141        const char *type,
    142        const char *conditional,
    143        const char *custom_setter,
    144        unsigned int argc,
    145        char **arguments,
    146        unsigned int desc,
    147        char **descriptions)
    148 {
    149   if (argc != desc)
    150   {
    151     fprintf (stderr,
    152              "Mismatch between descriptions and arguments for `%s'\n",
    153              name);
    154     exit (2);
    155   }
    156   if ((NULL == type)
    157       && ((0 == argc)
    158           || (1 != argc)))
    159   {
    160     fprintf (stderr,
    161              "Type and argument missing for `%s' and not exactly 1 argument\n",
    162              name);
    163     exit (2);
    164   }
    165   for (unsigned int i = 0; i < argc; i++)
    166   {
    167     const char *arg = arguments[i];
    168 
    169     if (NULL == (strrchr (arg, ' ')))
    170     {
    171       fprintf (stderr,
    172                "Mandatory space missing in argument%u of `%s'\n",
    173                i,
    174                name);
    175       exit (2);
    176     }
    177   }
    178   if (NULL != strchr (name, ' '))
    179   {
    180     fprintf (stderr,
    181              "Spaces are not allowed in names, found one in `%s'\n",
    182              name);
    183     exit (2);
    184   }
    185 }
    186 
    187 
    188 static char *
    189 indent (const char *pfx,
    190         const char *input)
    191 {
    192   char *ret = strdup (input);
    193   char *xfx = strdup (pfx);
    194   char *off;
    195   size_t pos = 0;
    196 
    197   while ((strlen (xfx) > 0)
    198          && (isspace (xfx[strlen (xfx) - 1])))
    199     xfx[strlen (xfx) - 1] = '\0';
    200   while (NULL != (off = strchr (ret + pos, '\n')))
    201   {
    202     char *tmp;
    203 
    204     my_asprintf (&tmp,
    205                  "%.*s\n%s%s",
    206                  (int)(off - ret),
    207                  ret,
    208                  (off[1] == '\n')
    209               ? xfx
    210               : pfx,
    211                  off + 1);
    212     pos = (off - ret) + strlen (pfx) + 1;
    213     free (ret);
    214     ret = tmp;
    215   }
    216   return ret;
    217 }
    218 
    219 
    220 static char *
    221 uppercase (const char *input)
    222 {
    223   char *ret = strdup (input);
    224 
    225   for (size_t i = 0; '\0' != ret[i]; i++)
    226     ret[i] = toupper (ret[i]);
    227   return ret;
    228 }
    229 
    230 
    231 static char *
    232 capitalize (const char *input)
    233 {
    234   char *ret = strdup (input);
    235 
    236   ret[0] = toupper (ret[0]);
    237   return ret;
    238 }
    239 
    240 
    241 static char *
    242 lowercase (const char *input)
    243 {
    244   char *ret = strdup (input);
    245 
    246   for (size_t i = 0; '\0' != ret[i]; i++)
    247     ret[i] = tolower (ret[i]);
    248   return ret;
    249 }
    250 
    251 
    252 static void
    253 dump_enum (const char *name,
    254            unsigned int value,
    255            const char *comment,
    256            const char *type,
    257            const char *conditional,
    258            const char *custom_setter,
    259            unsigned int argc,
    260            char **arguments,
    261            unsigned int desc,
    262            char **descriptions)
    263 {
    264   printf ("  /**\n   * %s\n   */\n  MHD_%c_O_%s = %u\n  ,\n\n",
    265           indent ("   * ", comment),
    266           (char)toupper (*category),
    267           uppercase (name),
    268           value);
    269 }
    270 
    271 
    272 static const char *
    273 var_name (const char *arg)
    274 {
    275   const char *space;
    276 
    277   space = strrchr (arg, ' ');
    278   while ('*' == space[1])
    279     space++;
    280   return space + 1;
    281 }
    282 
    283 
    284 static void
    285 dump_union_members (const char *name,
    286                     unsigned int value,
    287                     const char *comment,
    288                     const char *type,
    289                     const char *conditional,
    290                     const char *custom_setter,
    291                     unsigned int argc,
    292                     char **arguments,
    293                     unsigned int desct,
    294                     char **descriptions)
    295 {
    296   if (NULL == type)
    297     return;
    298   if (1 >= argc)
    299     return;
    300 
    301   printf ("/**\n * Data for #MHD_%c_O_%s\n */\n%s\n{\n",
    302           (char)toupper (*category),
    303           uppercase (name),
    304           type);
    305   for (unsigned int i = 0; i < argc; i++)
    306   {
    307     const char *arg = arguments[i];
    308     const char *desc = descriptions[i];
    309     const char *vn = var_name (arg);
    310 
    311     printf ("  /**\n   * %s\n   */\n  %.*sv_%s;\n\n",
    312             indent ("   * ",
    313                     desc),
    314             (int)(vn - arg),
    315             arg,
    316             vn);
    317   }
    318   printf ("};\n\n");
    319 }
    320 
    321 
    322 static void
    323 dump_union (const char *name,
    324             unsigned int value,
    325             const char *comment,
    326             const char *type,
    327             const char *conditional,
    328             const char *custom_setter,
    329             unsigned int argc,
    330             char **arguments,
    331             unsigned int desc,
    332             char **descriptions)
    333 {
    334   const char *xcomment = xcomment = descriptions[0];
    335 
    336   fprintf (f,
    337            "  /**\n"
    338            "   * Value for #MHD_%c_O_%s.%s%s\n"
    339            "   */\n",
    340            (char)toupper (*category),
    341            uppercase (name),
    342            NULL != xcomment
    343           ? "\n   * "
    344           : "",
    345            NULL != xcomment
    346           ? indent ("   * ", xcomment)
    347           : "");
    348   if (NULL != type)
    349   {
    350     fprintf (f,
    351              "  %s %s;\n",
    352              type,
    353              lowercase (name));
    354   }
    355   else
    356   {
    357     const char *arg = arguments[0];
    358     const char *vn = var_name (arg);
    359 
    360     fprintf (f,
    361              "  %.*s%s;\n",
    362              (int)(vn - arg),
    363              arg,
    364              lowercase (name));
    365   }
    366   fprintf (f,
    367            "\n");
    368 }
    369 
    370 
    371 static void
    372 dump_struct (const char *name,
    373              unsigned int value,
    374              const char *comment,
    375              const char *type,
    376              const char *conditional,
    377              const char *custom_setter,
    378              unsigned int argc,
    379              char **arguments,
    380              unsigned int desc,
    381              char **descriptions)
    382 {
    383   if (NULL != conditional)
    384     fprintf (f,
    385              "#ifdef HAVE_%s",
    386              uppercase (conditional));
    387   dump_union (name,
    388               value,
    389               comment,
    390               type,
    391               conditional,
    392               custom_setter,
    393               argc,
    394               arguments,
    395               desc,
    396               descriptions);
    397   if (NULL != conditional)
    398     fprintf (f,
    399              "#endif\n");
    400   fprintf (f,
    401            "\n");
    402 }
    403 
    404 
    405 static void
    406 dump_option_macros (const char *name,
    407                     unsigned int value,
    408                     const char *comment,
    409                     const char *type,
    410                     const char *conditional,
    411                     const char *custom_setter,
    412                     unsigned int argc,
    413                     char **arguments,
    414                     unsigned int desct,
    415                     char **descriptions)
    416 {
    417   printf (
    418     "/**\n * %s\n",
    419     indent (" * ", comment));
    420   for (unsigned int off = 0; off < desct; off++)
    421   {
    422     const char *arg = arguments[off];
    423     const char *desc = descriptions[off];
    424     const char *vn = var_name (arg);
    425 
    426     printf (" * @param %s %s\n",
    427             vn,
    428             indent (" *   ",
    429                     desc));
    430   }
    431   if (0 == desct)
    432     printf (" * @param value the value of the parameter");
    433   printf (" * @return structure with the requested setting\n */\n");
    434   printf ("#  define MHD_%c_OPTION_%s(",
    435           (char)toupper (*category),
    436           uppercase (name));
    437   if (0 == argc)
    438     printf ("value");
    439   else
    440     for (unsigned int i = 0; i < argc; i++)
    441     {
    442       if (0 != i)
    443         printf (", ");
    444       printf ("%s",
    445               var_name (arguments[i]));
    446     }
    447   printf (") \\\n"
    448           "        MHD_NOWARN_COMPOUND_LITERALS_ " \
    449           "MHD_NOWARN_AGGR_DYN_INIT_ \\\n"
    450           "          (const struct MHD_%sOptionAndValue) \\\n"
    451           "        { \\\n"
    452           "          .opt = MHD_%c_O_%s,  \\\n",
    453           capitalize (category),
    454           (char)toupper (*category),
    455           uppercase (name));
    456   if (0 == argc)
    457     printf ("          .val.%s = (value) \\\n",
    458             lowercase (name));
    459   else
    460     for (unsigned int i = 0; i < argc; i++)
    461     {
    462       const char *vn = var_name (arguments[i]);
    463 
    464       if (1 < argc)
    465         printf ("          .val.%s.v_%s = (%s)%s \\\n",
    466                 lowercase (name),
    467                 vn,
    468                 vn,
    469                 (i < argc - 1)
    470               ? ","
    471               : "");
    472       else
    473         printf ("          .val.%s = (%s)%s \\\n",
    474                 lowercase (name),
    475                 vn,
    476                 (i < argc - 1)
    477                 ? ","
    478                 : "");
    479     }
    480 
    481   printf ("        } \\\n"
    482           "        MHD_RESTORE_WARN_COMPOUND_LITERALS_ "
    483           "MHD_RESTORE_WARN_AGGR_DYN_INIT_\n");
    484 }
    485 
    486 
    487 static void
    488 dump_option_static_functions (const char *name,
    489                               unsigned int value,
    490                               const char *comment,
    491                               const char *type,
    492                               const char *conditional,
    493                               const char *custom_setter,
    494                               unsigned int argc,
    495                               char **arguments,
    496                               unsigned int desct,
    497                               char **descriptions)
    498 {
    499   printf (
    500     "\n"
    501     "/**\n * %s\n",
    502     indent (" * ", comment));
    503   for (unsigned int off = 0; off < desct; off++)
    504   {
    505     const char *arg = arguments[off];
    506     const char *desc = descriptions[off];
    507     const char *vn = var_name (arg);
    508 
    509     printf (" * @param %s %s\n",
    510             vn,
    511             indent (" *   ",
    512                     desc));
    513   }
    514   if (0 == desct)
    515     printf (" * @param value the value of the parameter");
    516   printf (" * @return structure with the requested setting\n */\n");
    517   printf ("static MHD_INLINE struct MHD_%sOptionAndValue\n"
    518           "MHD_%c_OPTION_%s (\n",
    519           capitalize (category),
    520           (char)toupper (*category),
    521           uppercase (name));
    522   if (0 == argc)
    523     printf ("  %s value",
    524             NULL != type
    525             ? type
    526             : arguments[0]);
    527   else
    528     for (unsigned int i = 0; i < argc; i++)
    529     {
    530       const char *arg = arguments[i];
    531       const char *vn
    532         = var_name (arg);
    533 
    534       if (0 != i)
    535         printf (",\n");
    536       printf ("  %.*s%s",
    537               (int)(vn - arg),
    538               arg,
    539               vn);
    540     }
    541   printf (
    542     "\n"
    543     "  )\n"
    544     "{\n"
    545     "  struct MHD_%sOptionAndValue opt_val;\n\n"
    546     "  opt_val.opt = MHD_%c_O_%s;\n",
    547     capitalize (category),
    548     (char)toupper (*category),
    549     uppercase (name));
    550   if (0 == argc)
    551     printf ("  opt_val.val.%s = (value); \\\n",
    552             lowercase (name));
    553   else
    554     for (unsigned int i = 0; i < argc; i++)
    555     {
    556       const char *vn = var_name (arguments[i]);
    557 
    558       if (1 < argc)
    559         printf ("  opt_val.val.%s.v_%s = %s;\n",
    560                 lowercase (name),
    561                 vn,
    562                 vn);
    563       else
    564         printf ("  opt_val.val.%s = %s;\n",
    565                 lowercase (name),
    566                 vn);
    567     }
    568   printf ("\n  return opt_val;\n}\n\n");
    569 }
    570 
    571 
    572 static void
    573 dump_option_documentation_functions (const char *name,
    574                                      unsigned int value,
    575                                      const char *comment,
    576                                      const char *type,
    577                                      const char *conditional,
    578                                      const char *custom_setter,
    579                                      unsigned int argc,
    580                                      char **arguments,
    581                                      unsigned int desct,
    582                                      char **descriptions)
    583 {
    584 
    585   fprintf (f,
    586            "/**\n * %s\n",
    587            indent (" * ", comment));
    588   for (unsigned int off = 0; off < desct; off++)
    589   {
    590     const char *arg = arguments[off];
    591     const char *desc = descriptions[off];
    592     const char *vn = var_name (arg);
    593 
    594     fprintf (f, " * @param %s %s\n",
    595              vn,
    596              indent (" *   ",
    597                      desc));
    598   }
    599   if (0 == desct)
    600     fprintf (f, " * @param value the value of the parameter");
    601   fprintf (f, " * @return structure with the requested setting\n */\n");
    602   fprintf (f, "struct MHD_%sOptionAndValue\n"
    603            "MHD_%c_OPTION_%s (\n",
    604            capitalize (category),
    605            (char)toupper (*category),
    606            uppercase (name));
    607   if (0 == argc)
    608     fprintf (f,
    609              "  %s value",
    610              NULL != type
    611              ? type
    612              : arguments[0]);
    613   else
    614     for (unsigned int i = 0; i < argc; i++)
    615     {
    616       const char *arg = arguments[i];
    617       const char *vn = var_name (arg);
    618 
    619       if (0 != i)
    620         fprintf (f, ",\n");
    621       fprintf (f,
    622                "  %.*s%s",
    623                (int)(vn - arg),
    624                arg,
    625                vn);
    626     }
    627   fprintf (f,
    628            "\n  );\n\n");
    629 }
    630 
    631 
    632 /* Emit "      DST = SRC;", wrapped after the '=' when the whole line would
    633  * exceed the 80-column target. */
    634 static void
    635 dump_setter_assignment (const char *dst,
    636                         const char *src)
    637 {
    638   if (80 >= 6 + strlen (dst) + 3 + strlen (src) + 1)
    639     fprintf (f,
    640              "      %s = %s;\n",
    641              dst,
    642              src);
    643   else
    644     fprintf (f,
    645              "      %s =\n"
    646              "        %s;\n",
    647              dst,
    648              src);
    649 }
    650 
    651 
    652 static void
    653 dump_option_set_switch (const char *name,
    654                         unsigned int value,
    655                         const char *comment,
    656                         const char *type,
    657                         const char *conditional,
    658                         const char *custom_setter,
    659                         unsigned int argc,
    660                         char **arguments,
    661                         unsigned int desc,
    662                         char **descriptions)
    663 {
    664   if (NULL != conditional)
    665     fprintf (f,
    666              "#ifdef HAVE_%s",
    667              uppercase (conditional));
    668   fprintf (f,
    669            "    case MHD_%c_O_%s:\n",
    670            (char)toupper (*category),
    671            uppercase (name));
    672   if (NULL != custom_setter)
    673   {
    674     /* Emit line by line: preprocessor directives must stay at the first
    675      * column, ordinary code lines get the 'case' body indentation. */
    676     const char *line = custom_setter;
    677 
    678     while (NULL != line)
    679     {
    680       const char *nl = strchr (line, '\n');
    681       size_t len = (NULL == nl) ? strlen (line) : (size_t)(nl - line);
    682 
    683       if (0 == len)
    684         fprintf (f, "\n");
    685       else if ('#' == line[0])
    686         fprintf (f, "%.*s\n", (int)len, line);
    687       else
    688         fprintf (f, "      %.*s\n", (int)len, line);
    689       line = (NULL == nl) ? NULL : nl + 1;
    690     }
    691   }
    692   else
    693   {
    694     if (0 == argc)
    695     {
    696       char *dst;
    697       char *src;
    698 
    699       my_asprintf (&dst, "settings->%s", lowercase (name));
    700       my_asprintf (&src, "option->val.%s", lowercase (name));
    701       dump_setter_assignment (dst, src);
    702       free (dst);
    703       free (src);
    704     }
    705     else
    706     {
    707       for (unsigned int i = 0; i < argc; i++)
    708       {
    709         const char *vn = var_name (arguments[i]);
    710         char *dst;
    711         char *src;
    712 
    713         if (1 < argc)
    714         {
    715           my_asprintf (&dst, "settings->%s.v_%s", lowercase (name), vn);
    716           my_asprintf (&src, "option->val.%s.v_%s", lowercase (name), vn);
    717         }
    718         else
    719         {
    720           my_asprintf (&dst, "settings->%s", lowercase (name));
    721           my_asprintf (&src, "option->val.%s", lowercase (name));
    722         }
    723         dump_setter_assignment (dst, src);
    724         free (dst);
    725         free (src);
    726       }
    727     }
    728   }
    729   fprintf (f,
    730            "      continue;\n");
    731   if (NULL != conditional)
    732     fprintf (f,
    733              "#endif\n");
    734 }
    735 
    736 
    737 static char **
    738 parse (char **target,
    739        const char *prefix,
    740        const char *input)
    741 {
    742   if (0 != strncasecmp (prefix,
    743                         input,
    744                         strlen (prefix)))
    745     return NULL;
    746   *target = strdup (input + strlen (prefix));
    747   return target;
    748 }
    749 
    750 
    751 int
    752 main (int argc,
    753       char **argv)
    754 {
    755   static char *dummy;
    756   struct Option *head = NULL;
    757 
    758   if (argc < 2)
    759   {
    760     fprintf (stderr,
    761              "Category argument required\n");
    762     return 3;
    763   }
    764   category = argv[1];
    765 
    766   {
    767     char *fn;
    768     char line[4092];
    769     char **larg = NULL;
    770     unsigned int off;
    771     struct Option *last = NULL;
    772 
    773     my_asprintf (&fn,
    774                  "%c_options.rec",
    775                  *category);
    776     f = fopen (fn, "r");
    777     if (NULL == f)
    778     {
    779       fprintf (stderr,
    780                "Failed to open %s: %s\n",
    781                fn,
    782                strerror (errno));
    783       free (fn);
    784       return 2;
    785     }
    786     off = 0;
    787 TOP:
    788     while (1)
    789     {
    790       ssize_t r;
    791 
    792       if (NULL ==
    793           fgets (line,
    794                  sizeof (line),
    795                  f))
    796         break;
    797       r = strlen (line);
    798       off++;
    799       while ((r > 0)
    800              && ((isspace (line[r - 1]))
    801                  || (line[r - 1] == '\n')))
    802         line[--r] = '\0'; /* remove new line */
    803       if (0 == r)
    804       {
    805         larg = NULL;
    806         continue;
    807       }
    808       if ((NULL != larg)
    809           && ((0 == strncmp ("+ ",
    810                              line,
    811                              2))
    812               || (0 == strcmp ("+",
    813                                line))))
    814       {
    815         if (larg == &dummy)
    816         {
    817           fprintf (stderr,
    818                    "Continuation after 'Value:' not supported on line %u\n",
    819                    off);
    820           exit (2);
    821         }
    822 
    823         *larg = (char *)realloc (*larg,
    824                                  strlen (*larg) + r + 2);
    825         strcat (*larg,
    826                 "\n");
    827         if (0 != strcmp ("+",
    828                          line))
    829           strcat (*larg,
    830                   line + 2);
    831         continue;
    832       }
    833       if (('%' == line[0])
    834           || ('#' == line[0]))
    835         continue;
    836       if (NULL == larg)
    837       {
    838         struct Option *o = (struct Option *)malloc (sizeof (struct Option));
    839 
    840         memset (o, 0, sizeof (*o));
    841         if (NULL == head)
    842           head = o;
    843         else
    844           last->next = o;
    845         last = o;
    846       }
    847       if (NULL != (larg = parse (&last->name,
    848                                  "Name: ",
    849                                  line)))
    850         continue;
    851       if (NULL != (larg = parse (&last->comment,
    852                                  "Comment: ",
    853                                  line)))
    854         continue;
    855       if (NULL != (larg = parse (&last->type,
    856                                  "Type: ",
    857                                  line)))
    858         continue;
    859       if (NULL != (larg = parse (&last->conditional,
    860                                  "Conditional: ",
    861                                  line)))
    862         continue;
    863       if (NULL != (larg = parse (&last->custom_setter,
    864                                  "CustomSetter: ",
    865                                  line)))
    866         continue;
    867       if (0 == strncasecmp (line,
    868                             "Value: ",
    869                             strlen ("Value: ")))
    870       {
    871         char xdummy;
    872 
    873         if (1 == sscanf (line + strlen ("Value: "),
    874                          "%u%c",
    875                          &last->value,
    876                          &xdummy))
    877         {
    878           larg = &dummy;
    879           continue;
    880         }
    881         fprintf (stderr,
    882                  "Value on line %d not a number\n",
    883                  off);
    884         return 2;
    885       }
    886       for (unsigned int i = 0; i < MAX_ARGS; i++)
    887       {
    888         char name[32];
    889 
    890         snprintf (name,
    891                   sizeof (name),
    892                   "Argument%u: ",
    893                   i + 1);
    894         if (NULL != (larg = parse (&last->arguments[i],
    895                                    name,
    896                                    line)))
    897         {
    898           last->argc = i + 1;
    899           goto TOP;
    900         }
    901         snprintf (name,
    902                   sizeof (name),
    903                   "Description%u: ",
    904                   i + 1);
    905         if (NULL != (larg = parse (&last->descriptions[i],
    906                                    name,
    907                                    line)))
    908         {
    909           last->desc = i + 1;
    910           goto TOP;
    911         }
    912       }
    913       fprintf (stderr,
    914                "Could not parse line %u: `%s'\n",
    915                off,
    916                line);
    917       exit (2);
    918     }
    919     free (fn);
    920   }
    921 
    922   iterate (head,
    923            &check);
    924 
    925   /* Generate enum MHD_${CATEGORY}Option */
    926   printf ("/**\n"
    927           " * The options (parameters) for MHD %s\n"
    928           " */\n"
    929           "enum MHD_FIXED_ENUM_APP_SET_ MHD_%sOption\n"
    930           "{",
    931           category,
    932           capitalize (category));
    933   printf ("  /**\n"
    934           "   * Not a real option.\n"
    935           "   * Should not be used directly.\n"
    936           "   * This value indicates the end of the list of the options.\n"
    937           "   */\n"
    938           "  MHD_%c_O_END = 0\n"
    939           "  ,\n\n",
    940           (char)toupper (*category));
    941   iterate (head,
    942            &dump_enum);
    943   printf ("  /**\n"
    944           "   * The sentinel value.\n"
    945           "   * This value enforces specific underlying integer type for the enum.\n"
    946           "   * Do not use.\n"
    947           "   */\n"
    948           "  MHD_%c_O_SENTINEL = 65535\n\n",
    949           (char)toupper (*category));
    950   printf ("};\n\n");
    951   iterate (head,
    952            &dump_union_members);
    953 
    954   /* Generate union MHD_${CATEGORY}OptionValue */
    955   printf ("/**\n"
    956           " * Parameters for MHD %s options\n"
    957           " */\n"
    958           "union MHD_%sOptionValue\n"
    959           "{\n",
    960           category,
    961           capitalize (category));
    962   f = stdout;
    963   iterate (head,
    964            &dump_union);
    965   f = NULL;
    966   printf ("};\n\n");
    967 
    968   printf ("\n"
    969           "struct MHD_%sOptionAndValue\n"
    970           "{\n"
    971           "  /**\n"
    972           "   * The %s configuration option\n"
    973           "   */\n"
    974           "  enum MHD_%sOption opt;\n\n"
    975           "  /**\n"
    976           "   * The value for the @a opt option\n"
    977           "   */\n"
    978           "  union MHD_%sOptionValue val;\n"
    979           "};\n\n",
    980           capitalize (category),
    981           category,
    982           capitalize (category),
    983           capitalize (category));
    984   printf (
    985     "#if defined(MHD_USE_COMPOUND_LITERALS) && defined(MHD_USE_DESIG_NEST_INIT)\n");
    986   iterate (head,
    987            &dump_option_macros);
    988   printf (
    989     "\n"
    990     "/**\n"
    991     " * Terminate the list of the options\n"
    992     " * @return the terminating object of struct MHD_%sOptionAndValue\n"
    993     " */\n"
    994     "#  define MHD_%c_OPTION_TERMINATE() \\\n"
    995     "        MHD_NOWARN_COMPOUND_LITERALS_ \\\n"
    996     "          (const struct MHD_%sOptionAndValue) \\\n"
    997     "        { \\\n"
    998     "          .opt = (MHD_%c_O_END) \\\n"
    999     "        } \\\n"
   1000     "        MHD_RESTORE_WARN_COMPOUND_LITERALS_\n\n",
   1001     capitalize (category),
   1002     (char)toupper (*category),
   1003     capitalize (category),
   1004     (char)toupper (*category));
   1005 
   1006   printf (
   1007     "#else /* !MHD_USE_COMPOUND_LITERALS || !MHD_USE_DESIG_NEST_INIT */\n");
   1008   printf ("MHD_NOWARN_UNUSED_FUNC_");
   1009   iterate (head,
   1010            &dump_option_static_functions);
   1011   printf ("\n/**\n"
   1012           " * Terminate the list of the options\n"
   1013           " * @return the terminating object of struct MHD_%sOptionAndValue\n"
   1014           " */\n"
   1015           "static MHD_INLINE struct MHD_%sOptionAndValue\n"
   1016           "MHD_%c_OPTION_TERMINATE (void)\n"
   1017           "{\n"
   1018           "  struct MHD_%sOptionAndValue opt_val;\n\n"
   1019           "  opt_val.opt = MHD_%c_O_END;\n\n"
   1020           "  return opt_val;\n"
   1021           "}\n\n\n",
   1022           capitalize (category),
   1023           capitalize (category),
   1024           (char)toupper (*category),
   1025           capitalize (category),
   1026           (char)toupper (*category));
   1027 
   1028   printf ("MHD_RESTORE_WARN_UNUSED_FUNC_\n");
   1029   printf (
   1030     "#endif /* !MHD_USE_COMPOUND_LITERALS || !MHD_USE_DESIG_NEST_INIT */\n");
   1031 
   1032   {
   1033     char *doc_in;
   1034 
   1035     my_asprintf (&doc_in,
   1036                  "microhttpd2_inline_%s_documentation.h.in",
   1037                  category);
   1038     (void)unlink (doc_in);
   1039     f = fopen (doc_in, "w");
   1040     if (NULL == f)
   1041     {
   1042       fprintf (stderr,
   1043                "Failed to open `%s': %s\n",
   1044                doc_in,
   1045                strerror (errno));
   1046       return 2;
   1047     }
   1048     fprintf (f,
   1049              "/* Beginning of generated code documenting how to use options.\n"
   1050              "   You should treat the following functions *as if* they were\n"
   1051              "   part of the header/API. The actual declarations are more\n"
   1052              "   complex, so these here are just for documentation!\n"
   1053              "   We do not actually *build* this code... */\n"
   1054              "#if 0\n\n");
   1055     iterate (head,
   1056              &dump_option_documentation_functions);
   1057     fprintf (f,
   1058              "/* End of generated code documenting how to use options */\n#endif\n\n");
   1059     fclose (f);
   1060     chmod (doc_in, S_IRUSR | S_IRGRP | S_IROTH);
   1061   }
   1062 
   1063   {
   1064     char *so_c;
   1065 
   1066     my_asprintf (&so_c,
   1067                  "../mhd2/%s_set_options.c",
   1068                  category);
   1069     (void)unlink (so_c);
   1070     f = fopen (so_c, "w");
   1071     if (NULL == f)
   1072     {
   1073       fprintf (stderr,
   1074                "Failed to open `%s': %s\n",
   1075                so_c,
   1076                strerror (errno));
   1077       return 2;
   1078     }
   1079     fprintf (f,
   1080              "/* This is generated code, it is still under LGPLv2.1+.\n"
   1081              "   Do not edit directly! */\n"
   1082              "/**\n"
   1083              " * @file %s_set_options.c\n"
   1084              " * @author options-generator.c\n"
   1085              " */\n"
   1086              "\n",
   1087              category);
   1088     if (0 == strcmp (category, "daemon"))
   1089     {
   1090       fprintf (f,
   1091                "#include \"mhd_sys_options.h\"\n"
   1092                "#include \"sys_base_types.h\"\n"
   1093                "#include \"sys_malloc.h\"\n"
   1094                "#include <string.h>\n"
   1095                "#include \"mhd_daemon.h\"\n"
   1096                "#include \"daemon_options.h\"\n"
   1097                "#include \"mhd_public_api.h\"\n"
   1098                "\n");
   1099     }
   1100     else if (0 == strcmp (category, "response"))
   1101     {
   1102       fprintf (f,
   1103                "#include \"mhd_sys_options.h\"\n"
   1104                "#include \"response_set_options.h\"\n"
   1105                "#include \"sys_base_types.h\"\n"
   1106                "#include \"sys_bool_type.h\"\n"
   1107                "#include \"response_options.h\"\n"
   1108                "#include \"mhd_response.h\"\n"
   1109                "#include \"mhd_public_api.h\"\n"
   1110                "#include \"mhd_locks.h\"\n"
   1111                "#include \"mhd_assert.h\"\n"
   1112                "#include \"response_funcs.h\"\n"
   1113                "\n");
   1114     }
   1115 
   1116     fprintf (f,
   1117              "\n"
   1118              "MHD_FN_PAR_NONNULL_ALL_ MHD_EXTERN_\n"
   1119              "enum MHD_StatusCode\n"
   1120              "MHD_%s_set_options (\n"
   1121              "  struct MHD_%s *MHD_RESTRICT %s,\n"
   1122              "  const struct MHD_%sOptionAndValue *MHD_RESTRICT options,\n"
   1123              "  size_t options_max_num)\n"
   1124              "{\n",
   1125              category,
   1126              capitalize (category),
   1127              category,
   1128              capitalize (category));
   1129     fprintf (f,
   1130              "  struct %sOptions *restrict settings = %s->settings;\n"
   1131              "  enum MHD_StatusCode res = MHD_SC_OK;\n"
   1132              "  size_t i;\n",
   1133              capitalize (category),
   1134              category);
   1135     if (0 == strcmp (category, "daemon"))
   1136     {
   1137       fprintf (f,
   1138                "\n"
   1139                "  if (mhd_DAEMON_STATE_NOT_STARTED != daemon->state)\n"
   1140                "    return MHD_SC_TOO_LATE;\n"
   1141                "\n");
   1142     }
   1143     else if (0 == strcmp (category, "response"))
   1144     {
   1145       fprintf (f,
   1146                "  bool need_unlock = false;\n"
   1147                "\n"
   1148                "  if (response->frozen)\n"
   1149                "    return MHD_SC_TOO_LATE;\n"
   1150                "  if (response->reuse.reusable)\n"
   1151                "  {\n"
   1152                "    need_unlock = true;\n"
   1153                "    if (!mhd_mutex_lock (&response->reuse.settings_lock))\n"
   1154                "      return MHD_SC_RESP_MUTEX_LOCK_FAILED;\n"
   1155                "    mhd_assert (1 == mhd_atomic_counter_get (&response->reuse.counter));\n"
   1156                "    if (response->frozen) /* Firm re-check under the lock */\n"
   1157                "    {\n"
   1158                "      mhd_mutex_unlock_chk (&response->reuse.settings_lock);\n"
   1159                "      return MHD_SC_TOO_LATE;\n"
   1160                "    }\n"
   1161                "  }\n"
   1162                "\n");
   1163     }
   1164     else
   1165     {
   1166       fprintf (f,
   1167                "\n"
   1168                "  if (NULL == settings)\n"
   1169                "    return MHD_SC_TOO_LATE;\n"
   1170                "\n");
   1171     }
   1172     fprintf (f,
   1173              "  for (i = 0; i < options_max_num; i++)\n"
   1174              "  {\n"
   1175              "    const struct MHD_%sOptionAndValue *const option\n"
   1176              "      = options + i;\n"
   1177              "    switch (option->opt)\n"
   1178              "    {\n",
   1179              capitalize (category));
   1180     fprintf (f,
   1181              "    case MHD_%c_O_END:\n"
   1182              "      i = options_max_num - 1;\n"
   1183              "      break;\n",
   1184              (char)toupper (*category));
   1185     iterate (head,
   1186              &dump_option_set_switch);
   1187     fprintf (f,
   1188              "    case MHD_%c_O_SENTINEL:\n"
   1189              "    default: /* for -Wswitch-default -Wswitch-enum */\n"
   1190              "      res = MHD_SC_OPTION_UNKNOWN;\n"
   1191              "      i = options_max_num - 1;\n"
   1192              "      break;\n",
   1193              (char)toupper (*category));
   1194     fprintf (f,
   1195              "    }\n"
   1196              "  }\n");
   1197     if (0 == strcmp (category, "response"))
   1198     {
   1199       fprintf (f,
   1200                "\n"
   1201                "  if (need_unlock)\n"
   1202                "    mhd_mutex_unlock_chk (&response->reuse.settings_lock);\n"
   1203                "\n");
   1204     }
   1205     fprintf (f,
   1206              "  return res;\n"
   1207              "}\n");
   1208     fclose (f);
   1209     chmod (so_c, S_IRUSR | S_IRGRP | S_IROTH);
   1210     free (so_c);
   1211   }
   1212 
   1213   {
   1214     char *do_h;
   1215 
   1216     my_asprintf (&do_h,
   1217                  "../mhd2/%s_options.h",
   1218                  category);
   1219     (void)unlink (do_h);
   1220     f = fopen (do_h, "w");
   1221     if (NULL == f)
   1222     {
   1223       fprintf (stderr,
   1224                "Failed to open `%s': %s\n",
   1225                do_h,
   1226                strerror (errno));
   1227       return 2;
   1228     }
   1229     fprintf (f,
   1230              "/* This is generated code, it is still under LGPLv2.1+.\n"
   1231              "   Do not edit directly! */\n"
   1232              "/**\n"
   1233              " * @file %s_options.h\n"
   1234              " * @author %s-options-generator.c\n"
   1235              " */\n\n"
   1236              "#ifndef MHD_%s_OPTIONS_H\n"
   1237              "#define MHD_%s_OPTIONS_H 1\n"
   1238              "\n"
   1239              "#include \"mhd_sys_options.h\"\n"
   1240              "#include \"mhd_public_api.h\"\n"
   1241              "\n"
   1242              "struct %sOptions\n"
   1243              "{\n",
   1244              category,
   1245              category,
   1246              uppercase (category),
   1247              uppercase (category),
   1248              capitalize (category));
   1249     iterate (head,
   1250              &dump_struct);
   1251     fprintf (f,
   1252              "};\n"
   1253              "\n"
   1254              "#endif /* ! MHD_%s_OPTIONS_H 1 */\n",
   1255              uppercase (category));
   1256     fclose (f);
   1257     chmod (do_h, S_IRUSR | S_IRGRP | S_IROTH);
   1258     free (do_h);
   1259   }
   1260 
   1261   return 0;
   1262 }