]> git.ipfire.org Git - thirdparty/squid.git/blob - src/cf_gen.cc
Source Format Enforcement (#763)
[thirdparty/squid.git] / src / cf_gen.cc
1 /*
2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
3 *
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.
7 */
8
9 /* DEBUG: none Generate squid.conf.default and cf_parser.cci */
10
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)
15 *
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
23 * administrator.
24 *****************************************************************************/
25
26 /*
27 * hack around a bug in intel's c++ compiler's libraries which do not
28 * correctly support 64-bit iostreams
29 */
30 #if defined(__INTEL_COMPILER) && defined(_FILE_OFFSET_BITS) && \
31 _FILE_OFFSET_BITS==64
32 #undef _FILE_OFFSET_BITS
33 #endif
34
35 #include <cassert>
36 #include <cctype>
37 #include <cerrno>
38 #include <cstdlib>
39 #include <cstring>
40 #include <fstream>
41 #include <iostream>
42 #include <list>
43 #include <stack>
44
45 #include "cf_gen_defines.cci"
46
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"
52
53 enum State {
54 sSTART,
55 s1,
56 sDOC,
57 sCFGLINES,
58 sEXIT
59 };
60
61 typedef std::list<std::string> LineList;
62 typedef std::list<std::string> TypeDepList;
63 typedef std::list<std::string> EntryAliasList;
64
65 class DefaultValues
66 {
67 public:
68 /// Default config lines to be defined before parsing the config files.
69 LineList preset;
70
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.
74 LineList if_none;
75
76 /// Default config lines to parse and add to any prior settings.
77 LineList postscriptum;
78
79 /// Text description to use in documentation for the default.
80 /// If unset the preset or if-none values will be displayed.
81 LineList docs;
82 };
83
84 class Entry
85 {
86 public:
87 explicit Entry(const char *str) : name(str) {}
88
89 std::string name;
90 EntryAliasList alias;
91 std::string type;
92 std::string loc;
93 DefaultValues defaults;
94 std::string comment;
95 std::string ifdef;
96 LineList doc;
97 LineList cfgLines; ///< between CONFIG_START and CONFIG_END
98 int array_flag = 0; ///< TYPE is a raw array[] declaration
99
100 void genParse(std::ostream &fout) const;
101
102 private:
103 void genParseAlias(const std::string &, std::ostream &) const;
104 };
105
106 typedef std::list<class Entry> EntryList;
107
108 class Type
109 {
110 public:
111 Type(const char *str) : name(str) {}
112
113 std::string name;
114 TypeDepList depend;
115 };
116
117 typedef std::list<class Type> TypeList;
118
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);
130
131 static void
132 checkDepend(const std::string &directive, const char *name, const TypeList &types, const EntryList &entries)
133 {
134 for (const auto &t : types) {
135 if (t.name.compare(name) != 0)
136 continue;
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)
141 break;
142 }
143 if (entry == entries.end()) {
144 std::cerr << "ERROR: '" << directive << "' (" << name << ") depends on '" << dep << "'\n";
145 exit(EXIT_FAILURE);
146 }
147 }
148 return;
149 }
150 std::cerr << "ERROR: Dependencies for cf.data type '" << name << "' used in ' " << directive << "' not defined\n" ;
151 exit(EXIT_FAILURE);
152 }
153
154 static void
155 usage(const char *program_name)
156 {
157 std::cerr << "Usage: " << program_name << " cf.data cf.data.depend\n";
158 exit(EXIT_FAILURE);
159 }
160
161 static void
162 errorMsg(const char *filename, int line, const char *detail)
163 {
164 std::cerr << "Error in '" << filename << "' on line " << line <<
165 "--> " << detail << std::endl;
166 }
167
168 int
169 main(int argc, char *argv[])
170 {
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;
176 int linenum = 0;
177 EntryList entries;
178 TypeList types;
179 enum State state;
180 int rc = 0;
181 char *ptr = NULL;
182 char buff[MAX_LINE];
183 std::ifstream fp;
184 std::stack<std::string> IFDEFS;
185
186 if (argc != 3)
187 usage(argv[0]);
188
189 input_filename = argv[1];
190 type_depend = argv[2];
191
192 /*-------------------------------------------------------------------*
193 * Parse type dependencies
194 *-------------------------------------------------------------------*/
195 fp.open(type_depend, std::ifstream::in);
196 if (fp.fail()) {
197 std::cerr << "Error while opening type dependencies file '" <<
198 type_depend << "': " << strerror(errno) << std::endl;
199 exit(EXIT_FAILURE);
200 }
201
202 while (fp.good()) {
203 fp.getline(buff,MAX_LINE);
204 const char *type = strtok(buff, WS);
205 const char *dep;
206 if (!type || type[0] == '#')
207 continue;
208 Type t(type);
209 while ((dep = strtok(NULL, WS)) != NULL) {
210 t.depend.push_front(dep);
211 }
212 types.push_front(t);
213 }
214 fp.close();
215 fp.clear(); // BSD does not reset flags in close().
216
217 /*-------------------------------------------------------------------*
218 * Parse input file
219 *-------------------------------------------------------------------*/
220
221 /* Open input file */
222 fp.open(input_filename, std::ifstream::in);
223 if (fp.fail()) {
224 std::cerr << "Error while opening input file '" <<
225 input_filename << "': " << strerror(errno) << std::endl;
226 exit(EXIT_FAILURE);
227 }
228
229 state = sSTART;
230
231 while (fp.getline(buff,MAX_LINE), fp.good() && state != sEXIT) {
232 char *t;
233
234 ++linenum;
235
236 if ((t = strchr(buff, '\n')))
237 *t = '\0';
238
239 if (strncmp(buff, "IF ", 3) == 0) {
240 if ((ptr = strtok(buff + 3, WS)) == NULL) {
241 errorMsg(input_filename, linenum, "Missing IF parameter");
242 exit(EXIT_FAILURE);
243 }
244 IFDEFS.push(ptr);
245 continue;
246 } else if (strcmp(buff, "ENDIF") == 0) {
247 if (IFDEFS.size() == 0) {
248 errorMsg(input_filename, linenum, "ENDIF without IF first");
249 exit(EXIT_FAILURE);
250 }
251 IFDEFS.pop();
252 } else if (!IFDEFS.size() || isDefined(IFDEFS.top()))
253 switch (state) {
254
255 case sSTART:
256
257 if ((strlen(buff) == 0) || (!strncmp(buff, "#", 1))) {
258 /* ignore empty and comment lines */
259 (void) 0;
260 } else if (!strncmp(buff, "NAME:", 5)) {
261 char *name, *aliasname;
262
263 if ((name = strtok(buff + 5, WS)) == NULL) {
264 errorMsg(input_filename, linenum, buff);
265 exit(EXIT_FAILURE);
266 }
267
268 entries.emplace_back(name);
269
270 while ((aliasname = strtok(NULL, WS)) != NULL)
271 entries.back().alias.push_front(aliasname);
272
273 state = s1;
274 } else if (!strcmp(buff, "EOF")) {
275 state = sEXIT;
276 } else if (!strcmp(buff, "COMMENT_START")) {
277 entries.emplace_back("comment");
278 entries.back().loc = "none";
279 state = sDOC;
280 } else {
281 errorMsg(input_filename, linenum, buff);
282 exit(EXIT_FAILURE);
283 }
284
285 break;
286
287 case s1: {
288 Entry &curr = entries.back();
289
290 if ((strlen(buff) == 0) || (!strncmp(buff, "#", 1))) {
291 /* ignore empty and comment lines */
292 (void) 0;
293 } else if (!strncmp(buff, "COMMENT:", 8)) {
294 ptr = buff + 8;
295
296 while (isspace((unsigned char)*ptr))
297 ++ptr;
298
299 curr.comment = ptr;
300 } else if (!strncmp(buff, "DEFAULT:", 8)) {
301 ptr = buff + 8;
302
303 while (isspace((unsigned char)*ptr))
304 ++ptr;
305
306 curr.defaults.preset.push_back(ptr);
307 } else if (!strncmp(buff, "DEFAULT_IF_NONE:", 16)) {
308 ptr = buff + 16;
309
310 while (isspace((unsigned char)*ptr))
311 ++ptr;
312
313 curr.defaults.if_none.push_back(ptr);
314 } else if (!strncmp(buff, "POSTSCRIPTUM:", 13)) {
315 ptr = buff + 13;
316
317 while (isspace((unsigned char)*ptr))
318 ++ptr;
319
320 curr.defaults.postscriptum.push_back(ptr);
321 } else if (!strncmp(buff, "DEFAULT_DOC:", 12)) {
322 ptr = buff + 12;
323
324 while (isspace((unsigned char)*ptr))
325 ++ptr;
326
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);
331 exit(EXIT_FAILURE);
332 }
333
334 curr.loc = ptr;
335 } else if (!strncmp(buff, "TYPE:", 5)) {
336 if ((ptr = strtok(buff + 5, WS)) == NULL) {
337 errorMsg(input_filename, linenum, buff);
338 exit(EXIT_FAILURE);
339 }
340
341 /* hack to support arrays, rather than pointers */
342 if (strcmp(ptr + strlen(ptr) - 2, "[]") == 0) {
343 curr.array_flag = 1;
344 *(ptr + strlen(ptr) - 2) = '\0';
345 }
346
347 checkDepend(curr.name, ptr, types, entries);
348 curr.type = ptr;
349 } else if (!strncmp(buff, "IFDEF:", 6)) {
350 if ((ptr = strtok(buff + 6, WS)) == NULL) {
351 errorMsg(input_filename, linenum, buff);
352 exit(EXIT_FAILURE);
353 }
354
355 curr.ifdef = ptr;
356 } else if (!strcmp(buff, "DOC_START")) {
357 state = sDOC;
358 } else if (!strcmp(buff, "DOC_NONE")) {
359 state = sSTART;
360 } else {
361 errorMsg(input_filename, linenum, buff);
362 exit(EXIT_FAILURE);
363 }
364 }
365 break;
366
367 case sDOC:
368 if (!strcmp(buff, "DOC_END") || !strcmp(buff, "COMMENT_END")) {
369 state = sSTART;
370 } else if (strcmp(buff, "CONFIG_START") == 0) {
371 state = sCFGLINES;
372 } else { // if (buff != NULL) {
373 assert(buff != NULL);
374 entries.back().doc.push_back(buff);
375 }
376 break;
377
378 case sCFGLINES:
379 if (strcmp(buff, "CONFIG_END") == 0) {
380 state = sDOC;
381 } else { // if (buff != NULL) {
382 assert(buff != NULL);
383 entries.back().cfgLines.push_back(buff);
384 }
385 break;
386 #if 0
387 case sEXIT:
388 assert(0); /* should never get here */
389 break;
390 #endif
391 }
392
393 }
394
395 if (state != sEXIT) {
396 errorMsg(input_filename, linenum, "Error: unexpected EOF");
397 exit(EXIT_FAILURE);
398 }
399
400 fp.close();
401
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 *-------------------------------------------------------------------*/
409
410 /* Open output x.c file */
411
412 std::ofstream fout(output_filename,std::ostream::out);
413 if (!fout.good()) {
414 std::cerr << "Error while opening output .c file '" <<
415 output_filename << "': " << strerror(errno) << std::endl;
416 exit(EXIT_FAILURE);
417 }
418
419 fout << "/*\n" <<
420 " * Generated automatically from " << input_filename << " by " <<
421 argv[0] << "\n"
422 " *\n"
423 " * Abstract: This file contains routines used to configure the\n"
424 " * variables in the squid server.\n"
425 " */\n"
426 "\n";
427
428 rc = gen_default(entries, fout);
429
430 gen_default_if_none(entries, fout);
431
432 gen_default_postscriptum(entries, fout);
433
434 gen_parse(entries, fout);
435
436 gen_dump(entries, fout);
437
438 gen_free(entries, fout);
439
440 fout.close();
441
442 /* Open output x.conf file */
443 fout.open(conf_filename,std::ostream::out);
444 if (!fout.good()) {
445 std::cerr << "Error while opening output conf file '" <<
446 output_filename << "': " << strerror(errno) << std::endl;
447 exit(EXIT_FAILURE);
448 }
449
450 gen_conf(entries, fout, 1);
451
452 fout.close();
453
454 fout.open(conf_filename_short,std::ostream::out);
455 if (!fout.good()) {
456 std::cerr << "Error while opening output short conf file '" <<
457 output_filename << "': " << strerror(errno) << std::endl;
458 exit(EXIT_FAILURE);
459 }
460 gen_conf(entries, fout, 0);
461 fout.close();
462
463 return (rc);
464 }
465
466 static int
467 gen_default(const EntryList &head, std::ostream &fout)
468 {
469 int rc = 0;
470 fout << "static void" << std::endl <<
471 "default_line(const char *s)" << std::endl <<
472 "{" << 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 <<
483 "{" << std::endl <<
484 " cfg_filename = \"Default Configuration\";" << std::endl <<
485 " config_lineno = 0;" << std::endl;
486
487 for (const auto &entry : head) {
488 assert(entry.name.size());
489
490 if (!entry.name.compare("comment"))
491 continue;
492
493 if (!entry.type.compare("obsolete"))
494 continue;
495
496 if (!entry.loc.size()) {
497 std::cerr << "NO LOCATION FOR " << entry.name << std::endl;
498 rc |= 1;
499 continue;
500 }
501
502 if (!entry.defaults.preset.size() && entry.defaults.if_none.empty()) {
503 std::cerr << "NO DEFAULT FOR " << entry.name << std::endl;
504 rc |= 1;
505 continue;
506 }
507
508 if (!entry.defaults.preset.size() || entry.defaults.preset.front().compare("none") == 0) {
509 fout << " // No default for " << entry.name << std::endl;
510 } else {
511 if (entry.ifdef.size())
512 fout << "#if " << entry.ifdef << std::endl;
513
514 for (const auto &l : entry.defaults.preset)
515 fout << " default_line(\"" << entry.name << " " << gen_quote_escape(l) << "\");" << std::endl;
516
517 if (entry.ifdef.size())
518 fout << "#endif" << std::endl;
519 }
520 }
521
522 fout << " cfg_filename = NULL;" << std::endl <<
523 "}" << std::endl << std::endl;
524 return rc;
525 }
526
527 static void
528 gen_default_if_none(const EntryList &head, std::ostream &fout)
529 {
530 fout << "static void" << std::endl <<
531 "defaults_if_none(void)" << std::endl <<
532 "{" << std::endl <<
533 " cfg_filename = \"Default Configuration (if absent)\";" << std::endl <<
534 " config_lineno = 0;" << std::endl;
535
536 for (const auto &entry : head) {
537 assert(entry.name.size());
538
539 if (!entry.loc.size())
540 continue;
541
542 if (entry.defaults.if_none.empty())
543 continue;
544
545 if (!entry.defaults.preset.empty()) {
546 std::cerr << "ERROR: " << entry.name << " has preset defaults. DEFAULT_IF_NONE cannot be true." << std::endl;
547 exit(EXIT_FAILURE);
548 }
549
550 if (entry.ifdef.size())
551 fout << "#if " << entry.ifdef << std::endl;
552
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;
557
558 if (entry.ifdef.size())
559 fout << "#endif" << std::endl;
560 }
561
562 fout << " cfg_filename = NULL;" << std::endl <<
563 "}" << std::endl << std::endl;
564 }
565
566 /// append configuration options specified by POSTSCRIPTUM lines
567 static void
568 gen_default_postscriptum(const EntryList &head, std::ostream &fout)
569 {
570 fout << "static void" << std::endl <<
571 "defaults_postscriptum(void)" << std::endl <<
572 "{" << std::endl <<
573 " cfg_filename = \"Default Configuration (postscriptum)\";" << std::endl <<
574 " config_lineno = 0;" << std::endl;
575
576 for (const auto &entry : head) {
577 assert(entry.name.size());
578
579 if (!entry.loc.size())
580 continue;
581
582 if (entry.defaults.postscriptum.empty())
583 continue;
584
585 if (entry.ifdef.size())
586 fout << "#if " << entry.ifdef << std::endl;
587
588 for (const auto &l : entry.defaults.postscriptum)
589 fout << " default_line(\"" << entry.name << " " << l <<"\");" << std::endl;
590
591 if (entry.ifdef.size())
592 fout << "#endif" << std::endl;
593 }
594
595 fout << " cfg_filename = NULL;" << std::endl <<
596 "}" << std::endl << std::endl;
597 }
598
599 void
600 Entry::genParseAlias(const std::string &aName, std::ostream &fout) const
601 {
602 fout << " if (!strcmp(token, \"" << aName << "\")) {" << std::endl;
603 if (ifdef.size())
604 fout << "#if " << ifdef << std::endl;
605 fout << " cfg_directive = \"" << aName << "\";" << std::endl;
606 fout << " ";
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;
612 }
613 fout << " parse_obsolete(token);";
614 } else if (!loc.size() || loc.compare("none") == 0) {
615 fout << "parse_" << type << "();";
616 } else {
617 fout << "parse_" << type << "(&" << loc << (array_flag ? "[0]" : "") << ");";
618 }
619 fout << std::endl;
620 fout << " cfg_directive = NULL;" << std::endl;
621 if (ifdef.size()) {
622 fout <<
623 "#else" << std::endl <<
624 " debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), \"ERROR: '" << name << "' requires " << available_if(ifdef) << "\");" << std::endl <<
625 "#endif" << std::endl;
626 }
627 fout << " return 1;" << std::endl;
628 fout << " };" << std::endl;
629 }
630
631 void
632 Entry::genParse(std::ostream &fout) const
633 {
634 if (name.compare("comment") == 0)
635 return;
636
637 // Once for the current directive name
638 genParseAlias(name, fout);
639
640 // All accepted aliases
641 for (const auto &a : alias) {
642 genParseAlias(a, fout);
643 }
644 }
645
646 static void
647 gen_parse(const EntryList &head, std::ostream &fout)
648 {
649 fout <<
650 "static int\n"
651 "parse_line(char *buff)\n"
652 "{\n"
653 "\tchar\t*token;\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";
657
658 for (const auto &e : head)
659 e.genParse(fout);
660
661 fout << "\treturn 0; /* failure */\n"
662 "}\n\n";
663
664 }
665
666 static void
667 gen_dump(const EntryList &head, std::ostream &fout)
668 {
669 fout <<
670 "static void" << std::endl <<
671 "dump_config(StoreEntry *entry)" << std::endl <<
672 "{" << std::endl <<
673 " debugs(5, 4, HERE);" << std::endl;
674
675 for (const auto &e : head) {
676
677 if (!e.loc.size() || e.loc.compare("none") == 0)
678 continue;
679
680 if (e.name.compare("comment") == 0)
681 continue;
682
683 if (e.ifdef.size())
684 fout << "#if " << e.ifdef << std::endl;
685
686 fout << " dump_" << e.type << "(entry, \"" << e.name << "\", " << e.loc << ");" << std::endl;
687
688 if (e.ifdef.size())
689 fout << "#endif" << std::endl;
690 }
691
692 fout << "}" << std::endl << std::endl;
693 }
694
695 static void
696 gen_free(const EntryList &head, std::ostream &fout)
697 {
698 fout <<
699 "static void" << std::endl <<
700 "free_all(void)" << std::endl <<
701 "{" << std::endl <<
702 " debugs(5, 4, HERE);" << std::endl;
703
704 for (const auto &e : head) {
705 if (!e.loc.size() || e.loc.compare("none") == 0)
706 continue;
707
708 if (e.name.compare("comment") == 0)
709 continue;
710
711 if (e.ifdef.size())
712 fout << "#if " << e.ifdef << std::endl;
713
714 fout << " free_" << e.type << "(&" << e.loc << (e.array_flag ? "[0]" : "") << ");" << std::endl;
715
716 if (e.ifdef.size())
717 fout << "#endif" << std::endl;
718 }
719
720 fout << "}" << std::endl << std::endl;
721 }
722
723 static bool
724 isDefined(const std::string &name)
725 {
726 if (!name.size())
727 return true;
728
729 for (int i = 0; defines[i].name; ++i) {
730 if (name.compare(defines[i].name) == 0)
731 return defines[i].defined;
732 }
733
734 return false;
735 }
736
737 static const char *
738 available_if(const std::string &name)
739 {
740 assert(name.size());
741
742 for (int i = 0; defines[i].name; ++i) {
743 if (name.compare(defines[i].name) == 0)
744 return defines[i].enable;
745 }
746
747 return name.c_str();
748 }
749
750 static void
751 gen_conf(const EntryList &head, std::ostream &fout, bool verbose_output)
752 {
753 for (const auto &entry : head) {
754 char buf[8192];
755 LineList def;
756 int enabled = 1;
757
758 // Display TAG: line
759 if (!entry.name.compare("comment"))
760 (void) 0;
761 else if (!entry.name.compare("obsolete"))
762 (void) 0;
763 else if (verbose_output) {
764 fout << "# TAG: " << entry.name;
765
766 if (entry.comment.size())
767 fout << "\t" << entry.comment;
768
769 fout << std::endl;
770 }
771
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 <<
777 "#" << std::endl;
778 }
779 enabled = 0;
780 }
781
782 // Display DOC_START section
783 if (verbose_output && entry.doc.size()) {
784 for (const auto &line : entry.doc) {
785 fout << "#" << line << std::endl;
786 }
787 }
788
789 if (entry.defaults.docs.size()) {
790 // Display the DEFAULT_DOC line(s)
791 def = entry.defaults.docs;
792 } else {
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());
797 def.push_back(buf);
798 }
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());
803 def.push_back(buf);
804 }
805 }
806 }
807
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");
811
812 if (verbose_output && def.size()) {
813 fout << "#Default:\n";
814 while (def.size()) {
815 fout << "# " << def.front() << std::endl;
816 def.pop_front();
817 }
818 if (entry.doc.empty() && entry.cfgLines.empty())
819 fout << std::endl;
820 }
821
822 if (verbose_output && entry.cfgLines.size())
823 fout << "#" << std::endl;
824
825 if (enabled || verbose_output) {
826 for (const auto &line : entry.cfgLines) {
827 if (!enabled && line.at(0) != '#')
828 fout << "#";
829 fout << line << std::endl;
830 }
831 }
832
833 if (verbose_output && entry.doc.size()) {
834 fout << std::endl;
835 }
836 }
837 }
838
839 static const char *
840 gen_quote_escape(const std::string &var)
841 {
842 static std::string esc;
843 esc.clear();
844
845 for (int i = 0; i < var.length(); ++i) {
846 switch (var[i]) {
847 case '"':
848 case '\\':
849 esc += '\\';
850 default:
851 esc += var[i];
852 }
853 }
854
855 return esc.c_str();
856 }
857