]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/cf_gen.cc
2 * Copyright (C) 1996-2016 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 DefaultValues() : preset(), if_none(), docs() {}
71 /// Default config lines to be defined before parsing the config files.
74 /// Default config lines to parse if the directive has no prior settings.
75 /// This is mutually exclusive with preset values.
76 /// An error will be printed during build if they clash.
79 /// Default config lines to parse and add to any prior settings.
80 LineList postscriptum
;
82 /// Text description to use in documentation for the default.
83 /// If unset the preset or if-none values will be displayed.
90 Entry(const char *str
) :
91 name(str
), alias(),type(), loc(),
92 defaults(), comment(), ifdef(), doc(), nocomment(),
100 DefaultValues defaults
;
107 void genParse(std::ostream
&fout
) const;
110 void genParseAlias(const std::string
&, std::ostream
&) const;
113 typedef std::list
<class Entry
> EntryList
;
118 Type(const char *str
) : name(str
) {}
125 typedef std::list
<class Type
> TypeList
;
127 static const char WS
[] = " \t\n";
128 static int gen_default(const EntryList
&, std::ostream
&);
129 static void gen_parse(const EntryList
&, std::ostream
&);
130 static void gen_dump(const EntryList
&, std::ostream
&);
131 static void gen_free(const EntryList
&, std::ostream
&);
132 static void gen_conf(const EntryList
&, std::ostream
&, bool verbose_output
);
133 static void gen_default_if_none(const EntryList
&, std::ostream
&);
134 static void gen_default_postscriptum(const EntryList
&, std::ostream
&);
135 static bool isDefined(const std::string
&name
);
136 static const char *available_if(const std::string
&name
);
137 static const char *gen_quote_escape(const std::string
&var
);
140 checkDepend(const std::string
&directive
, const char *name
, const TypeList
&types
, const EntryList
&entries
)
142 for (TypeList::const_iterator t
= types
.begin(); t
!= types
.end(); ++t
) {
143 if (t
->name
.compare(name
) != 0)
145 for (TypeDepList::const_iterator dep
= t
->depend
.begin(); dep
!= t
->depend
.end(); ++dep
) {
146 EntryList::const_iterator entry
= entries
.begin();
147 for (; entry
!= entries
.end(); ++entry
) {
148 if (entry
->name
.compare(*dep
) == 0)
151 if (entry
== entries
.end()) {
152 std::cerr
<< "ERROR: '" << directive
<< "' (" << name
<< ") depends on '" << *dep
<< "'\n";
158 std::cerr
<< "ERROR: Dependencies for cf.data type '" << name
<< "' used in ' " << directive
<< "' not defined\n" ;
163 usage(const char *program_name
)
165 std::cerr
<< "Usage: " << program_name
<< " cf.data cf.data.depend\n";
170 errorMsg(const char *filename
, int line
, const char *detail
)
172 std::cerr
<< "Error in '" << filename
<< "' on line " << line
<<
173 "--> " << detail
<< std::endl
;
177 main(int argc
, char *argv
[])
179 char *input_filename
;
180 const char *output_filename
= _PATH_PARSER
;
181 const char *conf_filename
= _PATH_SQUID_CONF
;
182 const char *conf_filename_short
= _PATH_SQUID_CONF_SHORT
;
183 const char *type_depend
;
192 std::stack
<std::string
> IFDEFS
;
197 input_filename
= argv
[1];
198 type_depend
= argv
[2];
200 /*-------------------------------------------------------------------*
201 * Parse type dependencies
202 *-------------------------------------------------------------------*/
203 fp
.open(type_depend
, std::ifstream::in
);
205 std::cerr
<< "Error while opening type dependencies file '" <<
206 type_depend
<< "': " << strerror(errno
) << std::endl
;
211 fp
.getline(buff
,MAX_LINE
);
212 const char *type
= strtok(buff
, WS
);
214 if (!type
|| type
[0] == '#')
217 while ((dep
= strtok(NULL
, WS
)) != NULL
) {
218 t
.depend
.push_front(dep
);
223 fp
.clear(); // BSD does not reset flags in close().
225 /*-------------------------------------------------------------------*
227 *-------------------------------------------------------------------*/
229 /* Open input file */
230 fp
.open(input_filename
, std::ifstream::in
);
232 std::cerr
<< "Error while opening input file '" <<
233 input_filename
<< "': " << strerror(errno
) << std::endl
;
239 while (fp
.getline(buff
,MAX_LINE
), fp
.good() && state
!= sEXIT
) {
244 if ((t
= strchr(buff
, '\n')))
247 if (strncmp(buff
, "IF ", 3) == 0) {
248 if ((ptr
= strtok(buff
+ 3, WS
)) == NULL
) {
249 errorMsg(input_filename
, linenum
, "Missing IF parameter");
254 } else if (strcmp(buff
, "ENDIF") == 0) {
255 if (IFDEFS
.size() == 0) {
256 errorMsg(input_filename
, linenum
, "ENDIF without IF first");
260 } else if (!IFDEFS
.size() || isDefined(IFDEFS
.top()))
265 if ((strlen(buff
) == 0) || (!strncmp(buff
, "#", 1))) {
266 /* ignore empty and comment lines */
268 } else if (!strncmp(buff
, "NAME:", 5)) {
269 char *name
, *aliasname
;
271 if ((name
= strtok(buff
+ 5, WS
)) == NULL
) {
272 errorMsg(input_filename
, linenum
, buff
);
276 entries
.push_back(name
);
278 while ((aliasname
= strtok(NULL
, WS
)) != NULL
)
279 entries
.back().alias
.push_front(aliasname
);
282 } else if (!strcmp(buff
, "EOF")) {
284 } else if (!strcmp(buff
, "COMMENT_START")) {
285 entries
.push_back("comment");
286 entries
.back().loc
= "none";
289 errorMsg(input_filename
, linenum
, buff
);
296 Entry
&curr
= entries
.back();
298 if ((strlen(buff
) == 0) || (!strncmp(buff
, "#", 1))) {
299 /* ignore empty and comment lines */
301 } else if (!strncmp(buff
, "COMMENT:", 8)) {
304 while (isspace((unsigned char)*ptr
))
308 } else if (!strncmp(buff
, "DEFAULT:", 8)) {
311 while (isspace((unsigned char)*ptr
))
314 curr
.defaults
.preset
.push_back(ptr
);
315 } else if (!strncmp(buff
, "DEFAULT_IF_NONE:", 16)) {
318 while (isspace((unsigned char)*ptr
))
321 curr
.defaults
.if_none
.push_back(ptr
);
322 } else if (!strncmp(buff
, "POSTSCRIPTUM:", 13)) {
325 while (isspace((unsigned char)*ptr
))
328 curr
.defaults
.postscriptum
.push_back(ptr
);
329 } else if (!strncmp(buff
, "DEFAULT_DOC:", 12)) {
332 while (isspace((unsigned char)*ptr
))
335 curr
.defaults
.docs
.push_back(ptr
);
336 } else if (!strncmp(buff
, "LOC:", 4)) {
337 if ((ptr
= strtok(buff
+ 4, WS
)) == NULL
) {
338 errorMsg(input_filename
, linenum
, buff
);
343 } else if (!strncmp(buff
, "TYPE:", 5)) {
344 if ((ptr
= strtok(buff
+ 5, WS
)) == NULL
) {
345 errorMsg(input_filename
, linenum
, buff
);
349 /* hack to support arrays, rather than pointers */
350 if (0 == strcmp(ptr
+ strlen(ptr
) - 2, "[]")) {
352 *(ptr
+ strlen(ptr
) - 2) = '\0';
355 checkDepend(curr
.name
, ptr
, types
, entries
);
357 } else if (!strncmp(buff
, "IFDEF:", 6)) {
358 if ((ptr
= strtok(buff
+ 6, WS
)) == NULL
) {
359 errorMsg(input_filename
, linenum
, buff
);
364 } else if (!strcmp(buff
, "DOC_START")) {
366 } else if (!strcmp(buff
, "DOC_NONE")) {
369 errorMsg(input_filename
, linenum
, buff
);
376 if (!strcmp(buff
, "DOC_END") || !strcmp(buff
, "COMMENT_END")) {
378 } else if (!strcmp(buff
, "NOCOMMENT_START")) {
380 } else { // if (buff != NULL) {
381 assert(buff
!= NULL
);
382 entries
.back().doc
.push_back(buff
);
387 if (!strcmp(buff
, "NOCOMMENT_END")) {
389 } else { // if (buff != NULL) {
390 assert(buff
!= NULL
);
391 entries
.back().nocomment
.push_back(buff
);
396 assert(0); /* should never get here */
403 if (state
!= sEXIT
) {
404 errorMsg(input_filename
, linenum
, "Error: unexpected EOF");
410 /*-------------------------------------------------------------------*
411 * Generate default_all()
412 * Generate parse_line()
413 * Generate dump_config()
414 * Generate free_all()
415 * Generate example squid.conf.default file
416 *-------------------------------------------------------------------*/
418 /* Open output x.c file */
420 std::ofstream
fout(output_filename
,std::ostream::out
);
422 std::cerr
<< "Error while opening output .c file '" <<
423 output_filename
<< "': " << strerror(errno
) << std::endl
;
428 " * Generated automatically from " << input_filename
<< " by " <<
431 " * Abstract: This file contains routines used to configure the\n"
432 " * variables in the squid server.\n"
436 rc
= gen_default(entries
, fout
);
438 gen_default_if_none(entries
, fout
);
440 gen_default_postscriptum(entries
, fout
);
442 gen_parse(entries
, fout
);
444 gen_dump(entries
, fout
);
446 gen_free(entries
, fout
);
450 /* Open output x.conf file */
451 fout
.open(conf_filename
,std::ostream::out
);
453 std::cerr
<< "Error while opening output conf file '" <<
454 output_filename
<< "': " << strerror(errno
) << std::endl
;
458 gen_conf(entries
, fout
, 1);
462 fout
.open(conf_filename_short
,std::ostream::out
);
464 std::cerr
<< "Error while opening output short conf file '" <<
465 output_filename
<< "': " << strerror(errno
) << std::endl
;
468 gen_conf(entries
, fout
, 0);
475 gen_default(const EntryList
&head
, std::ostream
&fout
)
478 fout
<< "static void" << std::endl
<<
479 "default_line(const char *s)" << std::endl
<<
481 " int len = strlen(s) +1;" << std::endl
<<
482 " char *tmp_line = xstrndup(s, len);" << std::endl
<<
483 " ProcessMacros(tmp_line, len);" << std::endl
<<
484 " xstrncpy(config_input_line, tmp_line, len);" << std::endl
<<
485 " config_lineno++;" << std::endl
<<
486 " parse_line(tmp_line);" << std::endl
<<
487 " xfree(tmp_line);" << std::endl
<<
488 "}" << std::endl
<< std::endl
;
489 fout
<< "static void" << std::endl
<<
490 "default_all(void)" << std::endl
<<
492 " cfg_filename = \"Default Configuration\";" << std::endl
<<
493 " config_lineno = 0;" << std::endl
;
495 for (EntryList::const_iterator entry
= head
.begin(); entry
!= head
.end(); ++entry
) {
496 assert(entry
->name
.size());
498 if (!entry
->name
.compare("comment"))
501 if (!entry
->type
.compare("obsolete"))
504 if (!entry
->loc
.size()) {
505 std::cerr
<< "NO LOCATION FOR " << entry
->name
<< std::endl
;
510 if (!entry
->defaults
.preset
.size() && entry
->defaults
.if_none
.empty()) {
511 std::cerr
<< "NO DEFAULT FOR " << entry
->name
<< std::endl
;
516 if (!entry
->defaults
.preset
.size() || entry
->defaults
.preset
.front().compare("none") == 0) {
517 fout
<< " // No default for " << entry
->name
<< std::endl
;
519 if (entry
->ifdef
.size())
520 fout
<< "#if " << entry
->ifdef
<< std::endl
;
522 for (LineList::const_iterator l
= entry
->defaults
.preset
.begin(); l
!= entry
->defaults
.preset
.end(); ++l
) {
523 fout
<< " default_line(\"" << entry
->name
<< " " << gen_quote_escape(*l
) << "\");" << std::endl
;
526 if (entry
->ifdef
.size())
527 fout
<< "#endif" << std::endl
;
531 fout
<< " cfg_filename = NULL;" << std::endl
<<
532 "}" << std::endl
<< std::endl
;
537 gen_default_if_none(const EntryList
&head
, std::ostream
&fout
)
539 fout
<< "static void" << std::endl
<<
540 "defaults_if_none(void)" << std::endl
<<
542 " cfg_filename = \"Default Configuration (if absent)\";" << std::endl
<<
543 " config_lineno = 0;" << std::endl
;
545 for (EntryList::const_iterator entry
= head
.begin(); entry
!= head
.end(); ++entry
) {
546 assert(entry
->name
.size());
548 if (!entry
->loc
.size())
551 if (entry
->defaults
.if_none
.empty())
554 if (!entry
->defaults
.preset
.empty()) {
555 std::cerr
<< "ERROR: " << entry
->name
<< " has preset defaults. DEFAULT_IF_NONE cannot be true." << std::endl
;
559 if (entry
->ifdef
.size())
560 fout
<< "#if " << entry
->ifdef
<< std::endl
;
562 fout
<< " if (check_null_" << entry
->type
<< "(" << entry
->loc
<< ")) {" << std::endl
;
563 for (LineList::const_iterator l
= entry
->defaults
.if_none
.begin(); l
!= entry
->defaults
.if_none
.end(); ++l
)
564 fout
<< " default_line(\"" << entry
->name
<< " " << gen_quote_escape(*l
) <<"\");" << std::endl
;
565 fout
<< " }" << std::endl
;
567 if (entry
->ifdef
.size())
568 fout
<< "#endif" << std::endl
;
571 fout
<< " cfg_filename = NULL;" << std::endl
<<
572 "}" << std::endl
<< std::endl
;
575 /// append configuration options specified by POSTSCRIPTUM lines
577 gen_default_postscriptum(const EntryList
&head
, std::ostream
&fout
)
579 fout
<< "static void" << std::endl
<<
580 "defaults_postscriptum(void)" << std::endl
<<
582 " cfg_filename = \"Default Configuration (postscriptum)\";" << std::endl
<<
583 " config_lineno = 0;" << std::endl
;
585 for (EntryList::const_iterator entry
= head
.begin(); entry
!= head
.end(); ++entry
) {
586 assert(entry
->name
.size());
588 if (!entry
->loc
.size())
591 if (entry
->defaults
.postscriptum
.empty())
594 if (entry
->ifdef
.size())
595 fout
<< "#if " << entry
->ifdef
<< std::endl
;
597 for (LineList::const_iterator l
= entry
->defaults
.postscriptum
.begin(); l
!= entry
->defaults
.postscriptum
.end(); ++l
)
598 fout
<< " default_line(\"" << entry
->name
<< " " << *l
<<"\");" << std::endl
;
600 if (entry
->ifdef
.size())
601 fout
<< "#endif" << std::endl
;
604 fout
<< " cfg_filename = NULL;" << std::endl
<<
605 "}" << std::endl
<< std::endl
;
609 Entry::genParseAlias(const std::string
&aName
, std::ostream
&fout
) const
611 fout
<< " if (!strcmp(token, \"" << aName
<< "\")) {" << std::endl
;
613 fout
<< "#if " << ifdef
<< std::endl
;
614 fout
<< " cfg_directive = \"" << aName
<< "\";" << std::endl
;
616 if (type
.compare("obsolete") == 0) {
617 fout
<< "debugs(0, DBG_CRITICAL, \"ERROR: Directive '" << aName
<< "' is obsolete.\");\n";
618 for (LineList::const_iterator l
= doc
.begin(); l
!= doc
.end(); ++l
) {
619 // offset line to strip initial whitespace tab byte
620 fout
<< " debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), \"" << aName
<< " : " << &(*l
)[1] << "\");" << std::endl
;
622 fout
<< " parse_obsolete(token);";
623 } else if (!loc
.size() || loc
.compare("none") == 0) {
624 fout
<< "parse_" << type
<< "();";
626 fout
<< "parse_" << type
<< "(&" << loc
<< (array_flag
? "[0]" : "") << ");";
629 fout
<< " cfg_directive = NULL;" << std::endl
;
632 "#else" << std::endl
<<
633 " debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), \"ERROR: '" << name
<< "' requires " << available_if(ifdef
) << "\");" << std::endl
<<
634 "#endif" << std::endl
;
636 fout
<< " return 1;" << std::endl
;
637 fout
<< " };" << std::endl
;
641 Entry::genParse(std::ostream
&fout
) const
643 if (name
.compare("comment") == 0)
646 // Once for the current directive name
647 genParseAlias(name
, fout
);
649 // All accepted aliases
650 for (EntryAliasList::const_iterator a
= alias
.begin(); a
!= alias
.end(); ++a
) {
651 genParseAlias(*a
, fout
);
656 gen_parse(const EntryList
&head
, std::ostream
&fout
)
660 "parse_line(char *buff)\n"
663 "\tif ((token = strtok(buff, w_space)) == NULL) \n"
664 "\t\treturn 1;\t/* ignore empty lines */\n"
665 "\tConfigParser::SetCfgLine(strtok(NULL, \"\"));\n";
667 for (EntryList::const_iterator e
= head
.begin(); e
!= head
.end(); ++e
)
670 fout
<< "\treturn 0; /* failure */\n"
676 gen_dump(const EntryList
&head
, std::ostream
&fout
)
679 "static void" << std::endl
<<
680 "dump_config(StoreEntry *entry)" << std::endl
<<
682 " debugs(5, 4, HERE);" << std::endl
;
684 for (EntryList::const_iterator e
= head
.begin(); e
!= head
.end(); ++e
) {
686 if (!e
->loc
.size() || e
->loc
.compare("none") == 0)
689 if (e
->name
.compare("comment") == 0)
693 fout
<< "#if " << e
->ifdef
<< std::endl
;
695 fout
<< " dump_" << e
->type
<< "(entry, \"" << e
->name
<< "\", " << e
->loc
<< ");" << std::endl
;
698 fout
<< "#endif" << std::endl
;
701 fout
<< "}" << std::endl
<< std::endl
;
705 gen_free(const EntryList
&head
, std::ostream
&fout
)
708 "static void" << std::endl
<<
709 "free_all(void)" << std::endl
<<
711 " debugs(5, 4, HERE);" << std::endl
;
713 for (EntryList::const_iterator e
= head
.begin(); e
!= head
.end(); ++e
) {
714 if (!e
->loc
.size() || e
->loc
.compare("none") == 0)
717 if (e
->name
.compare("comment") == 0)
721 fout
<< "#if " << e
->ifdef
<< std::endl
;
723 fout
<< " free_" << e
->type
<< "(&" << e
->loc
<< (e
->array_flag
? "[0]" : "") << ");" << std::endl
;
726 fout
<< "#endif" << std::endl
;
729 fout
<< "}" << std::endl
<< std::endl
;
733 isDefined(const std::string
&name
)
738 for (int i
= 0; defines
[i
].name
; ++i
) {
739 if (name
.compare(defines
[i
].name
) == 0)
740 return defines
[i
].defined
;
747 available_if(const std::string
&name
)
751 for (int i
= 0; defines
[i
].name
; ++i
) {
752 if (name
.compare(defines
[i
].name
) == 0)
753 return defines
[i
].enable
;
760 gen_conf(const EntryList
&head
, std::ostream
&fout
, bool verbose_output
)
762 for (EntryList::const_iterator entry
= head
.begin(); entry
!= head
.end(); ++entry
) {
768 if (!entry
->name
.compare("comment"))
770 else if (!entry
->name
.compare("obsolete"))
772 else if (verbose_output
) {
773 fout
<< "# TAG: " << entry
->name
;
775 if (entry
->comment
.size())
776 fout
<< "\t" << entry
->comment
;
781 // Display --enable/--disable disclaimer
782 if (!isDefined(entry
->ifdef
)) {
783 if (verbose_output
) {
784 fout
<< "# Note: This option is only available if Squid is rebuilt with the" << std::endl
<<
785 "# " << available_if(entry
->ifdef
) << std::endl
<<
791 // Display DOC_START section
792 if (verbose_output
&& entry
->doc
.size()) {
793 for (LineList::const_iterator line
= entry
->doc
.begin(); line
!= entry
->doc
.end(); ++line
) {
794 fout
<< "#" << *line
<< std::endl
;
798 if (entry
->defaults
.docs
.size()) {
799 // Display the DEFAULT_DOC line(s)
800 def
= entry
->defaults
.docs
;
802 if (entry
->defaults
.preset
.size() && entry
->defaults
.preset
.front().compare("none") != 0) {
803 // Display DEFAULT: line(s)
804 for (LineList::const_iterator l
= entry
->defaults
.preset
.begin(); l
!= entry
->defaults
.preset
.end(); ++l
) {
805 snprintf(buf
, sizeof(buf
), "%s %s", entry
->name
.c_str(), l
->c_str());
808 } else if (entry
->defaults
.if_none
.size()) {
809 // Display DEFAULT_IF_NONE: line(s)
810 for (LineList::const_iterator l
= entry
->defaults
.if_none
.begin(); l
!= entry
->defaults
.if_none
.end(); ++l
) {
811 snprintf(buf
, sizeof(buf
), "%s %s", entry
->name
.c_str(), l
->c_str());
817 // Display "none" if no default is set or comments to display
818 if (def
.empty() && entry
->nocomment
.empty() && entry
->name
.compare("comment") != 0)
819 def
.push_back("none");
821 if (verbose_output
&& def
.size()) {
822 fout
<< "#Default:\n";
824 fout
<< "# " << def
.front() << std::endl
;
827 if (entry
->doc
.empty() && entry
->nocomment
.empty())
831 if (verbose_output
&& entry
->nocomment
.size())
832 fout
<< "#" << std::endl
;
834 if (enabled
|| verbose_output
) {
835 for (LineList::const_iterator line
= entry
->nocomment
.begin(); line
!= entry
->nocomment
.end(); ++line
) {
836 if (!enabled
&& line
->at(0) != '#')
838 fout
<< *line
<< std::endl
;
842 if (verbose_output
&& entry
->doc
.size()) {
849 gen_quote_escape(const std::string
&var
)
851 static std::string esc
;
854 for (int i
= 0; i
< var
.length(); ++i
) {