]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (C) 1996-2025 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 = nullptr; | |
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(nullptr, WS)) != nullptr) { | |
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)) == nullptr) { | |
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)) == nullptr) { | |
264 | errorMsg(input_filename, linenum, buff); | |
265 | exit(EXIT_FAILURE); | |
266 | } | |
267 | ||
268 | auto &newEntry = entries.emplace_back(name); | |
269 | ||
270 | while ((aliasname = strtok(nullptr, WS)) != nullptr) | |
271 | newEntry.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 | auto &newEntry = entries.emplace_back("comment"); | |
278 | newEntry.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)) == nullptr) { | |
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)) == nullptr) { | |
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)) == nullptr) { | |
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 { | |
373 | entries.back().doc.push_back(buff); | |
374 | } | |
375 | break; | |
376 | ||
377 | case sCFGLINES: | |
378 | if (strcmp(buff, "CONFIG_END") == 0) { | |
379 | state = sDOC; | |
380 | } else { | |
381 | entries.back().cfgLines.push_back(buff); | |
382 | } | |
383 | break; | |
384 | case sEXIT: | |
385 | assert(0); /* should never get here */ | |
386 | break; | |
387 | } | |
388 | ||
389 | } | |
390 | ||
391 | if (state != sEXIT) { | |
392 | errorMsg(input_filename, linenum, "Error: unexpected EOF"); | |
393 | exit(EXIT_FAILURE); | |
394 | } | |
395 | ||
396 | fp.close(); | |
397 | ||
398 | /*-------------------------------------------------------------------* | |
399 | * Generate default_all() | |
400 | * Generate parse_line() | |
401 | * Generate dump_config() | |
402 | * Generate free_all() | |
403 | * Generate example squid.conf.default file | |
404 | *-------------------------------------------------------------------*/ | |
405 | ||
406 | /* Open output x.c file */ | |
407 | ||
408 | std::ofstream fout(output_filename,std::ostream::out); | |
409 | if (!fout.good()) { | |
410 | std::cerr << "Error while opening output .c file '" << | |
411 | output_filename << "': " << strerror(errno) << std::endl; | |
412 | exit(EXIT_FAILURE); | |
413 | } | |
414 | ||
415 | fout << "/*\n" << | |
416 | " * Generated automatically from " << input_filename << " by " << | |
417 | argv[0] << "\n" | |
418 | " *\n" | |
419 | " * Abstract: This file contains routines used to configure the\n" | |
420 | " * variables in the squid server.\n" | |
421 | " */\n" | |
422 | "\n"; | |
423 | ||
424 | rc = gen_default(entries, fout); | |
425 | ||
426 | gen_default_if_none(entries, fout); | |
427 | ||
428 | gen_default_postscriptum(entries, fout); | |
429 | ||
430 | gen_parse(entries, fout); | |
431 | ||
432 | gen_dump(entries, fout); | |
433 | ||
434 | gen_free(entries, fout); | |
435 | ||
436 | fout.close(); | |
437 | ||
438 | /* Open output x.conf file */ | |
439 | fout.open(conf_filename,std::ostream::out); | |
440 | if (!fout.good()) { | |
441 | std::cerr << "Error while opening output conf file '" << | |
442 | output_filename << "': " << strerror(errno) << std::endl; | |
443 | exit(EXIT_FAILURE); | |
444 | } | |
445 | ||
446 | gen_conf(entries, fout, 1); | |
447 | ||
448 | fout.close(); | |
449 | ||
450 | fout.open(conf_filename_short,std::ostream::out); | |
451 | if (!fout.good()) { | |
452 | std::cerr << "Error while opening output short conf file '" << | |
453 | output_filename << "': " << strerror(errno) << std::endl; | |
454 | exit(EXIT_FAILURE); | |
455 | } | |
456 | gen_conf(entries, fout, 0); | |
457 | fout.close(); | |
458 | ||
459 | return (rc); | |
460 | } | |
461 | ||
462 | static int | |
463 | gen_default(const EntryList &head, std::ostream &fout) | |
464 | { | |
465 | int rc = 0; | |
466 | fout << "static void" << std::endl << | |
467 | "default_line(const char *s)" << std::endl << | |
468 | "{" << std::endl << | |
469 | " char *tmp_line = xstrdup(s);" << std::endl << | |
470 | " int len = strlen(tmp_line);" << std::endl << | |
471 | " ProcessMacros(tmp_line, len);" << std::endl << | |
472 | " xstrncpy(config_input_line, tmp_line, sizeof(config_input_line));" << std::endl << | |
473 | " config_lineno++;" << std::endl << | |
474 | " parse_line(tmp_line);" << std::endl << | |
475 | " xfree(tmp_line);" << std::endl << | |
476 | "}" << std::endl << std::endl; | |
477 | fout << "static void" << std::endl << | |
478 | "default_all(void)" << std::endl << | |
479 | "{" << std::endl << | |
480 | " cfg_filename = \"Default Configuration\";" << std::endl << | |
481 | " config_lineno = 0;" << std::endl; | |
482 | ||
483 | for (const auto &entry : head) { | |
484 | assert(entry.name.size()); | |
485 | ||
486 | if (!entry.name.compare("comment")) | |
487 | continue; | |
488 | ||
489 | if (!entry.type.compare("obsolete")) | |
490 | continue; | |
491 | ||
492 | if (!entry.loc.size()) { | |
493 | std::cerr << "NO LOCATION FOR " << entry.name << std::endl; | |
494 | rc |= 1; | |
495 | continue; | |
496 | } | |
497 | ||
498 | if (!entry.defaults.preset.size() && entry.defaults.if_none.empty()) { | |
499 | std::cerr << "NO DEFAULT FOR " << entry.name << std::endl; | |
500 | rc |= 1; | |
501 | continue; | |
502 | } | |
503 | ||
504 | if (!entry.defaults.preset.size() || entry.defaults.preset.front().compare("none") == 0) { | |
505 | fout << " // No default for " << entry.name << std::endl; | |
506 | } else { | |
507 | if (entry.ifdef.size()) | |
508 | fout << "#if " << entry.ifdef << std::endl; | |
509 | ||
510 | for (const auto &l : entry.defaults.preset) | |
511 | fout << " default_line(\"" << entry.name << " " << gen_quote_escape(l) << "\");" << std::endl; | |
512 | ||
513 | if (entry.ifdef.size()) | |
514 | fout << "#endif" << std::endl; | |
515 | } | |
516 | } | |
517 | ||
518 | fout << " cfg_filename = nullptr;" << std::endl << | |
519 | "}" << std::endl << std::endl; | |
520 | return rc; | |
521 | } | |
522 | ||
523 | static void | |
524 | gen_default_if_none(const EntryList &head, std::ostream &fout) | |
525 | { | |
526 | fout << "static void" << std::endl << | |
527 | "defaults_if_none(void)" << std::endl << | |
528 | "{" << std::endl << | |
529 | " cfg_filename = \"Default Configuration (if absent)\";" << std::endl << | |
530 | " config_lineno = 0;" << std::endl; | |
531 | ||
532 | for (const auto &entry : head) { | |
533 | assert(entry.name.size()); | |
534 | ||
535 | if (!entry.loc.size()) | |
536 | continue; | |
537 | ||
538 | if (entry.defaults.if_none.empty()) | |
539 | continue; | |
540 | ||
541 | if (!entry.defaults.preset.empty()) { | |
542 | std::cerr << "ERROR: " << entry.name << " has preset defaults. DEFAULT_IF_NONE cannot be true." << std::endl; | |
543 | exit(EXIT_FAILURE); | |
544 | } | |
545 | ||
546 | if (entry.ifdef.size()) | |
547 | fout << "#if " << entry.ifdef << std::endl; | |
548 | ||
549 | fout << " if (check_null_" << entry.type << "(" << entry.loc << ")) {" << std::endl; | |
550 | for (const auto &l : entry.defaults.if_none) | |
551 | fout << " default_line(\"" << entry.name << " " << gen_quote_escape(l) <<"\");" << std::endl; | |
552 | fout << " }" << std::endl; | |
553 | ||
554 | if (entry.ifdef.size()) | |
555 | fout << "#endif" << std::endl; | |
556 | } | |
557 | ||
558 | fout << " cfg_filename = nullptr;" << std::endl << | |
559 | "}" << std::endl << std::endl; | |
560 | } | |
561 | ||
562 | /// append configuration options specified by POSTSCRIPTUM lines | |
563 | static void | |
564 | gen_default_postscriptum(const EntryList &head, std::ostream &fout) | |
565 | { | |
566 | fout << "static void" << std::endl << | |
567 | "defaults_postscriptum(void)" << std::endl << | |
568 | "{" << std::endl << | |
569 | " cfg_filename = \"Default Configuration (postscriptum)\";" << std::endl << | |
570 | " config_lineno = 0;" << std::endl; | |
571 | ||
572 | for (const auto &entry : head) { | |
573 | assert(entry.name.size()); | |
574 | ||
575 | if (!entry.loc.size()) | |
576 | continue; | |
577 | ||
578 | if (entry.defaults.postscriptum.empty()) | |
579 | continue; | |
580 | ||
581 | if (entry.ifdef.size()) | |
582 | fout << "#if " << entry.ifdef << std::endl; | |
583 | ||
584 | for (const auto &l : entry.defaults.postscriptum) | |
585 | fout << " default_line(\"" << entry.name << " " << l <<"\");" << std::endl; | |
586 | ||
587 | if (entry.ifdef.size()) | |
588 | fout << "#endif" << std::endl; | |
589 | } | |
590 | ||
591 | fout << " cfg_filename = nullptr;" << std::endl << | |
592 | "}" << std::endl << std::endl; | |
593 | } | |
594 | ||
595 | void | |
596 | Entry::genParseAlias(const std::string &aName, std::ostream &fout) const | |
597 | { | |
598 | fout << " if (!strcmp(token, \"" << aName << "\")) {" << std::endl; | |
599 | if (ifdef.size()) | |
600 | fout << "#if " << ifdef << std::endl; | |
601 | fout << " cfg_directive = \"" << aName << "\";" << std::endl; | |
602 | fout << " "; | |
603 | if (type.compare("obsolete") == 0) { | |
604 | fout << "debugs(0, DBG_CRITICAL, \"ERROR: Directive '" << aName << "' is obsolete.\");\n"; | |
605 | for (const auto &l : doc) { | |
606 | // offset line to strip initial whitespace tab byte | |
607 | fout << " debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), \"" << aName << " : " << &l[1] << "\");" << std::endl; | |
608 | } | |
609 | fout << " parse_obsolete(token);"; | |
610 | } else if (!loc.size() || loc.compare("none") == 0) { | |
611 | fout << "parse_" << type << "();"; | |
612 | } else if (type.find("::") != std::string::npos) { | |
613 | fout << "ParseDirective<" << type << ">(" << loc << ", LegacyParser);"; | |
614 | } else { | |
615 | fout << "parse_" << type << "(&" << loc << (array_flag ? "[0]" : "") << ");"; | |
616 | } | |
617 | fout << std::endl; | |
618 | fout << " cfg_directive = nullptr;" << std::endl; | |
619 | if (ifdef.size()) { | |
620 | fout << | |
621 | "#else" << std::endl << | |
622 | " debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), \"ERROR: '" << name << "' requires " << available_if(ifdef) << "\");" << std::endl << | |
623 | "#endif" << std::endl; | |
624 | } | |
625 | fout << " return 1;" << std::endl; | |
626 | fout << " };" << std::endl; | |
627 | } | |
628 | ||
629 | void | |
630 | Entry::genParse(std::ostream &fout) const | |
631 | { | |
632 | if (name.compare("comment") == 0) | |
633 | return; | |
634 | ||
635 | // Once for the current directive name | |
636 | genParseAlias(name, fout); | |
637 | ||
638 | // All accepted aliases | |
639 | for (const auto &a : alias) { | |
640 | genParseAlias(a, fout); | |
641 | } | |
642 | } | |
643 | ||
644 | static void | |
645 | gen_parse(const EntryList &head, std::ostream &fout) | |
646 | { | |
647 | fout << | |
648 | "static int\n" | |
649 | "parse_line(char *buff)\n" | |
650 | "{\n" | |
651 | "\tchar\t*token;\n" | |
652 | "\tif ((token = strtok(buff, \" \\t\")) == NULL) \n" | |
653 | "\t\treturn 1;\t/* ignore empty lines */\n" | |
654 | "\tConfigParser::SetCfgLine(strtok(nullptr, \"\"));\n"; | |
655 | ||
656 | for (const auto &e : head) | |
657 | e.genParse(fout); | |
658 | ||
659 | fout << "\treturn 0; /* failure */\n" | |
660 | "}\n\n"; | |
661 | ||
662 | } | |
663 | ||
664 | static void | |
665 | gen_dump(const EntryList &head, std::ostream &fout) | |
666 | { | |
667 | fout << | |
668 | "static void" << std::endl << | |
669 | "dump_config(StoreEntry *entry)" << std::endl << | |
670 | "{" << std::endl << | |
671 | " debugs(5, 4, MYNAME);" << std::endl; | |
672 | ||
673 | for (const auto &e : head) { | |
674 | ||
675 | if (!e.loc.size() || e.loc.compare("none") == 0) | |
676 | continue; | |
677 | ||
678 | if (e.name.compare("comment") == 0) | |
679 | continue; | |
680 | ||
681 | if (e.ifdef.size()) | |
682 | fout << "#if " << e.ifdef << std::endl; | |
683 | ||
684 | if (e.type.find("::") != std::string::npos) | |
685 | fout << " DumpDirective<" << e.type << ">(" << e.loc << ", entry, \"" << e.name << "\");\n"; | |
686 | else | |
687 | fout << " dump_" << e.type << "(entry, \"" << e.name << "\", " << e.loc << ");" << std::endl; | |
688 | ||
689 | if (e.ifdef.size()) | |
690 | fout << "#endif" << std::endl; | |
691 | } | |
692 | ||
693 | fout << "}" << std::endl << std::endl; | |
694 | } | |
695 | ||
696 | static void | |
697 | gen_free(const EntryList &head, std::ostream &fout) | |
698 | { | |
699 | fout << | |
700 | "static void" << std::endl << | |
701 | "free_all(void)" << std::endl << | |
702 | "{" << std::endl << | |
703 | " debugs(5, 4, MYNAME);" << std::endl; | |
704 | ||
705 | for (const auto &e : head) { | |
706 | if (!e.loc.size() || e.loc.compare("none") == 0) | |
707 | continue; | |
708 | ||
709 | if (e.name.compare("comment") == 0) | |
710 | continue; | |
711 | ||
712 | if (e.ifdef.size()) | |
713 | fout << "#if " << e.ifdef << std::endl; | |
714 | ||
715 | if (e.type.find("::") != std::string::npos) | |
716 | fout << " FreeDirective<" << e.type << ">(" << e.loc << ");\n"; | |
717 | else | |
718 | fout << " free_" << e.type << "(&" << e.loc << (e.array_flag ? "[0]" : "") << ");" << std::endl; | |
719 | ||
720 | if (e.ifdef.size()) | |
721 | fout << "#endif" << std::endl; | |
722 | } | |
723 | ||
724 | fout << "}" << std::endl << std::endl; | |
725 | } | |
726 | ||
727 | static bool | |
728 | isDefined(const std::string &name) | |
729 | { | |
730 | if (!name.size()) | |
731 | return true; | |
732 | ||
733 | for (int i = 0; defines[i].name; ++i) { | |
734 | if (name.compare(defines[i].name) == 0) | |
735 | return defines[i].defined; | |
736 | } | |
737 | ||
738 | return false; | |
739 | } | |
740 | ||
741 | static const char * | |
742 | available_if(const std::string &name) | |
743 | { | |
744 | assert(name.size()); | |
745 | ||
746 | for (int i = 0; defines[i].name; ++i) { | |
747 | if (name.compare(defines[i].name) == 0) | |
748 | return defines[i].enable; | |
749 | } | |
750 | ||
751 | return name.c_str(); | |
752 | } | |
753 | ||
754 | static void | |
755 | gen_conf(const EntryList &head, std::ostream &fout, bool verbose_output) | |
756 | { | |
757 | for (const auto &entry : head) { | |
758 | char buf[8192]; | |
759 | LineList def; | |
760 | int enabled = 1; | |
761 | ||
762 | // Display TAG: line | |
763 | if (!entry.name.compare("comment")) | |
764 | (void) 0; | |
765 | else if (!entry.name.compare("obsolete")) | |
766 | (void) 0; | |
767 | else if (verbose_output) { | |
768 | fout << "# TAG: " << entry.name; | |
769 | ||
770 | if (entry.comment.size()) | |
771 | fout << "\t" << entry.comment; | |
772 | ||
773 | fout << std::endl; | |
774 | } | |
775 | ||
776 | // Display --enable/--disable disclaimer | |
777 | if (!isDefined(entry.ifdef)) { | |
778 | if (verbose_output) { | |
779 | fout << "# Note: This option is only available if Squid is rebuilt with the" << std::endl << | |
780 | "# " << available_if(entry.ifdef) << std::endl << | |
781 | "#" << std::endl; | |
782 | } | |
783 | enabled = 0; | |
784 | } | |
785 | ||
786 | // Display DOC_START section | |
787 | if (verbose_output && entry.doc.size()) { | |
788 | for (const auto &line : entry.doc) { | |
789 | fout << "#" << line << std::endl; | |
790 | } | |
791 | } | |
792 | ||
793 | if (entry.defaults.docs.size()) { | |
794 | // Display the DEFAULT_DOC line(s) | |
795 | def = entry.defaults.docs; | |
796 | } else { | |
797 | if (entry.defaults.preset.size() && entry.defaults.preset.front().compare("none") != 0) { | |
798 | // Display DEFAULT: line(s) | |
799 | for (const auto &l : entry.defaults.preset) { | |
800 | snprintf(buf, sizeof(buf), "%s %s", entry.name.c_str(), l.c_str()); | |
801 | def.push_back(buf); | |
802 | } | |
803 | } else if (entry.defaults.if_none.size()) { | |
804 | // Display DEFAULT_IF_NONE: line(s) | |
805 | for (const auto &l : entry.defaults.if_none) { | |
806 | snprintf(buf, sizeof(buf), "%s %s", entry.name.c_str(), l.c_str()); | |
807 | def.push_back(buf); | |
808 | } | |
809 | } | |
810 | } | |
811 | ||
812 | // Display "none" if no default is set or comments to display | |
813 | if (def.empty() && entry.cfgLines.empty() && entry.name.compare("comment") != 0) | |
814 | def.push_back("none"); | |
815 | ||
816 | if (verbose_output && def.size()) { | |
817 | fout << "#Default:\n"; | |
818 | while (def.size()) { | |
819 | fout << "# " << def.front() << std::endl; | |
820 | def.pop_front(); | |
821 | } | |
822 | if (entry.doc.empty() && entry.cfgLines.empty()) | |
823 | fout << std::endl; | |
824 | } | |
825 | ||
826 | if (verbose_output && entry.cfgLines.size()) | |
827 | fout << "#" << std::endl; | |
828 | ||
829 | if (enabled || verbose_output) { | |
830 | for (const auto &line : entry.cfgLines) { | |
831 | if (!enabled && line.at(0) != '#') | |
832 | fout << "#"; | |
833 | fout << line << std::endl; | |
834 | } | |
835 | } | |
836 | ||
837 | if (verbose_output && entry.doc.size()) { | |
838 | fout << std::endl; | |
839 | } | |
840 | } | |
841 | } | |
842 | ||
843 | static const char * | |
844 | gen_quote_escape(const std::string &var) | |
845 | { | |
846 | static std::string esc; | |
847 | esc.clear(); | |
848 | ||
849 | for (const auto c : var) { | |
850 | switch (c) { | |
851 | case '"': | |
852 | case '\\': | |
853 | esc += '\\'; | |
854 | [[fallthrough]]; | |
855 | default: | |
856 | esc += c; | |
857 | } | |
858 | } | |
859 | ||
860 | return esc.c_str(); | |
861 | } | |
862 |