]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/cf_gen.cc
Source Format Enforcement (#1234)
[thirdparty/squid.git] / src / cf_gen.cc
index 5fdc4ad3966df7862fe67927feac9085cc26769c..07e1caf5c8b3cdba6d630faec8a4b9b730200c7a 100644 (file)
@@ -1,50 +1,26 @@
 /*
- * DEBUG: none          Generate squid.conf.default and cf_parser.cci
- * AUTHOR: Max Okumoto
- * AUTHOR: Francesco Chemolli
- * AUTHOR: Amos Jeffries
- *
- * SQUID Web Proxy Cache          http://www.squid-cache.org/
- * ----------------------------------------------------------
- *
- *  Squid is the result of efforts by numerous individuals from
- *  the Internet community; see the CONTRIBUTORS file for full
- *  details.   Many organizations have provided support for Squid's
- *  development; see the SPONSORS file for full details.  Squid is
- *  Copyrighted (C) 2001 by the Regents of the University of
- *  California; see the COPYRIGHT file for full details.  Squid
- *  incorporates software developed and/or copyrighted by other
- *  sources; see the CREDITS file for full details.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
  *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
+/* DEBUG: none          Generate squid.conf.default and cf_parser.cci */
+
 /*****************************************************************************
- * Abstract:   This program parses the input file and generates code and
- *             files used to configure the variables in squid.
- *             (ie it creates the squid.conf.default file from the cf.data file)
+ * Abstract:    This program parses the input file and generates code and
+ *      files used to configure the variables in squid.
+ *      (ie it creates the squid.conf.default file from the cf.data file)
  *
- *             The output files are as follows:
- *             cf_parser.cci - this file contains, default_all() which
- *                       initializes variables with the default
- *                       values, parse_line() that parses line from
- *                       squid.conf.default, dump_config that dumps the
- *                       current the values of the variables.
- *             squid.conf.default - default configuration file given to the server
- *                      administrator.
+ *      The output files are as follows:
+ *      cf_parser.cci - this file contains, default_all() which
+ *            initializes variables with the default
+ *            values, parse_line() that parses line from
+ *            squid.conf.default, dump_config that dumps the
+ *            current the values of the variables.
+ *      squid.conf.default - default configuration file given to the server
+ *           administrator.
  *****************************************************************************/
 
 /*
@@ -68,17 +44,17 @@ _FILE_OFFSET_BITS==64
 
 #include "cf_gen_defines.cci"
 
-#define MAX_LINE       1024    /* longest configuration line */
-#define _PATH_PARSER           "cf_parser.cci"
-#define _PATH_SQUID_CONF       "squid.conf.documented"
-#define _PATH_SQUID_CONF_SHORT "squid.conf.default"
-#define _PATH_CF_DEPEND                "cf.data.depend"
+#define MAX_LINE    1024    /* longest configuration line */
+#define _PATH_PARSER        "cf_parser.cci"
+#define _PATH_SQUID_CONF    "squid.conf.documented"
+#define _PATH_SQUID_CONF_SHORT  "squid.conf.default"
+#define _PATH_CF_DEPEND     "cf.data.depend"
 
 enum State {
     sSTART,
     s1,
     sDOC,
-    sNOCOMMENT,
+    sCFGLINES,
     sEXIT
 };
 
@@ -89,9 +65,6 @@ typedef std::list<std::string> EntryAliasList;
 class DefaultValues
 {
 public:
-    DefaultValues() : preset(), if_none(), docs() {}
-    ~DefaultValues() {}
-
     /// Default config lines to be defined before parsing the config files.
     LineList preset;
 
@@ -111,11 +84,7 @@ public:
 class Entry
 {
 public:
-    Entry(const char *str) :
-            name(str), alias(),type(), loc(),
-            defaults(), comment(), ifdef(), doc(), nocomment(),
-            array_flag(0) {}
-    ~Entry() {}
+    explicit Entry(const char *str) : name(str) {}
 
     std::string name;
     EntryAliasList alias;
@@ -125,8 +94,8 @@ public:
     std::string comment;
     std::string ifdef;
     LineList doc;
-    LineList nocomment;
-    int array_flag;
+    LineList cfgLines; ///< between CONFIG_START and CONFIG_END
+    int array_flag = 0; ///< TYPE is a raw array[] declaration
 
     void genParse(std::ostream &fout) const;
 
@@ -140,7 +109,6 @@ class Type
 {
 public:
     Type(const char *str) : name(str) {}
-    ~Type() {}
 
     std::string name;
     TypeDepList depend;
@@ -157,35 +125,44 @@ static void gen_conf(const EntryList &, std::ostream&, bool verbose_output);
 static void gen_default_if_none(const EntryList &, std::ostream&);
 static void gen_default_postscriptum(const EntryList &, std::ostream&);
 static bool isDefined(const std::string &name);
+static const char *available_if(const std::string &name);
+static const char *gen_quote_escape(const std::string &var);
 
 static void
 checkDepend(const std::string &directive, const char *name, const TypeList &types, const EntryList &entries)
 {
-    for (TypeList::const_iterator t = types.begin(); t != types.end(); ++t) {
-        if (t->name.compare(name) != 0)
+    for (const auto &t : types) {
+        if (t.name.compare(name) != 0)
             continue;
-        for (TypeDepList::const_iterator dep = t->depend.begin(); dep != t->depend.end(); ++dep) {
+        for (const auto &dep : t.depend) {
             EntryList::const_iterator entry = entries.begin();
             for (; entry != entries.end(); ++entry) {
-                if (entry->name.compare(*dep) == 0)
+                if (entry->name.compare(dep) == 0)
                     break;
             }
             if (entry == entries.end()) {
-                std::cerr << "ERROR: '" << directive << "' (" << name << ") depends on '" << *dep << "'\n";
-                exit(1);
+                std::cerr << "ERROR: '" << directive << "' (" << name << ") depends on '" << dep << "'\n";
+                exit(EXIT_FAILURE);
             }
         }
         return;
     }
     std::cerr << "ERROR: Dependencies for cf.data type '" << name << "' used in ' " << directive << "' not defined\n" ;
-    exit(1);
+    exit(EXIT_FAILURE);
 }
 
 static void
 usage(const char *program_name)
 {
     std::cerr << "Usage: " << program_name << " cf.data cf.data.depend\n";
-    exit(1);
+    exit(EXIT_FAILURE);
+}
+
+static void
+errorMsg(const char *filename, int line, const char *detail)
+{
+    std::cerr << "Error in '" << filename << "' on line " << line <<
+              "--> " << detail << std::endl;
 }
 
 int
@@ -201,7 +178,7 @@ main(int argc, char *argv[])
     TypeList types;
     enum State state;
     int rc = 0;
-    char *ptr = NULL;
+    char *ptr = nullptr;
     char buff[MAX_LINE];
     std::ifstream fp;
     std::stack<std::string> IFDEFS;
@@ -217,9 +194,9 @@ main(int argc, char *argv[])
      *-------------------------------------------------------------------*/
     fp.open(type_depend, std::ifstream::in);
     if (fp.fail()) {
-        std::cerr << "error while opening type dependencies file '" <<
+        std::cerr << "Error while opening type dependencies file '" <<
                   type_depend << "': " << strerror(errno) << std::endl;
-        exit(1);
+        exit(EXIT_FAILURE);
     }
 
     while (fp.good()) {
@@ -229,7 +206,7 @@ main(int argc, char *argv[])
         if (!type || type[0] == '#')
             continue;
         Type t(type);
-        while ((dep = strtok(NULL, WS)) != NULL) {
+        while ((dep = strtok(nullptr, WS)) != nullptr) {
             t.depend.push_front(dep);
         }
         types.push_front(t);
@@ -244,9 +221,9 @@ main(int argc, char *argv[])
     /* Open input file */
     fp.open(input_filename, std::ifstream::in);
     if (fp.fail()) {
-        std::cerr << "error while opening input file '" <<
+        std::cerr << "Error while opening input file '" <<
                   input_filename << "': " << strerror(errno) << std::endl;
-        exit(1);
+        exit(EXIT_FAILURE);
     }
 
     state = sSTART;
@@ -260,16 +237,16 @@ main(int argc, char *argv[])
             *t = '\0';
 
         if (strncmp(buff, "IF ", 3) == 0) {
-            if ((ptr = strtok(buff + 3, WS)) == NULL) {
-                std::cerr << "Missing IF parameter on line" << linenum << std::endl;
-                exit(1);
+            if ((ptr = strtok(buff + 3, WS)) == nullptr) {
+                errorMsg(input_filename, linenum, "Missing IF parameter");
+                exit(EXIT_FAILURE);
             }
             IFDEFS.push(ptr);
             continue;
         } else if (strcmp(buff, "ENDIF") == 0) {
             if (IFDEFS.size() == 0) {
-                std::cerr << "ENDIF without IF before on line " << linenum << std::endl;
-                exit(1);
+                errorMsg(input_filename, linenum, "ENDIF without IF first");
+                exit(EXIT_FAILURE);
             }
             IFDEFS.pop();
         } else if (!IFDEFS.size() || isDefined(IFDEFS.top()))
@@ -283,27 +260,26 @@ main(int argc, char *argv[])
                 } else if (!strncmp(buff, "NAME:", 5)) {
                     char *name, *aliasname;
 
-                    if ((name = strtok(buff + 5, WS)) == NULL) {
-                        std::cerr << "Error in input file\n";
-                        exit(1);
+                    if ((name = strtok(buff + 5, WS)) == nullptr) {
+                        errorMsg(input_filename, linenum, buff);
+                        exit(EXIT_FAILURE);
                     }
 
-                    entries.push_back(name);
+                    auto &newEntry = entries.emplace_back(name);
 
-                    while ((aliasname = strtok(NULL, WS)) != NULL)
-                        entries.back().alias.push_front(aliasname);
+                    while ((aliasname = strtok(nullptr, WS)) != nullptr)
+                        newEntry.alias.push_front(aliasname);
 
                     state = s1;
                 } else if (!strcmp(buff, "EOF")) {
                     state = sEXIT;
                 } else if (!strcmp(buff, "COMMENT_START")) {
-                    entries.push_back("comment");
-                    entries.back().loc = "none";
+                    auto &newEntry = entries.emplace_back("comment");
+                    newEntry.loc = "none";
                     state = sDOC;
                 } else {
-                    std::cerr << "Error on line " << linenum << std::endl <<
-                              "--> " << buff << std::endl;
-                    exit(1);
+                    errorMsg(input_filename, linenum, buff);
+                    exit(EXIT_FAILURE);
                 }
 
                 break;
@@ -350,20 +326,20 @@ main(int argc, char *argv[])
 
                     curr.defaults.docs.push_back(ptr);
                 } else if (!strncmp(buff, "LOC:", 4)) {
-                    if ((ptr = strtok(buff + 4, WS)) == NULL) {
-                        std::cerr << "Error on line " << linenum << std::endl;
-                        exit(1);
+                    if ((ptr = strtok(buff + 4, WS)) == nullptr) {
+                        errorMsg(input_filename, linenum, buff);
+                        exit(EXIT_FAILURE);
                     }
 
                     curr.loc = ptr;
                 } else if (!strncmp(buff, "TYPE:", 5)) {
-                    if ((ptr = strtok(buff + 5, WS)) == NULL) {
-                        std::cerr << "Error on line " << linenum << std::endl;
-                        exit(1);
+                    if ((ptr = strtok(buff + 5, WS)) == nullptr) {
+                        errorMsg(input_filename, linenum, buff);
+                        exit(EXIT_FAILURE);
                     }
 
                     /* hack to support arrays, rather than pointers */
-                    if (0 == strcmp(ptr + strlen(ptr) - 2, "[]")) {
+                    if (strcmp(ptr + strlen(ptr) - 2, "[]") == 0) {
                         curr.array_flag = 1;
                         *(ptr + strlen(ptr) - 2) = '\0';
                     }
@@ -371,9 +347,9 @@ main(int argc, char *argv[])
                     checkDepend(curr.name, ptr, types, entries);
                     curr.type = ptr;
                 } else if (!strncmp(buff, "IFDEF:", 6)) {
-                    if ((ptr = strtok(buff + 6, WS)) == NULL) {
-                        std::cerr << "Error on line " << linenum << std::endl;
-                        exit(1);
+                    if ((ptr = strtok(buff + 6, WS)) == nullptr) {
+                        errorMsg(input_filename, linenum, buff);
+                        exit(EXIT_FAILURE);
                     }
 
                     curr.ifdef = ptr;
@@ -382,8 +358,8 @@ main(int argc, char *argv[])
                 } else if (!strcmp(buff, "DOC_NONE")) {
                     state = sSTART;
                 } else {
-                    std::cerr << "Error on line " << linenum << std::endl;
-                    exit(1);
+                    errorMsg(input_filename, linenum, buff);
+                    exit(EXIT_FAILURE);
                 }
             }
             break;
@@ -391,33 +367,30 @@ main(int argc, char *argv[])
             case sDOC:
                 if (!strcmp(buff, "DOC_END") || !strcmp(buff, "COMMENT_END")) {
                     state = sSTART;
-                } else if (!strcmp(buff, "NOCOMMENT_START")) {
-                    state = sNOCOMMENT;
-                } else { // if (buff != NULL) {
-                    assert(buff != NULL);
+                } else if (strcmp(buff, "CONFIG_START") == 0) {
+                    state = sCFGLINES;
+                } else {
                     entries.back().doc.push_back(buff);
                 }
                 break;
 
-            case sNOCOMMENT:
-                if (!strcmp(buff, "NOCOMMENT_END")) {
+            case sCFGLINES:
+                if (strcmp(buff, "CONFIG_END") == 0) {
                     state = sDOC;
-                } else { // if (buff != NULL) {
-                    assert(buff != NULL);
-                    entries.back().nocomment.push_back(buff);
+                } else {
+                    entries.back().cfgLines.push_back(buff);
                 }
                 break;
-
             case sEXIT:
-                assert(0);             /* should never get here */
+                assert(0);      /* should never get here */
                 break;
             }
 
     }
 
     if (state != sEXIT) {
-        std::cerr << "Error: unexpected EOF\n";
-        exit(1);
+        errorMsg(input_filename, linenum, "Error: unexpected EOF");
+        exit(EXIT_FAILURE);
     }
 
     fp.close();
@@ -434,19 +407,19 @@ main(int argc, char *argv[])
 
     std::ofstream fout(output_filename,std::ostream::out);
     if (!fout.good()) {
-        std::cerr << "error while opening output .c file '" <<
+        std::cerr << "Error while opening output .c file '" <<
                   output_filename << "': " << strerror(errno) << std::endl;
-        exit(1);
+        exit(EXIT_FAILURE);
     }
 
     fout <<  "/*\n" <<
-    " * Generated automatically from " << input_filename << " by " <<
-    argv[0] << "\n"
-    " *\n"
-    " * Abstract: This file contains routines used to configure the\n"
-    " *           variables in the squid server.\n"
-    " */\n"
-    "\n";
+         " * Generated automatically from " << input_filename << " by " <<
+         argv[0] << "\n"
+         " *\n"
+         " * Abstract: This file contains routines used to configure the\n"
+         " *           variables in the squid server.\n"
+         " */\n"
+         "\n";
 
     rc = gen_default(entries, fout);
 
@@ -465,9 +438,9 @@ main(int argc, char *argv[])
     /* Open output x.conf file */
     fout.open(conf_filename,std::ostream::out);
     if (!fout.good()) {
-        std::cerr << "error while opening output conf file '" <<
+        std::cerr << "Error while opening output conf file '" <<
                   output_filename << "': " << strerror(errno) << std::endl;
-        exit(1);
+        exit(EXIT_FAILURE);
     }
 
     gen_conf(entries, fout, 1);
@@ -476,9 +449,9 @@ main(int argc, char *argv[])
 
     fout.open(conf_filename_short,std::ostream::out);
     if (!fout.good()) {
-        std::cerr << "error while opening output short conf file '" <<
+        std::cerr << "Error while opening output short conf file '" <<
                   output_filename << "': " << strerror(errno) << std::endl;
-        exit(1);
+        exit(EXIT_FAILURE);
     }
     gen_conf(entries, fout, 0);
     fout.close();
@@ -491,58 +464,59 @@ gen_default(const EntryList &head, std::ostream &fout)
 {
     int rc = 0;
     fout << "static void" << std::endl <<
-    "default_line(const char *s)" << std::endl <<
-    "{" << std::endl <<
-    "    LOCAL_ARRAY(char, tmp_line, BUFSIZ);" << std::endl <<
-    "    xstrncpy(tmp_line, s, BUFSIZ);" << std::endl <<
-    "    xstrncpy(config_input_line, s, BUFSIZ);" << std::endl <<
-    "    config_lineno++;" << std::endl <<
-    "    parse_line(tmp_line);" << std::endl <<
-    "}" << std::endl << std::endl;
+         "default_line(const char *s)" << std::endl <<
+         "{" << std::endl <<
+         "    char *tmp_line = xstrdup(s);" << std::endl <<
+         "    int len = strlen(tmp_line);" << std::endl <<
+         "    ProcessMacros(tmp_line, len);" << std::endl <<
+         "    xstrncpy(config_input_line, tmp_line, sizeof(config_input_line));" << std::endl <<
+         "    config_lineno++;" << std::endl <<
+         "    parse_line(tmp_line);" << std::endl <<
+         "    xfree(tmp_line);" << std::endl <<
+         "}" << std::endl << std::endl;
     fout << "static void" << std::endl <<
-    "default_all(void)" << std::endl <<
-    "{" << std::endl <<
-    "    cfg_filename = \"Default Configuration\";" << std::endl <<
-    "    config_lineno = 0;" << std::endl;
+         "default_all(void)" << std::endl <<
+         "{" << std::endl <<
+         "    cfg_filename = \"Default Configuration\";" << std::endl <<
+         "    config_lineno = 0;" << std::endl;
 
-    for (EntryList::const_iterator entry = head.begin(); entry != head.end(); ++entry) {
-        assert(entry->name.size());
+    for (const auto &entry : head) {
+        assert(entry.name.size());
 
-        if (!entry->name.compare("comment"))
+        if (!entry.name.compare("comment"))
             continue;
 
-        if (!entry->type.compare("obsolete"))
+        if (!entry.type.compare("obsolete"))
             continue;
 
-        if (!entry->loc.size()) {
-            std::cerr << "NO LOCATION FOR " << entry->name << std::endl;
+        if (!entry.loc.size()) {
+            std::cerr << "NO LOCATION FOR " << entry.name << std::endl;
             rc |= 1;
             continue;
         }
 
-        if (!entry->defaults.preset.size() && entry->defaults.if_none.empty()) {
-            std::cerr << "NO DEFAULT FOR " << entry->name << std::endl;
+        if (!entry.defaults.preset.size() && entry.defaults.if_none.empty()) {
+            std::cerr << "NO DEFAULT FOR " << entry.name << std::endl;
             rc |= 1;
             continue;
         }
 
-        if (!entry->defaults.preset.size() || entry->defaults.preset.front().compare("none") == 0) {
-            fout << "    // No default for " << entry->name << std::endl;
+        if (!entry.defaults.preset.size() || entry.defaults.preset.front().compare("none") == 0) {
+            fout << "    // No default for " << entry.name << std::endl;
         } else {
-            if (entry->ifdef.size())
-                fout << "#if " << entry->ifdef << std::endl;
+            if (entry.ifdef.size())
+                fout << "#if " << entry.ifdef << std::endl;
 
-            for (LineList::const_iterator l = entry->defaults.preset.begin(); l != entry->defaults.preset.end(); ++l) {
-                fout << "    default_line(\"" << entry->name << " " << *l << "\");" << std::endl;
-            }
+            for (const auto &l : entry.defaults.preset)
+                fout << "    default_line(\"" << entry.name << " " << gen_quote_escape(l) << "\");" << std::endl;
 
-            if (entry->ifdef.size())
+            if (entry.ifdef.size())
                 fout << "#endif" << std::endl;
         }
     }
 
     fout << "    cfg_filename = NULL;" << std::endl <<
-    "}" << std::endl << std::endl;
+         "}" << std::endl << std::endl;
     return rc;
 }
 
@@ -550,36 +524,39 @@ static void
 gen_default_if_none(const EntryList &head, std::ostream &fout)
 {
     fout << "static void" << std::endl <<
-    "defaults_if_none(void)" << std::endl <<
-    "{" << std::endl;
+         "defaults_if_none(void)" << std::endl <<
+         "{" << std::endl <<
+         "    cfg_filename = \"Default Configuration (if absent)\";" << std::endl <<
+         "    config_lineno = 0;" << std::endl;
 
-    for (EntryList::const_iterator entry = head.begin(); entry != head.end(); ++entry) {
-        assert(entry->name.size());
+    for (const auto &entry : head) {
+        assert(entry.name.size());
 
-        if (!entry->loc.size())
+        if (!entry.loc.size())
             continue;
 
-        if (entry->defaults.if_none.empty())
+        if (entry.defaults.if_none.empty())
             continue;
 
-        if (!entry->defaults.preset.empty()) {
-            std::cerr << "ERROR: " << entry->name << " has preset defaults. DEFAULT_IF_NONE cannot be true." << std::endl;
-            exit(1);
+        if (!entry.defaults.preset.empty()) {
+            std::cerr << "ERROR: " << entry.name << " has preset defaults. DEFAULT_IF_NONE cannot be true." << std::endl;
+            exit(EXIT_FAILURE);
         }
 
-        if (entry->ifdef.size())
-            fout << "#if " << entry->ifdef << std::endl;
+        if (entry.ifdef.size())
+            fout << "#if " << entry.ifdef << std::endl;
 
-        fout << "    if (check_null_" << entry->type << "(" << entry->loc << ")) {" << std::endl;
-        for (LineList::const_iterator l = entry->defaults.if_none.begin(); l != entry->defaults.if_none.end(); ++l)
-            fout << "        default_line(\"" << entry->name << " " << *l <<"\");" << std::endl;
+        fout << "    if (check_null_" << entry.type << "(" << entry.loc << ")) {" << std::endl;
+        for (const auto &l : entry.defaults.if_none)
+            fout << "        default_line(\"" << entry.name << " " << gen_quote_escape(l) <<"\");" << std::endl;
         fout << "    }" << std::endl;
 
-        if (entry->ifdef.size())
+        if (entry.ifdef.size())
             fout << "#endif" << std::endl;
     }
 
-    fout << "}" << std::endl << std::endl;
+    fout << "    cfg_filename = NULL;" << std::endl <<
+         "}" << std::endl << std::endl;
 }
 
 /// append configuration options specified by POSTSCRIPTUM lines
@@ -587,49 +564,64 @@ static void
 gen_default_postscriptum(const EntryList &head, std::ostream &fout)
 {
     fout << "static void" << std::endl <<
-    "defaults_postscriptum(void)" << std::endl <<
-    "{" << std::endl;
+         "defaults_postscriptum(void)" << std::endl <<
+         "{" << std::endl <<
+         "    cfg_filename = \"Default Configuration (postscriptum)\";" << std::endl <<
+         "    config_lineno = 0;" << std::endl;
 
-    for (EntryList::const_iterator entry = head.begin(); entry != head.end(); ++entry) {
-        assert(entry->name.size());
+    for (const auto &entry : head) {
+        assert(entry.name.size());
 
-        if (!entry->loc.size())
+        if (!entry.loc.size())
             continue;
 
-        if (entry->defaults.postscriptum.empty())
+        if (entry.defaults.postscriptum.empty())
             continue;
 
-        if (entry->ifdef.size())
-            fout << "#if " << entry->ifdef << std::endl;
+        if (entry.ifdef.size())
+            fout << "#if " << entry.ifdef << std::endl;
 
-        for (LineList::const_iterator l = entry->defaults.postscriptum.begin(); l != entry->defaults.postscriptum.end(); ++l)
-            fout << "    default_line(\"" << entry->name << " " << *l <<"\");" << std::endl;
+        for (const auto &l : entry.defaults.postscriptum)
+            fout << "    default_line(\"" << entry.name << " " << l <<"\");" << std::endl;
 
-        if (entry->ifdef.size())
+        if (entry.ifdef.size())
             fout << "#endif" << std::endl;
     }
 
-    fout << "}" << std::endl << std::endl;
+    fout << "    cfg_filename = NULL;" << std::endl <<
+         "}" << std::endl << std::endl;
 }
 
 void
 Entry::genParseAlias(const std::string &aName, std::ostream &fout) const
 {
     fout << "    if (!strcmp(token, \"" << aName << "\")) {" << std::endl;
+    if (ifdef.size())
+        fout << "#if " << ifdef << std::endl;
+    fout << "        cfg_directive = \"" << aName << "\";" << std::endl;
     fout << "        ";
     if (type.compare("obsolete") == 0) {
         fout << "debugs(0, DBG_CRITICAL, \"ERROR: Directive '" << aName << "' is obsolete.\");\n";
-        for (LineList::const_iterator l = doc.begin(); l != doc.end(); ++l) {
+        for (const auto &l : doc) {
             // offset line to strip initial whitespace tab byte
-            fout << "        debugs(0, opt_parse_cfg_only?0:1, \"" << aName << " : " << &(*l)[1] << "\");" << std::endl;
+            fout << "        debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), \"" << aName << " : " << &l[1] << "\");" << std::endl;
         }
         fout << "        parse_obsolete(token);";
     } else if (!loc.size() || loc.compare("none") == 0) {
         fout << "parse_" << type << "();";
+    } else if (type.find("::") != std::string::npos) {
+        fout << "ParseDirective<" << type << ">(" << loc << ", LegacyParser);";
     } else {
         fout << "parse_" << type << "(&" << loc << (array_flag ? "[0]" : "") << ");";
     }
     fout << std::endl;
+    fout << "        cfg_directive = NULL;" << std::endl;
+    if (ifdef.size()) {
+        fout <<
+             "#else" << std::endl <<
+             "    debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), \"ERROR: '" << name << "' requires " << available_if(ifdef) << "\");" << std::endl <<
+             "#endif" << std::endl;
+    }
     fout << "        return 1;" << std::endl;
     fout << "    };" << std::endl;
 }
@@ -640,37 +632,32 @@ Entry::genParse(std::ostream &fout) const
     if (name.compare("comment") == 0)
         return;
 
-    if (ifdef.size())
-        fout << "#if " << ifdef << std::endl;
-
     // Once for the current directive name
     genParseAlias(name, fout);
 
     // All accepted aliases
-    for (EntryAliasList::const_iterator a = alias.begin(); a != alias.end(); ++a) {
-        genParseAlias(*a, fout);
+    for (const auto &a : alias) {
+        genParseAlias(a, fout);
     }
-
-    if (ifdef.size())
-        fout << "#endif\n";
 }
 
 static void
 gen_parse(const EntryList &head, std::ostream &fout)
 {
     fout <<
-    "static int\n"
-    "parse_line(char *buff)\n"
-    "{\n"
-    "\tchar\t*token;\n"
-    "\tif ((token = strtok(buff, w_space)) == NULL) \n"
-    "\t\treturn 1;\t/* ignore empty lines */\n";
+         "static int\n"
+         "parse_line(char *buff)\n"
+         "{\n"
+         "\tchar\t*token;\n"
+         "\tif ((token = strtok(buff, w_space)) == NULL) \n"
+         "\t\treturn 1;\t/* ignore empty lines */\n"
+         "\tConfigParser::SetCfgLine(strtok(NULL, \"\"));\n";
 
-    for (EntryList::const_iterator e = head.begin(); e != head.end(); ++e)
-        e->genParse(fout);
+    for (const auto &e : head)
+        e.genParse(fout);
 
     fout << "\treturn 0; /* failure */\n"
-    "}\n\n";
+         "}\n\n";
 
 }
 
@@ -678,25 +665,28 @@ static void
 gen_dump(const EntryList &head, std::ostream &fout)
 {
     fout <<
-    "static void" << std::endl <<
-    "dump_config(StoreEntry *entry)" << std::endl <<
-    "{" << std::endl <<
-    "    debugs(5, 4, HERE);" << std::endl;
+         "static void" << std::endl <<
+         "dump_config(StoreEntry *entry)" << std::endl <<
+         "{" << std::endl <<
+         "    debugs(5, 4, MYNAME);" << std::endl;
 
-    for (EntryList::const_iterator e = head.begin(); e != head.end(); ++e) {
+    for (const auto &e : head) {
 
-        if (!e->loc.size() || e->loc.compare("none") == 0)
+        if (!e.loc.size() || e.loc.compare("none") == 0)
             continue;
 
-        if (e->name.compare("comment") == 0)
+        if (e.name.compare("comment") == 0)
             continue;
 
-        if (e->ifdef.size())
-            fout << "#if " << e->ifdef << std::endl;
+        if (e.ifdef.size())
+            fout << "#if " << e.ifdef << std::endl;
 
-        fout << "    dump_" << e->type << "(entry, \"" << e->name << "\", " << e->loc << ");" << std::endl;
+        if (e.type.find("::") != std::string::npos)
+            fout << "    DumpDirective<" << e.type << ">(" << e.loc << ", entry, \"" << e.name << "\");\n";
+        else
+            fout << "    dump_" << e.type << "(entry, \"" << e.name << "\", " << e.loc << ");" << std::endl;
 
-        if (e->ifdef.size())
+        if (e.ifdef.size())
             fout << "#endif" << std::endl;
     }
 
@@ -707,24 +697,27 @@ static void
 gen_free(const EntryList &head, std::ostream &fout)
 {
     fout <<
-    "static void" << std::endl <<
-    "free_all(void)" << std::endl <<
-    "{" << std::endl <<
-    "    debugs(5, 4, HERE);" << std::endl;
+         "static void" << std::endl <<
+         "free_all(void)" << std::endl <<
+         "{" << std::endl <<
+         "    debugs(5, 4, MYNAME);" << std::endl;
 
-    for (EntryList::const_iterator e = head.begin(); e != head.end(); ++e) {
-        if (!e->loc.size() || e->loc.compare("none") == 0)
+    for (const auto &e : head) {
+        if (!e.loc.size() || e.loc.compare("none") == 0)
             continue;
 
-        if (e->name.compare("comment") == 0)
+        if (e.name.compare("comment") == 0)
             continue;
 
-        if (e->ifdef.size())
-            fout << "#if " << e->ifdef << std::endl;
+        if (e.ifdef.size())
+            fout << "#if " << e.ifdef << std::endl;
 
-        fout << "    free_" << e->type << "(&" << e->loc << (e->array_flag ? "[0]" : "") << ");" << std::endl;
+        if (e.type.find("::") != std::string::npos)
+            fout << "    FreeDirective<" << e.type << ">(" << e.loc << ");\n";
+        else
+            fout << "    free_" << e.type << "(&" << e.loc << (e.array_flag ? "[0]" : "") << ");" << std::endl;
 
-        if (e->ifdef.size())
+        if (e.ifdef.size())
             fout << "#endif" << std::endl;
     }
 
@@ -761,63 +754,63 @@ available_if(const std::string &name)
 static void
 gen_conf(const EntryList &head, std::ostream &fout, bool verbose_output)
 {
-    for (EntryList::const_iterator entry = head.begin(); entry != head.end(); ++entry) {
+    for (const auto &entry : head) {
         char buf[8192];
         LineList def;
         int enabled = 1;
 
         // Display TAG: line
-        if (!entry->name.compare("comment"))
+        if (!entry.name.compare("comment"))
             (void) 0;
-        else if (!entry->name.compare("obsolete"))
+        else if (!entry.name.compare("obsolete"))
             (void) 0;
         else if (verbose_output) {
-            fout << "#  TAG: " << entry->name;
+            fout << "#  TAG: " << entry.name;
 
-            if (entry->comment.size())
-                fout << "\t" << entry->comment;
+            if (entry.comment.size())
+                fout << "\t" << entry.comment;
 
             fout << std::endl;
         }
 
         // Display --enable/--disable disclaimer
-        if (!isDefined(entry->ifdef)) {
+        if (!isDefined(entry.ifdef)) {
             if (verbose_output) {
                 fout << "# Note: This option is only available if Squid is rebuilt with the" << std::endl <<
-                "#       " << available_if(entry->ifdef) << std::endl <<
-                "#" << std::endl;
+                     "#       " << available_if(entry.ifdef) << std::endl <<
+                     "#" << std::endl;
             }
             enabled = 0;
         }
 
         // Display DOC_START section
-        if (verbose_output && entry->doc.size()) {
-            for (LineList::const_iterator line = entry->doc.begin(); line != entry->doc.end(); ++line) {
-                fout << "#" << *line << std::endl;
+        if (verbose_output && entry.doc.size()) {
+            for (const auto &line : entry.doc) {
+                fout << "#" << line << std::endl;
             }
         }
 
-        if (entry->defaults.docs.size()) {
+        if (entry.defaults.docs.size()) {
             // Display the DEFAULT_DOC line(s)
-            def = entry->defaults.docs;
+            def = entry.defaults.docs;
         } else {
-            if (entry->defaults.preset.size() && entry->defaults.preset.front().compare("none") != 0) {
+            if (entry.defaults.preset.size() && entry.defaults.preset.front().compare("none") != 0) {
                 // Display DEFAULT: line(s)
-                for (LineList::const_iterator l = entry->defaults.preset.begin(); l != entry->defaults.preset.end(); ++l) {
-                    snprintf(buf, sizeof(buf), "%s %s", entry->name.c_str(), l->c_str());
+                for (const auto &l : entry.defaults.preset) {
+                    snprintf(buf, sizeof(buf), "%s %s", entry.name.c_str(), l.c_str());
                     def.push_back(buf);
                 }
-            } else if (entry->defaults.if_none.size()) {
+            } else if (entry.defaults.if_none.size()) {
                 // Display DEFAULT_IF_NONE: line(s)
-                for (LineList::const_iterator l = entry->defaults.if_none.begin(); l != entry->defaults.if_none.end(); ++l) {
-                    snprintf(buf, sizeof(buf), "%s %s", entry->name.c_str(), l->c_str());
+                for (const auto &l : entry.defaults.if_none) {
+                    snprintf(buf, sizeof(buf), "%s %s", entry.name.c_str(), l.c_str());
                     def.push_back(buf);
                 }
             }
         }
 
         // Display "none" if no default is set or comments to display
-        if (def.empty() && entry->nocomment.empty() && entry->name.compare("comment") != 0)
+        if (def.empty() && entry.cfgLines.empty() && entry.name.compare("comment") != 0)
             def.push_back("none");
 
         if (verbose_output && def.size()) {
@@ -826,23 +819,44 @@ gen_conf(const EntryList &head, std::ostream &fout, bool verbose_output)
                 fout << "# " << def.front() << std::endl;
                 def.pop_front();
             }
-            if (entry->doc.empty() && entry->nocomment.empty())
+            if (entry.doc.empty() && entry.cfgLines.empty())
                 fout << std::endl;
         }
 
-        if (verbose_output && entry->nocomment.size())
+        if (verbose_output && entry.cfgLines.size())
             fout << "#" << std::endl;
 
         if (enabled || verbose_output) {
-            for (LineList::const_iterator line = entry->nocomment.begin(); line != entry->nocomment.end(); ++line) {
-                if (!enabled && line->at(0) != '#')
+            for (const auto &line : entry.cfgLines) {
+                if (!enabled && line.at(0) != '#')
                     fout << "#";
-                fout << *line << std::endl;
+                fout << line << std::endl;
             }
         }
 
-        if (verbose_output && entry->doc.size()) {
+        if (verbose_output && entry.doc.size()) {
             fout << std::endl;
         }
     }
 }
+
+static const char *
+gen_quote_escape(const std::string &var)
+{
+    static std::string esc;
+    esc.clear();
+
+    for (const auto c : var) {
+        switch (c) {
+        case '"':
+        case '\\':
+            esc += '\\';
+            [[fallthrough]];
+        default:
+            esc += c;
+        }
+    }
+
+    return esc.c_str();
+}
+