]> git.ipfire.org Git - thirdparty/squid.git/blob - src/cf_gen.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / cf_gen.cc
1 /*
2 * Copyright (C) 1996-2017 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 sNOCOMMENT,
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 DefaultValues() : preset(), if_none(), docs() {}
69 ~DefaultValues() {}
70
71 /// Default config lines to be defined before parsing the config files.
72 LineList preset;
73
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.
77 LineList if_none;
78
79 /// Default config lines to parse and add to any prior settings.
80 LineList postscriptum;
81
82 /// Text description to use in documentation for the default.
83 /// If unset the preset or if-none values will be displayed.
84 LineList docs;
85 };
86
87 class Entry
88 {
89 public:
90 Entry(const char *str) :
91 name(str), alias(),type(), loc(),
92 defaults(), comment(), ifdef(), doc(), nocomment(),
93 array_flag(0) {}
94 ~Entry() {}
95
96 std::string name;
97 EntryAliasList alias;
98 std::string type;
99 std::string loc;
100 DefaultValues defaults;
101 std::string comment;
102 std::string ifdef;
103 LineList doc;
104 LineList nocomment;
105 int array_flag;
106
107 void genParse(std::ostream &fout) const;
108
109 private:
110 void genParseAlias(const std::string &, std::ostream &) const;
111 };
112
113 typedef std::list<class Entry> EntryList;
114
115 class Type
116 {
117 public:
118 Type(const char *str) : name(str) {}
119 ~Type() {}
120
121 std::string name;
122 TypeDepList depend;
123 };
124
125 typedef std::list<class Type> TypeList;
126
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);
138
139 static void
140 checkDepend(const std::string &directive, const char *name, const TypeList &types, const EntryList &entries)
141 {
142 for (TypeList::const_iterator t = types.begin(); t != types.end(); ++t) {
143 if (t->name.compare(name) != 0)
144 continue;
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)
149 break;
150 }
151 if (entry == entries.end()) {
152 std::cerr << "ERROR: '" << directive << "' (" << name << ") depends on '" << *dep << "'\n";
153 exit(1);
154 }
155 }
156 return;
157 }
158 std::cerr << "ERROR: Dependencies for cf.data type '" << name << "' used in ' " << directive << "' not defined\n" ;
159 exit(1);
160 }
161
162 static void
163 usage(const char *program_name)
164 {
165 std::cerr << "Usage: " << program_name << " cf.data cf.data.depend\n";
166 exit(1);
167 }
168
169 static void
170 errorMsg(const char *filename, int line, const char *detail)
171 {
172 std::cerr << "Error in '" << filename << "' on line " << line <<
173 "--> " << detail << std::endl;
174 }
175
176 int
177 main(int argc, char *argv[])
178 {
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;
184 int linenum = 0;
185 EntryList entries;
186 TypeList types;
187 enum State state;
188 int rc = 0;
189 char *ptr = NULL;
190 char buff[MAX_LINE];
191 std::ifstream fp;
192 std::stack<std::string> IFDEFS;
193
194 if (argc != 3)
195 usage(argv[0]);
196
197 input_filename = argv[1];
198 type_depend = argv[2];
199
200 /*-------------------------------------------------------------------*
201 * Parse type dependencies
202 *-------------------------------------------------------------------*/
203 fp.open(type_depend, std::ifstream::in);
204 if (fp.fail()) {
205 std::cerr << "Error while opening type dependencies file '" <<
206 type_depend << "': " << strerror(errno) << std::endl;
207 exit(1);
208 }
209
210 while (fp.good()) {
211 fp.getline(buff,MAX_LINE);
212 const char *type = strtok(buff, WS);
213 const char *dep;
214 if (!type || type[0] == '#')
215 continue;
216 Type t(type);
217 while ((dep = strtok(NULL, WS)) != NULL) {
218 t.depend.push_front(dep);
219 }
220 types.push_front(t);
221 }
222 fp.close();
223 fp.clear(); // BSD does not reset flags in close().
224
225 /*-------------------------------------------------------------------*
226 * Parse input file
227 *-------------------------------------------------------------------*/
228
229 /* Open input file */
230 fp.open(input_filename, std::ifstream::in);
231 if (fp.fail()) {
232 std::cerr << "Error while opening input file '" <<
233 input_filename << "': " << strerror(errno) << std::endl;
234 exit(1);
235 }
236
237 state = sSTART;
238
239 while (fp.getline(buff,MAX_LINE), fp.good() && state != sEXIT) {
240 char *t;
241
242 ++linenum;
243
244 if ((t = strchr(buff, '\n')))
245 *t = '\0';
246
247 if (strncmp(buff, "IF ", 3) == 0) {
248 if ((ptr = strtok(buff + 3, WS)) == NULL) {
249 errorMsg(input_filename, linenum, "Missing IF parameter");
250 exit(1);
251 }
252 IFDEFS.push(ptr);
253 continue;
254 } else if (strcmp(buff, "ENDIF") == 0) {
255 if (IFDEFS.size() == 0) {
256 errorMsg(input_filename, linenum, "ENDIF without IF first");
257 exit(1);
258 }
259 IFDEFS.pop();
260 } else if (!IFDEFS.size() || isDefined(IFDEFS.top()))
261 switch (state) {
262
263 case sSTART:
264
265 if ((strlen(buff) == 0) || (!strncmp(buff, "#", 1))) {
266 /* ignore empty and comment lines */
267 (void) 0;
268 } else if (!strncmp(buff, "NAME:", 5)) {
269 char *name, *aliasname;
270
271 if ((name = strtok(buff + 5, WS)) == NULL) {
272 errorMsg(input_filename, linenum, buff);
273 exit(1);
274 }
275
276 entries.push_back(name);
277
278 while ((aliasname = strtok(NULL, WS)) != NULL)
279 entries.back().alias.push_front(aliasname);
280
281 state = s1;
282 } else if (!strcmp(buff, "EOF")) {
283 state = sEXIT;
284 } else if (!strcmp(buff, "COMMENT_START")) {
285 entries.push_back("comment");
286 entries.back().loc = "none";
287 state = sDOC;
288 } else {
289 errorMsg(input_filename, linenum, buff);
290 exit(1);
291 }
292
293 break;
294
295 case s1: {
296 Entry &curr = entries.back();
297
298 if ((strlen(buff) == 0) || (!strncmp(buff, "#", 1))) {
299 /* ignore empty and comment lines */
300 (void) 0;
301 } else if (!strncmp(buff, "COMMENT:", 8)) {
302 ptr = buff + 8;
303
304 while (isspace((unsigned char)*ptr))
305 ++ptr;
306
307 curr.comment = ptr;
308 } else if (!strncmp(buff, "DEFAULT:", 8)) {
309 ptr = buff + 8;
310
311 while (isspace((unsigned char)*ptr))
312 ++ptr;
313
314 curr.defaults.preset.push_back(ptr);
315 } else if (!strncmp(buff, "DEFAULT_IF_NONE:", 16)) {
316 ptr = buff + 16;
317
318 while (isspace((unsigned char)*ptr))
319 ++ptr;
320
321 curr.defaults.if_none.push_back(ptr);
322 } else if (!strncmp(buff, "POSTSCRIPTUM:", 13)) {
323 ptr = buff + 13;
324
325 while (isspace((unsigned char)*ptr))
326 ++ptr;
327
328 curr.defaults.postscriptum.push_back(ptr);
329 } else if (!strncmp(buff, "DEFAULT_DOC:", 12)) {
330 ptr = buff + 12;
331
332 while (isspace((unsigned char)*ptr))
333 ++ptr;
334
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);
339 exit(1);
340 }
341
342 curr.loc = ptr;
343 } else if (!strncmp(buff, "TYPE:", 5)) {
344 if ((ptr = strtok(buff + 5, WS)) == NULL) {
345 errorMsg(input_filename, linenum, buff);
346 exit(1);
347 }
348
349 /* hack to support arrays, rather than pointers */
350 if (0 == strcmp(ptr + strlen(ptr) - 2, "[]")) {
351 curr.array_flag = 1;
352 *(ptr + strlen(ptr) - 2) = '\0';
353 }
354
355 checkDepend(curr.name, ptr, types, entries);
356 curr.type = ptr;
357 } else if (!strncmp(buff, "IFDEF:", 6)) {
358 if ((ptr = strtok(buff + 6, WS)) == NULL) {
359 errorMsg(input_filename, linenum, buff);
360 exit(1);
361 }
362
363 curr.ifdef = ptr;
364 } else if (!strcmp(buff, "DOC_START")) {
365 state = sDOC;
366 } else if (!strcmp(buff, "DOC_NONE")) {
367 state = sSTART;
368 } else {
369 errorMsg(input_filename, linenum, buff);
370 exit(1);
371 }
372 }
373 break;
374
375 case sDOC:
376 if (!strcmp(buff, "DOC_END") || !strcmp(buff, "COMMENT_END")) {
377 state = sSTART;
378 } else if (!strcmp(buff, "NOCOMMENT_START")) {
379 state = sNOCOMMENT;
380 } else { // if (buff != NULL) {
381 assert(buff != NULL);
382 entries.back().doc.push_back(buff);
383 }
384 break;
385
386 case sNOCOMMENT:
387 if (!strcmp(buff, "NOCOMMENT_END")) {
388 state = sDOC;
389 } else { // if (buff != NULL) {
390 assert(buff != NULL);
391 entries.back().nocomment.push_back(buff);
392 }
393 break;
394 #if 0
395 case sEXIT:
396 assert(0); /* should never get here */
397 break;
398 #endif
399 }
400
401 }
402
403 if (state != sEXIT) {
404 errorMsg(input_filename, linenum, "Error: unexpected EOF");
405 exit(1);
406 }
407
408 fp.close();
409
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 *-------------------------------------------------------------------*/
417
418 /* Open output x.c file */
419
420 std::ofstream fout(output_filename,std::ostream::out);
421 if (!fout.good()) {
422 std::cerr << "Error while opening output .c file '" <<
423 output_filename << "': " << strerror(errno) << std::endl;
424 exit(1);
425 }
426
427 fout << "/*\n" <<
428 " * Generated automatically from " << input_filename << " by " <<
429 argv[0] << "\n"
430 " *\n"
431 " * Abstract: This file contains routines used to configure the\n"
432 " * variables in the squid server.\n"
433 " */\n"
434 "\n";
435
436 rc = gen_default(entries, fout);
437
438 gen_default_if_none(entries, fout);
439
440 gen_default_postscriptum(entries, fout);
441
442 gen_parse(entries, fout);
443
444 gen_dump(entries, fout);
445
446 gen_free(entries, fout);
447
448 fout.close();
449
450 /* Open output x.conf file */
451 fout.open(conf_filename,std::ostream::out);
452 if (!fout.good()) {
453 std::cerr << "Error while opening output conf file '" <<
454 output_filename << "': " << strerror(errno) << std::endl;
455 exit(1);
456 }
457
458 gen_conf(entries, fout, 1);
459
460 fout.close();
461
462 fout.open(conf_filename_short,std::ostream::out);
463 if (!fout.good()) {
464 std::cerr << "Error while opening output short conf file '" <<
465 output_filename << "': " << strerror(errno) << std::endl;
466 exit(1);
467 }
468 gen_conf(entries, fout, 0);
469 fout.close();
470
471 return (rc);
472 }
473
474 static int
475 gen_default(const EntryList &head, std::ostream &fout)
476 {
477 int rc = 0;
478 fout << "static void" << std::endl <<
479 "default_line(const char *s)" << std::endl <<
480 "{" << std::endl <<
481 " char *tmp_line = xstrdup(s);" << std::endl <<
482 " int len = strlen(tmp_line);" << std::endl <<
483 " ProcessMacros(tmp_line, len);" << std::endl <<
484 " xstrncpy(config_input_line, tmp_line, sizeof(config_input_line));" << 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 <<
491 "{" << std::endl <<
492 " cfg_filename = \"Default Configuration\";" << std::endl <<
493 " config_lineno = 0;" << std::endl;
494
495 for (EntryList::const_iterator entry = head.begin(); entry != head.end(); ++entry) {
496 assert(entry->name.size());
497
498 if (!entry->name.compare("comment"))
499 continue;
500
501 if (!entry->type.compare("obsolete"))
502 continue;
503
504 if (!entry->loc.size()) {
505 std::cerr << "NO LOCATION FOR " << entry->name << std::endl;
506 rc |= 1;
507 continue;
508 }
509
510 if (!entry->defaults.preset.size() && entry->defaults.if_none.empty()) {
511 std::cerr << "NO DEFAULT FOR " << entry->name << std::endl;
512 rc |= 1;
513 continue;
514 }
515
516 if (!entry->defaults.preset.size() || entry->defaults.preset.front().compare("none") == 0) {
517 fout << " // No default for " << entry->name << std::endl;
518 } else {
519 if (entry->ifdef.size())
520 fout << "#if " << entry->ifdef << std::endl;
521
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;
524 }
525
526 if (entry->ifdef.size())
527 fout << "#endif" << std::endl;
528 }
529 }
530
531 fout << " cfg_filename = NULL;" << std::endl <<
532 "}" << std::endl << std::endl;
533 return rc;
534 }
535
536 static void
537 gen_default_if_none(const EntryList &head, std::ostream &fout)
538 {
539 fout << "static void" << std::endl <<
540 "defaults_if_none(void)" << std::endl <<
541 "{" << std::endl <<
542 " cfg_filename = \"Default Configuration (if absent)\";" << std::endl <<
543 " config_lineno = 0;" << std::endl;
544
545 for (EntryList::const_iterator entry = head.begin(); entry != head.end(); ++entry) {
546 assert(entry->name.size());
547
548 if (!entry->loc.size())
549 continue;
550
551 if (entry->defaults.if_none.empty())
552 continue;
553
554 if (!entry->defaults.preset.empty()) {
555 std::cerr << "ERROR: " << entry->name << " has preset defaults. DEFAULT_IF_NONE cannot be true." << std::endl;
556 exit(1);
557 }
558
559 if (entry->ifdef.size())
560 fout << "#if " << entry->ifdef << std::endl;
561
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;
566
567 if (entry->ifdef.size())
568 fout << "#endif" << std::endl;
569 }
570
571 fout << " cfg_filename = NULL;" << std::endl <<
572 "}" << std::endl << std::endl;
573 }
574
575 /// append configuration options specified by POSTSCRIPTUM lines
576 static void
577 gen_default_postscriptum(const EntryList &head, std::ostream &fout)
578 {
579 fout << "static void" << std::endl <<
580 "defaults_postscriptum(void)" << std::endl <<
581 "{" << std::endl <<
582 " cfg_filename = \"Default Configuration (postscriptum)\";" << std::endl <<
583 " config_lineno = 0;" << std::endl;
584
585 for (EntryList::const_iterator entry = head.begin(); entry != head.end(); ++entry) {
586 assert(entry->name.size());
587
588 if (!entry->loc.size())
589 continue;
590
591 if (entry->defaults.postscriptum.empty())
592 continue;
593
594 if (entry->ifdef.size())
595 fout << "#if " << entry->ifdef << std::endl;
596
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;
599
600 if (entry->ifdef.size())
601 fout << "#endif" << std::endl;
602 }
603
604 fout << " cfg_filename = NULL;" << std::endl <<
605 "}" << std::endl << std::endl;
606 }
607
608 void
609 Entry::genParseAlias(const std::string &aName, std::ostream &fout) const
610 {
611 fout << " if (!strcmp(token, \"" << aName << "\")) {" << std::endl;
612 if (ifdef.size())
613 fout << "#if " << ifdef << std::endl;
614 fout << " cfg_directive = \"" << aName << "\";" << std::endl;
615 fout << " ";
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;
621 }
622 fout << " parse_obsolete(token);";
623 } else if (!loc.size() || loc.compare("none") == 0) {
624 fout << "parse_" << type << "();";
625 } else {
626 fout << "parse_" << type << "(&" << loc << (array_flag ? "[0]" : "") << ");";
627 }
628 fout << std::endl;
629 fout << " cfg_directive = NULL;" << std::endl;
630 if (ifdef.size()) {
631 fout <<
632 "#else" << std::endl <<
633 " debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), \"ERROR: '" << name << "' requires " << available_if(ifdef) << "\");" << std::endl <<
634 "#endif" << std::endl;
635 }
636 fout << " return 1;" << std::endl;
637 fout << " };" << std::endl;
638 }
639
640 void
641 Entry::genParse(std::ostream &fout) const
642 {
643 if (name.compare("comment") == 0)
644 return;
645
646 // Once for the current directive name
647 genParseAlias(name, fout);
648
649 // All accepted aliases
650 for (EntryAliasList::const_iterator a = alias.begin(); a != alias.end(); ++a) {
651 genParseAlias(*a, fout);
652 }
653 }
654
655 static void
656 gen_parse(const EntryList &head, std::ostream &fout)
657 {
658 fout <<
659 "static int\n"
660 "parse_line(char *buff)\n"
661 "{\n"
662 "\tchar\t*token;\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";
666
667 for (EntryList::const_iterator e = head.begin(); e != head.end(); ++e)
668 e->genParse(fout);
669
670 fout << "\treturn 0; /* failure */\n"
671 "}\n\n";
672
673 }
674
675 static void
676 gen_dump(const EntryList &head, std::ostream &fout)
677 {
678 fout <<
679 "static void" << std::endl <<
680 "dump_config(StoreEntry *entry)" << std::endl <<
681 "{" << std::endl <<
682 " debugs(5, 4, HERE);" << std::endl;
683
684 for (EntryList::const_iterator e = head.begin(); e != head.end(); ++e) {
685
686 if (!e->loc.size() || e->loc.compare("none") == 0)
687 continue;
688
689 if (e->name.compare("comment") == 0)
690 continue;
691
692 if (e->ifdef.size())
693 fout << "#if " << e->ifdef << std::endl;
694
695 fout << " dump_" << e->type << "(entry, \"" << e->name << "\", " << e->loc << ");" << std::endl;
696
697 if (e->ifdef.size())
698 fout << "#endif" << std::endl;
699 }
700
701 fout << "}" << std::endl << std::endl;
702 }
703
704 static void
705 gen_free(const EntryList &head, std::ostream &fout)
706 {
707 fout <<
708 "static void" << std::endl <<
709 "free_all(void)" << std::endl <<
710 "{" << std::endl <<
711 " debugs(5, 4, HERE);" << std::endl;
712
713 for (EntryList::const_iterator e = head.begin(); e != head.end(); ++e) {
714 if (!e->loc.size() || e->loc.compare("none") == 0)
715 continue;
716
717 if (e->name.compare("comment") == 0)
718 continue;
719
720 if (e->ifdef.size())
721 fout << "#if " << e->ifdef << std::endl;
722
723 fout << " free_" << e->type << "(&" << e->loc << (e->array_flag ? "[0]" : "") << ");" << std::endl;
724
725 if (e->ifdef.size())
726 fout << "#endif" << std::endl;
727 }
728
729 fout << "}" << std::endl << std::endl;
730 }
731
732 static bool
733 isDefined(const std::string &name)
734 {
735 if (!name.size())
736 return true;
737
738 for (int i = 0; defines[i].name; ++i) {
739 if (name.compare(defines[i].name) == 0)
740 return defines[i].defined;
741 }
742
743 return false;
744 }
745
746 static const char *
747 available_if(const std::string &name)
748 {
749 assert(name.size());
750
751 for (int i = 0; defines[i].name; ++i) {
752 if (name.compare(defines[i].name) == 0)
753 return defines[i].enable;
754 }
755
756 return name.c_str();
757 }
758
759 static void
760 gen_conf(const EntryList &head, std::ostream &fout, bool verbose_output)
761 {
762 for (EntryList::const_iterator entry = head.begin(); entry != head.end(); ++entry) {
763 char buf[8192];
764 LineList def;
765 int enabled = 1;
766
767 // Display TAG: line
768 if (!entry->name.compare("comment"))
769 (void) 0;
770 else if (!entry->name.compare("obsolete"))
771 (void) 0;
772 else if (verbose_output) {
773 fout << "# TAG: " << entry->name;
774
775 if (entry->comment.size())
776 fout << "\t" << entry->comment;
777
778 fout << std::endl;
779 }
780
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 <<
786 "#" << std::endl;
787 }
788 enabled = 0;
789 }
790
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;
795 }
796 }
797
798 if (entry->defaults.docs.size()) {
799 // Display the DEFAULT_DOC line(s)
800 def = entry->defaults.docs;
801 } else {
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());
806 def.push_back(buf);
807 }
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());
812 def.push_back(buf);
813 }
814 }
815 }
816
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");
820
821 if (verbose_output && def.size()) {
822 fout << "#Default:\n";
823 while (def.size()) {
824 fout << "# " << def.front() << std::endl;
825 def.pop_front();
826 }
827 if (entry->doc.empty() && entry->nocomment.empty())
828 fout << std::endl;
829 }
830
831 if (verbose_output && entry->nocomment.size())
832 fout << "#" << std::endl;
833
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) != '#')
837 fout << "#";
838 fout << *line << std::endl;
839 }
840 }
841
842 if (verbose_output && entry->doc.size()) {
843 fout << std::endl;
844 }
845 }
846 }
847
848 static const char *
849 gen_quote_escape(const std::string &var)
850 {
851 static std::string esc;
852 esc.clear();
853
854 for (int i = 0; i < var.length(); ++i) {
855 switch (var[i]) {
856 case '"':
857 case '\\':
858 esc += '\\';
859 default:
860 esc += var[i];
861 }
862 }
863
864 return esc.c_str();
865 }
866