]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/cf_gen.cc
2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9 /* DEBUG: none Generate squid.conf.default and cf_parser.cci */
11 /*****************************************************************************
12 * Abstract: This program parses the input file and generates code and
13 * files used to configure the variables in squid.
14 * (ie it creates the squid.conf.default file from the cf.data file)
16 * The output files are as follows:
17 * cf_parser.cci - this file contains, default_all() which
18 * initializes variables with the default
19 * values, parse_line() that parses line from
20 * squid.conf.default, dump_config that dumps the
21 * current the values of the variables.
22 * squid.conf.default - default configuration file given to the server
24 *****************************************************************************/
27 * hack around a bug in intel's c++ compiler's libraries which do not
28 * correctly support 64-bit iostreams
30 #if defined(__INTEL_COMPILER) && defined(_FILE_OFFSET_BITS) && \
32 #undef _FILE_OFFSET_BITS
45 #include "cf_gen_defines.cci"
47 #define MAX_LINE 1024 /* longest configuration line */
48 #define _PATH_PARSER "cf_parser.cci"
49 #define _PATH_SQUID_CONF "squid.conf.documented"
50 #define _PATH_SQUID_CONF_SHORT "squid.conf.default"
51 #define _PATH_CF_DEPEND "cf.data.depend"
61 typedef std::list
<std::string
> LineList
;
62 typedef std::list
<std::string
> TypeDepList
;
63 typedef std::list
<std::string
> EntryAliasList
;
68 /// Default config lines to be defined before parsing the config files.
71 /// Default config lines to parse if the directive has no prior settings.
72 /// This is mutually exclusive with preset values.
73 /// An error will be printed during build if they clash.
76 /// Default config lines to parse and add to any prior settings.
77 LineList postscriptum
;
79 /// Text description to use in documentation for the default.
80 /// If unset the preset or if-none values will be displayed.
87 explicit Entry(const char *str
) : name(str
) {}
93 DefaultValues defaults
;
97 LineList cfgLines
; ///< between CONFIG_START and CONFIG_END
98 int array_flag
= 0; ///< TYPE is a raw array[] declaration
100 void genParse(std::ostream
&fout
) const;
103 void genParseAlias(const std::string
&, std::ostream
&) const;
106 typedef std::list
<class Entry
> EntryList
;
111 Type(const char *str
) : name(str
) {}
117 typedef std::list
<class Type
> TypeList
;
119 static const char WS
[] = " \t\n";
120 static int gen_default(const EntryList
&, std::ostream
&);
121 static void gen_parse(const EntryList
&, std::ostream
&);
122 static void gen_dump(const EntryList
&, std::ostream
&);
123 static void gen_free(const EntryList
&, std::ostream
&);
124 static void gen_conf(const EntryList
&, std::ostream
&, bool verbose_output
);
125 static void gen_default_if_none(const EntryList
&, std::ostream
&);
126 static void gen_default_postscriptum(const EntryList
&, std::ostream
&);
127 static bool isDefined(const std::string
&name
);
128 static const char *available_if(const std::string
&name
);
129 static const char *gen_quote_escape(const std::string
&var
);
132 checkDepend(const std::string
&directive
, const char *name
, const TypeList
&types
, const EntryList
&entries
)
134 for (const auto &t
: types
) {
135 if (t
.name
.compare(name
) != 0)
137 for (const auto &dep
: t
.depend
) {
138 EntryList::const_iterator entry
= entries
.begin();
139 for (; entry
!= entries
.end(); ++entry
) {
140 if (entry
->name
.compare(dep
) == 0)
143 if (entry
== entries
.end()) {
144 std::cerr
<< "ERROR: '" << directive
<< "' (" << name
<< ") depends on '" << dep
<< "'\n";
150 std::cerr
<< "ERROR: Dependencies for cf.data type '" << name
<< "' used in ' " << directive
<< "' not defined\n" ;
155 usage(const char *program_name
)
157 std::cerr
<< "Usage: " << program_name
<< " cf.data cf.data.depend\n";
162 errorMsg(const char *filename
, int line
, const char *detail
)
164 std::cerr
<< "Error in '" << filename
<< "' on line " << line
<<
165 "--> " << detail
<< std::endl
;
169 main(int argc
, char *argv
[])
171 char *input_filename
;
172 const char *output_filename
= _PATH_PARSER
;
173 const char *conf_filename
= _PATH_SQUID_CONF
;
174 const char *conf_filename_short
= _PATH_SQUID_CONF_SHORT
;
175 const char *type_depend
;
184 std::stack
<std::string
> IFDEFS
;
189 input_filename
= argv
[1];
190 type_depend
= argv
[2];
192 /*-------------------------------------------------------------------*
193 * Parse type dependencies
194 *-------------------------------------------------------------------*/
195 fp
.open(type_depend
, std::ifstream::in
);
197 std::cerr
<< "Error while opening type dependencies file '" <<
198 type_depend
<< "': " << strerror(errno
) << std::endl
;
203 fp
.getline(buff
,MAX_LINE
);
204 const char *type
= strtok(buff
, WS
);
206 if (!type
|| type
[0] == '#')
209 while ((dep
= strtok(NULL
, WS
)) != NULL
) {
210 t
.depend
.push_front(dep
);
215 fp
.clear(); // BSD does not reset flags in close().
217 /*-------------------------------------------------------------------*
219 *-------------------------------------------------------------------*/
221 /* Open input file */
222 fp
.open(input_filename
, std::ifstream::in
);
224 std::cerr
<< "Error while opening input file '" <<
225 input_filename
<< "': " << strerror(errno
) << std::endl
;
231 while (fp
.getline(buff
,MAX_LINE
), fp
.good() && state
!= sEXIT
) {
236 if ((t
= strchr(buff
, '\n')))
239 if (strncmp(buff
, "IF ", 3) == 0) {
240 if ((ptr
= strtok(buff
+ 3, WS
)) == NULL
) {
241 errorMsg(input_filename
, linenum
, "Missing IF parameter");
246 } else if (strcmp(buff
, "ENDIF") == 0) {
247 if (IFDEFS
.size() == 0) {
248 errorMsg(input_filename
, linenum
, "ENDIF without IF first");
252 } else if (!IFDEFS
.size() || isDefined(IFDEFS
.top()))
257 if ((strlen(buff
) == 0) || (!strncmp(buff
, "#", 1))) {
258 /* ignore empty and comment lines */
260 } else if (!strncmp(buff
, "NAME:", 5)) {
261 char *name
, *aliasname
;
263 if ((name
= strtok(buff
+ 5, WS
)) == NULL
) {
264 errorMsg(input_filename
, linenum
, buff
);
268 entries
.emplace_back(name
);
270 while ((aliasname
= strtok(NULL
, WS
)) != NULL
)
271 entries
.back().alias
.push_front(aliasname
);
274 } else if (!strcmp(buff
, "EOF")) {
276 } else if (!strcmp(buff
, "COMMENT_START")) {
277 entries
.emplace_back("comment");
278 entries
.back().loc
= "none";
281 errorMsg(input_filename
, linenum
, buff
);
288 Entry
&curr
= entries
.back();
290 if ((strlen(buff
) == 0) || (!strncmp(buff
, "#", 1))) {
291 /* ignore empty and comment lines */
293 } else if (!strncmp(buff
, "COMMENT:", 8)) {
296 while (isspace((unsigned char)*ptr
))
300 } else if (!strncmp(buff
, "DEFAULT:", 8)) {
303 while (isspace((unsigned char)*ptr
))
306 curr
.defaults
.preset
.push_back(ptr
);
307 } else if (!strncmp(buff
, "DEFAULT_IF_NONE:", 16)) {
310 while (isspace((unsigned char)*ptr
))
313 curr
.defaults
.if_none
.push_back(ptr
);
314 } else if (!strncmp(buff
, "POSTSCRIPTUM:", 13)) {
317 while (isspace((unsigned char)*ptr
))
320 curr
.defaults
.postscriptum
.push_back(ptr
);
321 } else if (!strncmp(buff
, "DEFAULT_DOC:", 12)) {
324 while (isspace((unsigned char)*ptr
))
327 curr
.defaults
.docs
.push_back(ptr
);
328 } else if (!strncmp(buff
, "LOC:", 4)) {
329 if ((ptr
= strtok(buff
+ 4, WS
)) == NULL
) {
330 errorMsg(input_filename
, linenum
, buff
);
335 } else if (!strncmp(buff
, "TYPE:", 5)) {
336 if ((ptr
= strtok(buff
+ 5, WS
)) == NULL
) {
337 errorMsg(input_filename
, linenum
, buff
);
341 /* hack to support arrays, rather than pointers */
342 if (strcmp(ptr
+ strlen(ptr
) - 2, "[]") == 0) {
344 *(ptr
+ strlen(ptr
) - 2) = '\0';
347 checkDepend(curr
.name
, ptr
, types
, entries
);
349 } else if (!strncmp(buff
, "IFDEF:", 6)) {
350 if ((ptr
= strtok(buff
+ 6, WS
)) == NULL
) {
351 errorMsg(input_filename
, linenum
, buff
);
356 } else if (!strcmp(buff
, "DOC_START")) {
358 } else if (!strcmp(buff
, "DOC_NONE")) {
361 errorMsg(input_filename
, linenum
, buff
);
368 if (!strcmp(buff
, "DOC_END") || !strcmp(buff
, "COMMENT_END")) {
370 } else if (strcmp(buff
, "CONFIG_START") == 0) {
372 } else { // if (buff != NULL) {
373 assert(buff
!= NULL
);
374 entries
.back().doc
.push_back(buff
);
379 if (strcmp(buff
, "CONFIG_END") == 0) {
381 } else { // if (buff != NULL) {
382 assert(buff
!= NULL
);
383 entries
.back().cfgLines
.push_back(buff
);
388 assert(0); /* should never get here */
395 if (state
!= sEXIT
) {
396 errorMsg(input_filename
, linenum
, "Error: unexpected EOF");
402 /*-------------------------------------------------------------------*
403 * Generate default_all()
404 * Generate parse_line()
405 * Generate dump_config()
406 * Generate free_all()
407 * Generate example squid.conf.default file
408 *-------------------------------------------------------------------*/
410 /* Open output x.c file */
412 std::ofstream
fout(output_filename
,std::ostream::out
);
414 std::cerr
<< "Error while opening output .c file '" <<
415 output_filename
<< "': " << strerror(errno
) << std::endl
;
420 " * Generated automatically from " << input_filename
<< " by " <<
423 " * Abstract: This file contains routines used to configure the\n"
424 " * variables in the squid server.\n"
428 rc
= gen_default(entries
, fout
);
430 gen_default_if_none(entries
, fout
);
432 gen_default_postscriptum(entries
, fout
);
434 gen_parse(entries
, fout
);
436 gen_dump(entries
, fout
);
438 gen_free(entries
, fout
);
442 /* Open output x.conf file */
443 fout
.open(conf_filename
,std::ostream::out
);
445 std::cerr
<< "Error while opening output conf file '" <<
446 output_filename
<< "': " << strerror(errno
) << std::endl
;
450 gen_conf(entries
, fout
, 1);
454 fout
.open(conf_filename_short
,std::ostream::out
);
456 std::cerr
<< "Error while opening output short conf file '" <<
457 output_filename
<< "': " << strerror(errno
) << std::endl
;
460 gen_conf(entries
, fout
, 0);
467 gen_default(const EntryList
&head
, std::ostream
&fout
)
470 fout
<< "static void" << std::endl
<<
471 "default_line(const char *s)" << std::endl
<<
473 " char *tmp_line = xstrdup(s);" << std::endl
<<
474 " int len = strlen(tmp_line);" << std::endl
<<
475 " ProcessMacros(tmp_line, len);" << std::endl
<<
476 " xstrncpy(config_input_line, tmp_line, sizeof(config_input_line));" << std::endl
<<
477 " config_lineno++;" << std::endl
<<
478 " parse_line(tmp_line);" << std::endl
<<
479 " xfree(tmp_line);" << std::endl
<<
480 "}" << std::endl
<< std::endl
;
481 fout
<< "static void" << std::endl
<<
482 "default_all(void)" << std::endl
<<
484 " cfg_filename = \"Default Configuration\";" << std::endl
<<
485 " config_lineno = 0;" << std::endl
;
487 for (const auto &entry
: head
) {
488 assert(entry
.name
.size());
490 if (!entry
.name
.compare("comment"))
493 if (!entry
.type
.compare("obsolete"))
496 if (!entry
.loc
.size()) {
497 std::cerr
<< "NO LOCATION FOR " << entry
.name
<< std::endl
;
502 if (!entry
.defaults
.preset
.size() && entry
.defaults
.if_none
.empty()) {
503 std::cerr
<< "NO DEFAULT FOR " << entry
.name
<< std::endl
;
508 if (!entry
.defaults
.preset
.size() || entry
.defaults
.preset
.front().compare("none") == 0) {
509 fout
<< " // No default for " << entry
.name
<< std::endl
;
511 if (entry
.ifdef
.size())
512 fout
<< "#if " << entry
.ifdef
<< std::endl
;
514 for (const auto &l
: entry
.defaults
.preset
)
515 fout
<< " default_line(\"" << entry
.name
<< " " << gen_quote_escape(l
) << "\");" << std::endl
;
517 if (entry
.ifdef
.size())
518 fout
<< "#endif" << std::endl
;
522 fout
<< " cfg_filename = NULL;" << std::endl
<<
523 "}" << std::endl
<< std::endl
;
528 gen_default_if_none(const EntryList
&head
, std::ostream
&fout
)
530 fout
<< "static void" << std::endl
<<
531 "defaults_if_none(void)" << std::endl
<<
533 " cfg_filename = \"Default Configuration (if absent)\";" << std::endl
<<
534 " config_lineno = 0;" << std::endl
;
536 for (const auto &entry
: head
) {
537 assert(entry
.name
.size());
539 if (!entry
.loc
.size())
542 if (entry
.defaults
.if_none
.empty())
545 if (!entry
.defaults
.preset
.empty()) {
546 std::cerr
<< "ERROR: " << entry
.name
<< " has preset defaults. DEFAULT_IF_NONE cannot be true." << std::endl
;
550 if (entry
.ifdef
.size())
551 fout
<< "#if " << entry
.ifdef
<< std::endl
;
553 fout
<< " if (check_null_" << entry
.type
<< "(" << entry
.loc
<< ")) {" << std::endl
;
554 for (const auto &l
: entry
.defaults
.if_none
)
555 fout
<< " default_line(\"" << entry
.name
<< " " << gen_quote_escape(l
) <<"\");" << std::endl
;
556 fout
<< " }" << std::endl
;
558 if (entry
.ifdef
.size())
559 fout
<< "#endif" << std::endl
;
562 fout
<< " cfg_filename = NULL;" << std::endl
<<
563 "}" << std::endl
<< std::endl
;
566 /// append configuration options specified by POSTSCRIPTUM lines
568 gen_default_postscriptum(const EntryList
&head
, std::ostream
&fout
)
570 fout
<< "static void" << std::endl
<<
571 "defaults_postscriptum(void)" << std::endl
<<
573 " cfg_filename = \"Default Configuration (postscriptum)\";" << std::endl
<<
574 " config_lineno = 0;" << std::endl
;
576 for (const auto &entry
: head
) {
577 assert(entry
.name
.size());
579 if (!entry
.loc
.size())
582 if (entry
.defaults
.postscriptum
.empty())
585 if (entry
.ifdef
.size())
586 fout
<< "#if " << entry
.ifdef
<< std::endl
;
588 for (const auto &l
: entry
.defaults
.postscriptum
)
589 fout
<< " default_line(\"" << entry
.name
<< " " << l
<<"\");" << std::endl
;
591 if (entry
.ifdef
.size())
592 fout
<< "#endif" << std::endl
;
595 fout
<< " cfg_filename = NULL;" << std::endl
<<
596 "}" << std::endl
<< std::endl
;
600 Entry::genParseAlias(const std::string
&aName
, std::ostream
&fout
) const
602 fout
<< " if (!strcmp(token, \"" << aName
<< "\")) {" << std::endl
;
604 fout
<< "#if " << ifdef
<< std::endl
;
605 fout
<< " cfg_directive = \"" << aName
<< "\";" << std::endl
;
607 if (type
.compare("obsolete") == 0) {
608 fout
<< "debugs(0, DBG_CRITICAL, \"ERROR: Directive '" << aName
<< "' is obsolete.\");\n";
609 for (const auto &l
: doc
) {
610 // offset line to strip initial whitespace tab byte
611 fout
<< " debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), \"" << aName
<< " : " << &l
[1] << "\");" << std::endl
;
613 fout
<< " parse_obsolete(token);";
614 } else if (!loc
.size() || loc
.compare("none") == 0) {
615 fout
<< "parse_" << type
<< "();";
617 fout
<< "parse_" << type
<< "(&" << loc
<< (array_flag
? "[0]" : "") << ");";
620 fout
<< " cfg_directive = NULL;" << std::endl
;
623 "#else" << std::endl
<<
624 " debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), \"ERROR: '" << name
<< "' requires " << available_if(ifdef
) << "\");" << std::endl
<<
625 "#endif" << std::endl
;
627 fout
<< " return 1;" << std::endl
;
628 fout
<< " };" << std::endl
;
632 Entry::genParse(std::ostream
&fout
) const
634 if (name
.compare("comment") == 0)
637 // Once for the current directive name
638 genParseAlias(name
, fout
);
640 // All accepted aliases
641 for (const auto &a
: alias
) {
642 genParseAlias(a
, fout
);
647 gen_parse(const EntryList
&head
, std::ostream
&fout
)
651 "parse_line(char *buff)\n"
654 "\tif ((token = strtok(buff, w_space)) == NULL) \n"
655 "\t\treturn 1;\t/* ignore empty lines */\n"
656 "\tConfigParser::SetCfgLine(strtok(NULL, \"\"));\n";
658 for (const auto &e
: head
)
661 fout
<< "\treturn 0; /* failure */\n"
667 gen_dump(const EntryList
&head
, std::ostream
&fout
)
670 "static void" << std::endl
<<
671 "dump_config(StoreEntry *entry)" << std::endl
<<
673 " debugs(5, 4, HERE);" << std::endl
;
675 for (const auto &e
: head
) {
677 if (!e
.loc
.size() || e
.loc
.compare("none") == 0)
680 if (e
.name
.compare("comment") == 0)
684 fout
<< "#if " << e
.ifdef
<< std::endl
;
686 fout
<< " dump_" << e
.type
<< "(entry, \"" << e
.name
<< "\", " << e
.loc
<< ");" << std::endl
;
689 fout
<< "#endif" << std::endl
;
692 fout
<< "}" << std::endl
<< std::endl
;
696 gen_free(const EntryList
&head
, std::ostream
&fout
)
699 "static void" << std::endl
<<
700 "free_all(void)" << std::endl
<<
702 " debugs(5, 4, HERE);" << std::endl
;
704 for (const auto &e
: head
) {
705 if (!e
.loc
.size() || e
.loc
.compare("none") == 0)
708 if (e
.name
.compare("comment") == 0)
712 fout
<< "#if " << e
.ifdef
<< std::endl
;
714 fout
<< " free_" << e
.type
<< "(&" << e
.loc
<< (e
.array_flag
? "[0]" : "") << ");" << std::endl
;
717 fout
<< "#endif" << std::endl
;
720 fout
<< "}" << std::endl
<< std::endl
;
724 isDefined(const std::string
&name
)
729 for (int i
= 0; defines
[i
].name
; ++i
) {
730 if (name
.compare(defines
[i
].name
) == 0)
731 return defines
[i
].defined
;
738 available_if(const std::string
&name
)
742 for (int i
= 0; defines
[i
].name
; ++i
) {
743 if (name
.compare(defines
[i
].name
) == 0)
744 return defines
[i
].enable
;
751 gen_conf(const EntryList
&head
, std::ostream
&fout
, bool verbose_output
)
753 for (const auto &entry
: head
) {
759 if (!entry
.name
.compare("comment"))
761 else if (!entry
.name
.compare("obsolete"))
763 else if (verbose_output
) {
764 fout
<< "# TAG: " << entry
.name
;
766 if (entry
.comment
.size())
767 fout
<< "\t" << entry
.comment
;
772 // Display --enable/--disable disclaimer
773 if (!isDefined(entry
.ifdef
)) {
774 if (verbose_output
) {
775 fout
<< "# Note: This option is only available if Squid is rebuilt with the" << std::endl
<<
776 "# " << available_if(entry
.ifdef
) << std::endl
<<
782 // Display DOC_START section
783 if (verbose_output
&& entry
.doc
.size()) {
784 for (const auto &line
: entry
.doc
) {
785 fout
<< "#" << line
<< std::endl
;
789 if (entry
.defaults
.docs
.size()) {
790 // Display the DEFAULT_DOC line(s)
791 def
= entry
.defaults
.docs
;
793 if (entry
.defaults
.preset
.size() && entry
.defaults
.preset
.front().compare("none") != 0) {
794 // Display DEFAULT: line(s)
795 for (const auto &l
: entry
.defaults
.preset
) {
796 snprintf(buf
, sizeof(buf
), "%s %s", entry
.name
.c_str(), l
.c_str());
799 } else if (entry
.defaults
.if_none
.size()) {
800 // Display DEFAULT_IF_NONE: line(s)
801 for (const auto &l
: entry
.defaults
.if_none
) {
802 snprintf(buf
, sizeof(buf
), "%s %s", entry
.name
.c_str(), l
.c_str());
808 // Display "none" if no default is set or comments to display
809 if (def
.empty() && entry
.cfgLines
.empty() && entry
.name
.compare("comment") != 0)
810 def
.push_back("none");
812 if (verbose_output
&& def
.size()) {
813 fout
<< "#Default:\n";
815 fout
<< "# " << def
.front() << std::endl
;
818 if (entry
.doc
.empty() && entry
.cfgLines
.empty())
822 if (verbose_output
&& entry
.cfgLines
.size())
823 fout
<< "#" << std::endl
;
825 if (enabled
|| verbose_output
) {
826 for (const auto &line
: entry
.cfgLines
) {
827 if (!enabled
&& line
.at(0) != '#')
829 fout
<< line
<< std::endl
;
833 if (verbose_output
&& entry
.doc
.size()) {
840 gen_quote_escape(const std::string
&var
)
842 static std::string esc
;
845 for (int i
= 0; i
< var
.length(); ++i
) {