Branch data Line data Source code
1 : : %top{
2 : : /*
3 : : * Scanner for the configuration file
4 : : *
5 : : * Copyright (c) 2000-2026, PostgreSQL Global Development Group
6 : : *
7 : : * src/backend/utils/misc/guc-file.l
8 : : */
9 : :
10 : : #include "postgres.h"
11 : :
12 : : #include <ctype.h>
13 : : #include <unistd.h>
14 : :
15 : : #include "common/file_utils.h"
16 : : #include "guc_internal.h"
17 : : #include "mb/pg_wchar.h"
18 : : #include "miscadmin.h"
19 : : #include "storage/fd.h"
20 : : #include "utils/conffiles.h"
21 : : #include "utils/memutils.h"
22 : : }
23 : :
24 : :
25 : : %{
26 : : /*
27 : : * flex emits a yy_fatal_error() function that it calls in response to
28 : : * critical errors like malloc failure, file I/O errors, and detection of
29 : : * internal inconsistency. That function prints a message and calls exit().
30 : : * Mutate it to instead call our handler, which jumps out of the parser.
31 : : */
32 : : #undef fprintf
33 : : #define fprintf(file, fmt, msg) GUC_flex_fatal(msg)
34 : :
35 : : enum
36 : : {
37 : : GUC_ID = 1,
38 : : GUC_STRING = 2,
39 : : GUC_INTEGER = 3,
40 : : GUC_REAL = 4,
41 : : GUC_EQUALS = 5,
42 : : GUC_UNQUOTED_STRING = 6,
43 : : GUC_QUALIFIED_ID = 7,
44 : : GUC_EOL = 99,
45 : : GUC_ERROR = 100
46 : : };
47 : :
48 : : static unsigned int ConfigFileLineno;
49 : : static const char *GUC_flex_fatal_errmsg;
50 : : static sigjmp_buf *GUC_flex_fatal_jmp;
51 : :
52 : : static void FreeConfigVariable(ConfigVariable *item);
53 : :
54 : : static int GUC_flex_fatal(const char *msg);
55 : :
56 : : /* LCOV_EXCL_START */
57 : :
58 : : %}
59 : :
60 : : %option reentrant
61 : : %option 8bit
62 : : %option never-interactive
63 : : %option nodefault
64 : : %option noinput
65 : : %option nounput
66 : : %option noyywrap
67 : : %option warn
68 : : %option prefix="GUC_yy"
69 : :
70 : :
71 : : SIGN ("-"|"+")
72 : : DIGIT [0-9]
73 : : HEXDIGIT [0-9a-fA-F]
74 : :
75 : : UNIT_LETTER [a-zA-Z]
76 : :
77 : : INTEGER {SIGN}?({DIGIT}+|0x{HEXDIGIT}+){UNIT_LETTER}*
78 : :
79 : : EXPONENT [Ee]{SIGN}?{DIGIT}+
80 : : REAL {SIGN}?{DIGIT}*"."{DIGIT}*{EXPONENT}?
81 : :
82 : : LETTER [A-Za-z_\200-\377]
83 : : LETTER_OR_DIGIT [A-Za-z_0-9\200-\377]
84 : :
85 : : ID {LETTER}{LETTER_OR_DIGIT}*
86 : : QUALIFIED_ID {ID}"."{ID}
87 : :
88 : : UNQUOTED_STRING {LETTER}({LETTER_OR_DIGIT}|[-._:/])*
89 : : STRING \'([^'\\\n]|\\.|\'\')*\'
90 : :
91 : : %%
92 : :
93 : : \n ConfigFileLineno++; return GUC_EOL;
94 : : [ \t\r]+ /* eat whitespace */
95 : : #.* /* eat comment (.* matches anything until newline) */
96 : :
97 : : {ID} return GUC_ID;
98 : : {QUALIFIED_ID} return GUC_QUALIFIED_ID;
99 : : {STRING} return GUC_STRING;
100 : : {UNQUOTED_STRING} return GUC_UNQUOTED_STRING;
101 : : {INTEGER} return GUC_INTEGER;
102 : : {REAL} return GUC_REAL;
103 : : = return GUC_EQUALS;
104 : :
105 : : . return GUC_ERROR;
106 : :
107 : : %%
108 : :
109 : : /* LCOV_EXCL_STOP */
110 : :
111 : : /*
112 : : * Exported function to read and process the configuration file. The
113 : : * parameter indicates in what context the file is being read --- either
114 : : * postmaster startup (including standalone-backend startup) or SIGHUP.
115 : : * All options mentioned in the configuration file are set to new values.
116 : : * If a hard error occurs, no values will be changed. (There can also be
117 : : * errors that prevent just one value from being changed.)
118 : : */
119 : : void
120 : 12 : ProcessConfigFile(GucContext context)
121 : : {
122 : 12 : int elevel;
123 : 12 : MemoryContext config_cxt;
124 : 12 : MemoryContext caller_cxt;
125 : :
126 : : /*
127 : : * Config files are processed on startup (by the postmaster only) and on
128 : : * SIGHUP (by the postmaster and its children)
129 : : */
130 [ + - + - ]: 12 : Assert((context == PGC_POSTMASTER && !IsUnderPostmaster) ||
131 : : context == PGC_SIGHUP);
132 : :
133 : : /*
134 : : * To avoid cluttering the log, only the postmaster bleats loudly about
135 : : * problems with the config file.
136 : : */
137 : 12 : elevel = IsUnderPostmaster ? DEBUG2 : LOG;
138 : :
139 : : /*
140 : : * This function is usually called within a process-lifespan memory
141 : : * context. To ensure that any memory leaked during GUC processing does
142 : : * not accumulate across repeated SIGHUP cycles, do the work in a private
143 : : * context that we can free at exit.
144 : : */
145 : 12 : config_cxt = AllocSetContextCreate(CurrentMemoryContext,
146 : : "config file processing",
147 : : ALLOCSET_DEFAULT_SIZES);
148 : 12 : caller_cxt = MemoryContextSwitchTo(config_cxt);
149 : :
150 : : /*
151 : : * Read and apply the config file. We don't need to examine the result.
152 : : */
153 : 12 : (void) ProcessConfigFileInternal(context, true, elevel);
154 : :
155 : : /* Clean up */
156 : 12 : MemoryContextSwitchTo(caller_cxt);
157 : 12 : MemoryContextDelete(config_cxt);
158 : 12 : }
159 : :
160 : : /*
161 : : * Read and parse a single configuration file. This function recurses
162 : : * to handle "include" directives.
163 : : *
164 : : * If "strict" is true, treat failure to open the config file as an error,
165 : : * otherwise just skip the file.
166 : : *
167 : : * calling_file/calling_lineno identify the source of the request.
168 : : * Pass NULL/0 if not recursing from an inclusion request.
169 : : *
170 : : * See ParseConfigFp for further details. This one merely adds opening the
171 : : * config file rather than working from a caller-supplied file descriptor,
172 : : * and absolute-ifying the path name if necessary.
173 : : */
174 : : bool
175 : 20 : ParseConfigFile(const char *config_file, bool strict,
176 : : const char *calling_file, int calling_lineno,
177 : : int depth, int elevel,
178 : : ConfigVariable **head_p,
179 : : ConfigVariable **tail_p)
180 : : {
181 : 20 : char *abs_path;
182 : 20 : bool OK = true;
183 : 20 : FILE *fp;
184 : :
185 : : /*
186 : : * Reject file name that is all-blank (including empty), as that leads to
187 : : * confusion --- we'd try to read the containing directory as a file.
188 : : */
189 [ + - ]: 20 : if (strspn(config_file, " \t\r\n") == strlen(config_file))
190 : : {
191 [ # # # # : 0 : ereport(elevel,
# # # # #
# ]
192 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
193 : : errmsg("empty configuration file name: \"%s\"",
194 : : config_file)));
195 : 0 : record_config_file_error("empty configuration file name",
196 : 0 : calling_file, calling_lineno,
197 : 0 : head_p, tail_p);
198 : 0 : return false;
199 : : }
200 : :
201 : : /*
202 : : * Reject too-deep include nesting depth. This is just a safety check to
203 : : * avoid dumping core due to stack overflow if an include file loops back
204 : : * to itself. The maximum nesting depth is pretty arbitrary.
205 : : */
206 [ - + ]: 20 : if (depth > CONF_FILE_MAX_DEPTH)
207 : : {
208 [ # # # # : 0 : ereport(elevel,
# # # # #
# ]
209 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
210 : : errmsg("could not open configuration file \"%s\": maximum nesting depth exceeded",
211 : : config_file)));
212 : 0 : record_config_file_error("nesting depth exceeded",
213 : 0 : calling_file, calling_lineno,
214 : 0 : head_p, tail_p);
215 : 0 : return false;
216 : : }
217 : :
218 : 20 : abs_path = AbsoluteConfigLocation(config_file, calling_file);
219 : :
220 : : /*
221 : : * Reject direct recursion. Indirect recursion is also possible, but it's
222 : : * harder to detect and so doesn't seem worth the trouble. (We test at
223 : : * this step because the canonicalization done by AbsoluteConfigLocation
224 : : * makes it more likely that a simple strcmp comparison will match.)
225 : : */
226 [ - + # # ]: 20 : if (calling_file && strcmp(abs_path, calling_file) == 0)
227 : : {
228 [ # # # # : 0 : ereport(elevel,
# # # # #
# ]
229 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
230 : : errmsg("configuration file recursion in \"%s\"",
231 : : calling_file)));
232 : 0 : record_config_file_error("configuration file recursion",
233 : 0 : calling_file, calling_lineno,
234 : 0 : head_p, tail_p);
235 : 0 : pfree(abs_path);
236 : 0 : return false;
237 : : }
238 : :
239 : 20 : fp = AllocateFile(abs_path, "r");
240 [ + + ]: 20 : if (!fp)
241 : : {
242 [ - + ]: 2 : if (strict)
243 : : {
244 [ # # # # : 0 : ereport(elevel,
# # # # #
# ]
245 : : (errcode_for_file_access(),
246 : : errmsg("could not open configuration file \"%s\": %m",
247 : : abs_path)));
248 : 0 : record_config_file_error(psprintf("could not open file \"%s\"",
249 : 0 : abs_path),
250 : 0 : calling_file, calling_lineno,
251 : 0 : head_p, tail_p);
252 : 0 : OK = false;
253 : 0 : }
254 : : else
255 : : {
256 [ - + + - ]: 2 : ereport(LOG,
257 : : (errmsg("skipping missing configuration file \"%s\"",
258 : : abs_path)));
259 : : }
260 : 2 : goto cleanup;
261 : : }
262 : :
263 : 18 : OK = ParseConfigFp(fp, abs_path, depth, elevel, head_p, tail_p);
264 : :
265 : : cleanup:
266 [ + + ]: 20 : if (fp)
267 : 18 : FreeFile(fp);
268 : 20 : pfree(abs_path);
269 : :
270 : 20 : return OK;
271 : 20 : }
272 : :
273 : : /*
274 : : * Capture an error message in the ConfigVariable list returned by
275 : : * config file parsing.
276 : : */
277 : : void
278 : 0 : record_config_file_error(const char *errmsg,
279 : : const char *config_file,
280 : : int lineno,
281 : : ConfigVariable **head_p,
282 : : ConfigVariable **tail_p)
283 : : {
284 : 0 : ConfigVariable *item;
285 : :
286 : 0 : item = palloc_object(ConfigVariable);
287 : 0 : item->name = NULL;
288 : 0 : item->value = NULL;
289 : 0 : item->errmsg = pstrdup(errmsg);
290 [ # # ]: 0 : item->filename = config_file ? pstrdup(config_file) : NULL;
291 : 0 : item->sourceline = lineno;
292 : 0 : item->ignore = true;
293 : 0 : item->applied = false;
294 : 0 : item->next = NULL;
295 [ # # ]: 0 : if (*head_p == NULL)
296 : 0 : *head_p = item;
297 : : else
298 : 0 : (*tail_p)->next = item;
299 : 0 : *tail_p = item;
300 : 0 : }
301 : :
302 : : /*
303 : : * Flex fatal errors bring us here. Stash the error message and jump back to
304 : : * ParseConfigFp(). Assume all msg arguments point to string constants; this
305 : : * holds for all currently known flex versions. Otherwise, we would need to
306 : : * copy the message.
307 : : *
308 : : * We return "int" since this takes the place of calls to fprintf().
309 : : */
310 : : static int
311 : 0 : GUC_flex_fatal(const char *msg)
312 : : {
313 : 0 : GUC_flex_fatal_errmsg = msg;
314 : 0 : siglongjmp(*GUC_flex_fatal_jmp, 1);
315 : : return 0; /* keep compiler quiet */
316 : : }
317 : :
318 : : /*
319 : : * Read and parse a single configuration file. This function recurses
320 : : * to handle "include" directives.
321 : : *
322 : : * Input parameters:
323 : : * fp: file pointer from AllocateFile for the configuration file to parse
324 : : * config_file: absolute or relative path name of the configuration file
325 : : * depth: recursion depth (should be CONF_FILE_START_DEPTH in the outermost
326 : : * call)
327 : : * elevel: error logging level to use
328 : : * Input/Output parameters:
329 : : * head_p, tail_p: head and tail of linked list of name/value pairs
330 : : *
331 : : * *head_p and *tail_p must be initialized, either to NULL or valid pointers
332 : : * to a ConfigVariable list, before calling the outer recursion level. Any
333 : : * name-value pairs read from the input file(s) will be appended to the list.
334 : : * Error reports will also be appended to the list, if elevel < ERROR.
335 : : *
336 : : * Returns TRUE if successful, FALSE if an error occurred. The error has
337 : : * already been ereport'd, it is only necessary for the caller to clean up
338 : : * its own state and release the ConfigVariable list.
339 : : *
340 : : * Note: if elevel >= ERROR then an error will not return control to the
341 : : * caller, so there is no need to check the return value in that case.
342 : : *
343 : : * Note: this function is used to parse not only postgresql.conf, but
344 : : * various other configuration files that use the same "name = value"
345 : : * syntax. Hence, do not do anything here or in the subsidiary routines
346 : : * ParseConfigFile/ParseConfigDirectory that assumes we are processing
347 : : * GUCs specifically.
348 : : */
349 : : bool
350 : 235 : ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
351 : : ConfigVariable **head_p, ConfigVariable **tail_p)
352 : : {
353 : 235 : volatile bool OK = true;
354 : 235 : unsigned int save_ConfigFileLineno = ConfigFileLineno;
355 : 235 : sigjmp_buf *save_GUC_flex_fatal_jmp = GUC_flex_fatal_jmp;
356 : 235 : sigjmp_buf flex_fatal_jmp;
357 : 235 : yyscan_t scanner;
358 : 235 : struct yyguts_t *yyg; /* needed for yytext macro */
359 : 235 : volatile YY_BUFFER_STATE lex_buffer = NULL;
360 : 235 : int errorcount;
361 : 235 : int token;
362 : :
363 [ - + ]: 235 : if (sigsetjmp(flex_fatal_jmp, 1) == 0)
364 : 235 : GUC_flex_fatal_jmp = &flex_fatal_jmp;
365 : : else
366 : : {
367 : : /*
368 : : * Regain control after a fatal, internal flex error. It may have
369 : : * corrupted parser state. Consequently, abandon the file, but trust
370 : : * that the state remains sane enough for yy_delete_buffer().
371 : : */
372 [ # # # # : 0 : elog(elevel, "%s at file \"%s\" line %u",
# # # # #
# ]
373 : : GUC_flex_fatal_errmsg, config_file, ConfigFileLineno);
374 : 0 : record_config_file_error(GUC_flex_fatal_errmsg,
375 : 0 : config_file, ConfigFileLineno,
376 : 0 : head_p, tail_p);
377 : 0 : OK = false;
378 : 0 : goto cleanup;
379 : : }
380 : :
381 : : /*
382 : : * Parse
383 : : */
384 : 235 : ConfigFileLineno = 1;
385 : 235 : errorcount = 0;
386 : :
387 [ + - ]: 235 : if (yylex_init(&scanner) != 0)
388 [ # # # # : 0 : elog(elevel, "yylex_init() failed: %m");
# # # # #
# ]
389 : 235 : yyg = (struct yyguts_t *) scanner;
390 : :
391 : 235 : lex_buffer = yy_create_buffer(fp, YY_BUF_SIZE, scanner);
392 : 235 : yy_switch_to_buffer(lex_buffer, scanner);
393 : :
394 : : /* This loop iterates once per logical line */
395 [ + + ]: 9400 : while ((token = yylex(scanner)))
396 : : {
397 : 9165 : char *opt_name = NULL;
398 : 9165 : char *opt_value = NULL;
399 : 9165 : ConfigVariable *item;
400 : :
401 [ + + ]: 9165 : if (token == GUC_EOL) /* empty or comment line */
402 : 8036 : continue;
403 : :
404 : : /* first token on line is option name */
405 [ + + + - ]: 1129 : if (token != GUC_ID && token != GUC_QUALIFIED_ID)
406 : 0 : goto parse_error;
407 : 1129 : opt_name = pstrdup(yytext);
408 : :
409 : : /* next we have an optional equal sign; discard if present */
410 : 1129 : token = yylex(scanner);
411 [ - + ]: 1129 : if (token == GUC_EQUALS)
412 : 1129 : token = yylex(scanner);
413 : :
414 : : /* now we must have the option value */
415 [ + + ]: 1129 : if (token != GUC_ID &&
416 [ + + ]: 789 : token != GUC_STRING &&
417 [ - + ]: 62 : token != GUC_INTEGER &&
418 [ # # # # ]: 0 : token != GUC_REAL &&
419 : 0 : token != GUC_UNQUOTED_STRING)
420 : 0 : goto parse_error;
421 [ + + ]: 1129 : if (token == GUC_STRING) /* strip quotes and escapes */
422 : 727 : opt_value = DeescapeQuotedString(yytext);
423 : : else
424 : 402 : opt_value = pstrdup(yytext);
425 : :
426 : : /* now we'd like an end of line, or possibly EOF */
427 : 1129 : token = yylex(scanner);
428 [ + - ]: 1129 : if (token != GUC_EOL)
429 : : {
430 [ # # ]: 0 : if (token != 0)
431 : 0 : goto parse_error;
432 : : /* treat EOF like \n for line numbering purposes, cf bug 4752 */
433 : 0 : ConfigFileLineno++;
434 : 0 : }
435 : :
436 : : /* OK, process the option name and value */
437 [ + - ]: 1129 : if (guc_name_compare(opt_name, "include_dir") == 0)
438 : : {
439 : : /*
440 : : * An include_dir directive isn't a variable and should be
441 : : * processed immediately.
442 : : */
443 [ # # # # ]: 0 : if (!ParseConfigDirectory(opt_value,
444 : 0 : config_file, ConfigFileLineno - 1,
445 : 0 : depth + 1, elevel,
446 : 0 : head_p, tail_p))
447 : 0 : OK = false;
448 : 0 : yy_switch_to_buffer(lex_buffer, scanner);
449 : 0 : pfree(opt_name);
450 : 0 : pfree(opt_value);
451 : 0 : }
452 [ + - ]: 1129 : else if (guc_name_compare(opt_name, "include_if_exists") == 0)
453 : : {
454 : : /*
455 : : * An include_if_exists directive isn't a variable and should be
456 : : * processed immediately.
457 : : */
458 [ # # # # ]: 0 : if (!ParseConfigFile(opt_value, false,
459 : 0 : config_file, ConfigFileLineno - 1,
460 : 0 : depth + 1, elevel,
461 : 0 : head_p, tail_p))
462 : 0 : OK = false;
463 : 0 : yy_switch_to_buffer(lex_buffer, scanner);
464 : 0 : pfree(opt_name);
465 : 0 : pfree(opt_value);
466 : 0 : }
467 [ + - ]: 1129 : else if (guc_name_compare(opt_name, "include") == 0)
468 : : {
469 : : /*
470 : : * An include directive isn't a variable and should be processed
471 : : * immediately.
472 : : */
473 [ # # # # ]: 0 : if (!ParseConfigFile(opt_value, true,
474 : 0 : config_file, ConfigFileLineno - 1,
475 : 0 : depth + 1, elevel,
476 : 0 : head_p, tail_p))
477 : 0 : OK = false;
478 : 0 : yy_switch_to_buffer(lex_buffer, scanner);
479 : 0 : pfree(opt_name);
480 : 0 : pfree(opt_value);
481 : 0 : }
482 : : else
483 : : {
484 : : /* ordinary variable, append to list */
485 : 1129 : item = palloc_object(ConfigVariable);
486 : 1129 : item->name = opt_name;
487 : 1129 : item->value = opt_value;
488 : 1129 : item->errmsg = NULL;
489 : 1129 : item->filename = pstrdup(config_file);
490 : 1129 : item->sourceline = ConfigFileLineno - 1;
491 : 1129 : item->ignore = false;
492 : 1129 : item->applied = false;
493 : 1129 : item->next = NULL;
494 [ + + ]: 1129 : if (*head_p == NULL)
495 : 226 : *head_p = item;
496 : : else
497 : 903 : (*tail_p)->next = item;
498 : 1129 : *tail_p = item;
499 : : }
500 : :
501 : : /* break out of loop if read EOF, else loop for next line */
502 [ + - ]: 1129 : if (token == 0)
503 : 0 : break;
504 : 1129 : continue;
505 : :
506 : : parse_error:
507 : : /* release storage if we allocated any on this line */
508 [ # # ]: 0 : if (opt_name)
509 : 0 : pfree(opt_name);
510 [ # # ]: 0 : if (opt_value)
511 : 0 : pfree(opt_value);
512 : :
513 : : /* report the error */
514 [ # # # # ]: 0 : if (token == GUC_EOL || token == 0)
515 : : {
516 [ # # # # : 0 : ereport(elevel,
# # # # #
# ]
517 : : (errcode(ERRCODE_SYNTAX_ERROR),
518 : : errmsg("syntax error in file \"%s\" line %u, near end of line",
519 : : config_file, ConfigFileLineno - 1)));
520 : 0 : record_config_file_error("syntax error",
521 : 0 : config_file, ConfigFileLineno - 1,
522 : 0 : head_p, tail_p);
523 : 0 : }
524 : : else
525 : : {
526 [ # # # # : 0 : ereport(elevel,
# # # # #
# ]
527 : : (errcode(ERRCODE_SYNTAX_ERROR),
528 : : errmsg("syntax error in file \"%s\" line %u, near token \"%s\"",
529 : : config_file, ConfigFileLineno, yytext)));
530 : 0 : record_config_file_error("syntax error",
531 : 0 : config_file, ConfigFileLineno,
532 : 0 : head_p, tail_p);
533 : : }
534 : 0 : OK = false;
535 : 0 : errorcount++;
536 : :
537 : : /*
538 : : * To avoid producing too much noise when fed a totally bogus file,
539 : : * give up after 100 syntax errors per file (an arbitrary number).
540 : : * Also, if we're only logging the errors at DEBUG level anyway, might
541 : : * as well give up immediately. (This prevents postmaster children
542 : : * from bloating the logs with duplicate complaints.)
543 : : */
544 [ # # # # ]: 0 : if (errorcount >= 100 || elevel <= DEBUG1)
545 : : {
546 [ # # # # : 0 : ereport(elevel,
# # # # #
# ]
547 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
548 : : errmsg("too many syntax errors found, abandoning file \"%s\"",
549 : : config_file)));
550 : 0 : break;
551 : : }
552 : :
553 : : /* resync to next end-of-line or EOF */
554 [ # # # # ]: 0 : while (token != GUC_EOL && token != 0)
555 : 0 : token = yylex(scanner);
556 : : /* break out of loop on EOF */
557 [ # # ]: 0 : if (token == 0)
558 : 0 : break;
559 [ - + - ]: 9165 : }
560 : :
561 : : cleanup:
562 : 235 : yy_delete_buffer(lex_buffer, scanner);
563 : 235 : yylex_destroy(scanner);
564 : : /* Each recursion level must save and restore these static variables. */
565 : 235 : ConfigFileLineno = save_ConfigFileLineno;
566 : 235 : GUC_flex_fatal_jmp = save_GUC_flex_fatal_jmp;
567 : 470 : return OK;
568 : 235 : }
569 : :
570 : : /*
571 : : * Read and parse all config files in a subdirectory in alphabetical order
572 : : *
573 : : * includedir is the absolute or relative path to the subdirectory to scan.
574 : : *
575 : : * calling_file/calling_lineno identify the source of the request.
576 : : * Pass NULL/0 if not recursing from an inclusion request.
577 : : *
578 : : * See ParseConfigFp for further details.
579 : : */
580 : : bool
581 : 0 : ParseConfigDirectory(const char *includedir,
582 : : const char *calling_file, int calling_lineno,
583 : : int depth, int elevel,
584 : : ConfigVariable **head_p,
585 : : ConfigVariable **tail_p)
586 : : {
587 : 0 : char *err_msg;
588 : 0 : char **filenames;
589 : 0 : int num_filenames;
590 : :
591 : 0 : filenames = GetConfFilesInDir(includedir, calling_file, elevel,
592 : : &num_filenames, &err_msg);
593 : :
594 [ # # ]: 0 : if (!filenames)
595 : : {
596 : 0 : record_config_file_error(err_msg, calling_file, calling_lineno, head_p,
597 : 0 : tail_p);
598 : 0 : return false;
599 : : }
600 : :
601 [ # # # # ]: 0 : for (int i = 0; i < num_filenames; i++)
602 : : {
603 [ # # # # ]: 0 : if (!ParseConfigFile(filenames[i], true,
604 : 0 : calling_file, calling_lineno,
605 : 0 : depth, elevel,
606 : 0 : head_p, tail_p))
607 : 0 : return false;
608 : 0 : }
609 : :
610 : 0 : return true;
611 : 0 : }
612 : :
613 : : /*
614 : : * Free a list of ConfigVariables, including the names and the values
615 : : */
616 : : void
617 : 217 : FreeConfigVariables(ConfigVariable *list)
618 : : {
619 : 217 : ConfigVariable *item;
620 : :
621 : 217 : item = list;
622 [ + + ]: 1158 : while (item)
623 : : {
624 : 941 : ConfigVariable *next = item->next;
625 : :
626 : 941 : FreeConfigVariable(item);
627 : 941 : item = next;
628 : 941 : }
629 : 217 : }
630 : :
631 : : /*
632 : : * Free a single ConfigVariable
633 : : */
634 : : static void
635 : 941 : FreeConfigVariable(ConfigVariable *item)
636 : : {
637 [ - + ]: 941 : if (item->name)
638 : 941 : pfree(item->name);
639 [ - + ]: 941 : if (item->value)
640 : 941 : pfree(item->value);
641 [ + - ]: 941 : if (item->errmsg)
642 : 0 : pfree(item->errmsg);
643 [ - + ]: 941 : if (item->filename)
644 : 941 : pfree(item->filename);
645 : 941 : pfree(item);
646 : 941 : }
647 : :
648 : :
649 : : /*
650 : : * DeescapeQuotedString
651 : : *
652 : : * Strip the quotes surrounding the given string, and collapse any embedded
653 : : * '' sequences and backslash escapes.
654 : : *
655 : : * The string returned is palloc'd and should eventually be pfree'd by the
656 : : * caller.
657 : : *
658 : : * This is exported because it is also used by the bootstrap scanner.
659 : : */
660 : : char *
661 : 8147 : DeescapeQuotedString(const char *s)
662 : : {
663 : 8147 : char *newStr;
664 : 8147 : int len,
665 : : i,
666 : : j;
667 : :
668 : : /* We just Assert that there are leading and trailing quotes */
669 [ + - ]: 8147 : Assert(s != NULL && s[0] == '\'');
670 : 8147 : len = strlen(s);
671 [ + - ]: 8147 : Assert(len >= 2);
672 [ + - ]: 8147 : Assert(s[len - 1] == '\'');
673 : :
674 : : /* Skip the leading quote; we'll handle the trailing quote below */
675 : 8147 : s++, len--;
676 : :
677 : : /* Since len still includes trailing quote, this is enough space */
678 : 8147 : newStr = palloc(len);
679 : :
680 [ + + ]: 157402 : for (i = 0, j = 0; i < len; i++)
681 : : {
682 [ - + ]: 149255 : if (s[i] == '\\')
683 : : {
684 : 0 : i++;
685 [ # # # # : 0 : switch (s[i])
# # # ]
686 : : {
687 : : case 'b':
688 : 0 : newStr[j] = '\b';
689 : 0 : break;
690 : : case 'f':
691 : 0 : newStr[j] = '\f';
692 : 0 : break;
693 : : case 'n':
694 : 0 : newStr[j] = '\n';
695 : 0 : break;
696 : : case 'r':
697 : 0 : newStr[j] = '\r';
698 : 0 : break;
699 : : case 't':
700 : 0 : newStr[j] = '\t';
701 : 0 : break;
702 : : case '0':
703 : : case '1':
704 : : case '2':
705 : : case '3':
706 : : case '4':
707 : : case '5':
708 : : case '6':
709 : : case '7':
710 : : {
711 : 0 : int k;
712 : 0 : long octVal = 0;
713 : :
714 [ # # ]: 0 : for (k = 0;
715 [ # # # # ]: 0 : s[i + k] >= '0' && s[i + k] <= '7' && k < 3;
716 : 0 : k++)
717 : 0 : octVal = (octVal << 3) + (s[i + k] - '0');
718 : 0 : i += k - 1;
719 : 0 : newStr[j] = ((char) octVal);
720 : 0 : }
721 : 0 : break;
722 : : default:
723 : 0 : newStr[j] = s[i];
724 : 0 : break;
725 : : } /* switch */
726 : 0 : }
727 [ + + + + ]: 149255 : else if (s[i] == '\'' && s[i + 1] == '\'')
728 : : {
729 : : /* doubled quote becomes just one quote */
730 : 62 : newStr[j] = s[++i];
731 : 62 : }
732 : : else
733 : 149193 : newStr[j] = s[i];
734 : 149255 : j++;
735 : 149255 : }
736 : :
737 : : /* We copied the ending quote to newStr, so replace with \0 */
738 [ + - ]: 8147 : Assert(j > 0 && j <= len);
739 : 8147 : newStr[--j] = '\0';
740 : :
741 : 16294 : return newStr;
742 : 8147 : }
|