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 : 3132961 : \n ConfigFileLineno++; return GUC_EOL;
94 : : [ \t\r]+ /* eat whitespace */
95 : 1173070 : #.* /* eat comment (.* matches anything until newline) */
96 : 2522692 :
97 : 179671 : {ID} return GUC_ID;
98 : 101 : {QUALIFIED_ID} return GUC_QUALIFIED_ID;
99 : 101 : {STRING} return GUC_STRING;
100 : 65384 : {UNQUOTED_STRING} return GUC_UNQUOTED_STRING;
101 : 384 : {INTEGER} return GUC_INTEGER;
102 : 33178 : {REAL} return GUC_REAL;
103 : 0 : = return GUC_EQUALS;
104 : 139156 :
105 : 0 : . return GUC_ERROR;
106 : :
107 : 0 : %%
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 : 3442 : ProcessConfigFile(GucContext context)
121 : : {
122 : : int elevel;
123 : : MemoryContext config_cxt;
124 : : 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 : : 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 [ + + ]: 3442 : 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 : 3442 : config_cxt = AllocSetContextCreate(CurrentMemoryContext,
146 : : "config file processing",
147 : : ALLOCSET_DEFAULT_SIZES);
148 : 3442 : caller_cxt = MemoryContextSwitchTo(config_cxt);
149 : :
150 : : /*
151 : : * Read and apply the config file. We don't need to examine the result.
152 : : */
153 : 3442 : (void) ProcessConfigFileInternal(context, true, elevel);
154 : :
155 : : /* Clean up */
156 : 3441 : MemoryContextSwitchTo(caller_cxt);
157 : 3441 : MemoryContextDelete(config_cxt);
158 : 3441 : }
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 : 5812 : 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 : : char *abs_path;
182 : 5812 : bool OK = true;
183 : : 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 [ - + ]: 5812 : 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 : : calling_file, calling_lineno,
197 : : 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 [ - + ]: 5812 : 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 : : calling_file, calling_lineno,
214 : : head_p, tail_p);
215 : 0 : return false;
216 : : }
217 : :
218 : 5812 : 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 [ + + - + ]: 5812 : 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 : : calling_file, calling_lineno,
234 : : head_p, tail_p);
235 : 0 : pfree(abs_path);
236 : 0 : return false;
237 : : }
238 : :
239 : 5812 : fp = AllocateFile(abs_path, "r");
240 [ + + ]: 5812 : if (!fp)
241 : : {
242 [ - + ]: 116 : 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 : : abs_path),
250 : : calling_file, calling_lineno,
251 : : head_p, tail_p);
252 : 0 : OK = false;
253 : : }
254 : : else
255 : : {
256 [ + - ]: 116 : ereport(LOG,
257 : : (errmsg("skipping missing configuration file \"%s\"",
258 : : abs_path)));
259 : : }
260 : 116 : goto cleanup;
261 : : }
262 : :
263 : 5696 : OK = ParseConfigFp(fp, abs_path, depth, elevel, head_p, tail_p);
264 : :
265 : 5812 : cleanup:
266 [ + + ]: 5812 : if (fp)
267 : 5696 : FreeFile(fp);
268 : 5812 : pfree(abs_path);
269 : :
270 : 5812 : return OK;
271 : : }
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 : : 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 : 14806 : ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
351 : : ConfigVariable **head_p, ConfigVariable **tail_p)
352 : : {
353 : 14806 : volatile bool OK = true;
354 : 14806 : unsigned int save_ConfigFileLineno = ConfigFileLineno;
355 : 14806 : sigjmp_buf *save_GUC_flex_fatal_jmp = GUC_flex_fatal_jmp;
356 : : sigjmp_buf flex_fatal_jmp;
357 : 14806 : volatile yyscan_t scanner = NULL;
358 : 14806 : yyscan_t scanner_init = NULL; /* non-volatile for yylex_init() */
359 : : struct yyguts_t *yyg; /* needed for yytext macro */
360 : 14806 : volatile YY_BUFFER_STATE lex_buffer = NULL;
361 : : int errorcount;
362 : : int token;
363 : :
364 [ + - ]: 14806 : if (sigsetjmp(flex_fatal_jmp, 1) == 0)
365 : 14806 : GUC_flex_fatal_jmp = &flex_fatal_jmp;
366 : : else
367 : : {
368 : : /*
369 : : * Regain control after a fatal, internal flex error. It may have
370 : : * corrupted parser state. Consequently, abandon the file, but trust
371 : : * that the state remains sane enough for yy_delete_buffer().
372 : : */
373 [ # # ]: 0 : elog(elevel, "%s at file \"%s\" line %u",
374 : : GUC_flex_fatal_errmsg, config_file, ConfigFileLineno);
375 : 0 : record_config_file_error(GUC_flex_fatal_errmsg,
376 : : config_file, ConfigFileLineno,
377 : : head_p, tail_p);
378 : 0 : OK = false;
379 : 0 : goto cleanup;
380 : : }
381 : :
382 : : /*
383 : : * Parse
384 : : */
385 : 14806 : ConfigFileLineno = 1;
386 : 14806 : errorcount = 0;
387 : :
388 [ - + ]: 14806 : if (yylex_init(&scanner_init) != 0)
389 : : {
390 [ # # ]: 0 : elog(elevel, "yylex_init() failed: %m");
391 : 0 : goto cleanup;
392 : : }
393 : 14806 : scanner = scanner_init;
394 : 14806 : yyg = (struct yyguts_t *) scanner;
395 : :
396 : 14806 : lex_buffer = yy_create_buffer(fp, YY_BUF_SIZE, scanner);
397 [ - + ]: 14806 : if (lex_buffer == NULL)
398 : : {
399 [ # # ]: 0 : elog(elevel, "yy_create_buffer() failed");
400 : 0 : goto cleanup;
401 : : }
402 : 14806 : yy_switch_to_buffer(lex_buffer, scanner);
403 : :
404 : : /* This loop iterates once per logical line */
405 [ + + ]: 3147767 : while ((token = yylex(scanner)))
406 : 139351 : {
407 : 3132969 : char *opt_name = NULL;
408 : 3132969 : char *opt_value = NULL;
409 : : ConfigVariable *item;
410 : :
411 [ + + ]: 3132969 : if (token == GUC_EOL) /* empty or comment line */
412 : 2993610 : continue;
413 : :
414 : : /* first token on line is option name */
415 [ + + - + ]: 139359 : if (token != GUC_ID && token != GUC_QUALIFIED_ID)
416 : 0 : goto parse_error;
417 : 139359 : opt_name = pstrdup(yytext);
418 : :
419 : : /* next we have an optional equal sign; discard if present */
420 : 139359 : token = yylex(scanner);
421 [ + + ]: 139359 : if (token == GUC_EQUALS)
422 : 139156 : token = yylex(scanner);
423 : :
424 : : /* now we must have the option value */
425 [ + + + + ]: 139359 : if (token != GUC_ID &&
426 [ + + ]: 33562 : token != GUC_STRING &&
427 [ + - ]: 384 : token != GUC_INTEGER &&
428 [ - + ]: 384 : token != GUC_REAL &&
429 : : token != GUC_UNQUOTED_STRING)
430 : 0 : goto parse_error;
431 [ + + ]: 139359 : if (token == GUC_STRING) /* strip quotes and escapes */
432 : 65384 : opt_value = DeescapeQuotedString(yytext);
433 : : else
434 : 73975 : opt_value = pstrdup(yytext);
435 : :
436 : : /* now we'd like an end of line, or possibly EOF */
437 : 139359 : token = yylex(scanner);
438 [ + + ]: 139359 : if (token != GUC_EOL)
439 : : {
440 [ - + ]: 8 : if (token != 0)
441 : 0 : goto parse_error;
442 : : /* treat EOF like \n for line numbering purposes, cf bug 4752 */
443 : 8 : ConfigFileLineno++;
444 : : }
445 : :
446 : : /* OK, process the option name and value */
447 [ - + ]: 139359 : if (guc_name_compare(opt_name, "include_dir") == 0)
448 : : {
449 : : /*
450 : : * An include_dir directive isn't a variable and should be
451 : : * processed immediately.
452 : : */
453 [ # # ]: 0 : if (!ParseConfigDirectory(opt_value,
454 : 0 : config_file, ConfigFileLineno - 1,
455 : : depth + 1, elevel,
456 : : head_p, tail_p))
457 : 0 : OK = false;
458 : 0 : yy_switch_to_buffer(lex_buffer, scanner);
459 : 0 : pfree(opt_name);
460 : 0 : pfree(opt_value);
461 : : }
462 [ + + ]: 139359 : else if (guc_name_compare(opt_name, "include_if_exists") == 0)
463 : : {
464 : : /*
465 : : * An include_if_exists directive isn't a variable and should be
466 : : * processed immediately.
467 : : */
468 [ - + ]: 3 : if (!ParseConfigFile(opt_value, false,
469 : 3 : config_file, ConfigFileLineno - 1,
470 : : depth + 1, elevel,
471 : : head_p, tail_p))
472 : 0 : OK = false;
473 : 3 : yy_switch_to_buffer(lex_buffer, scanner);
474 : 3 : pfree(opt_name);
475 : 3 : pfree(opt_value);
476 : : }
477 [ + + ]: 139356 : else if (guc_name_compare(opt_name, "include") == 0)
478 : : {
479 : : /*
480 : : * An include directive isn't a variable and should be processed
481 : : * immediately.
482 : : */
483 [ - + ]: 200 : if (!ParseConfigFile(opt_value, true,
484 : 200 : config_file, ConfigFileLineno - 1,
485 : : depth + 1, elevel,
486 : : head_p, tail_p))
487 : 0 : OK = false;
488 : 200 : yy_switch_to_buffer(lex_buffer, scanner);
489 : 200 : pfree(opt_name);
490 : 200 : pfree(opt_value);
491 : : }
492 : : else
493 : : {
494 : : /* ordinary variable, append to list */
495 : 139156 : item = palloc_object(ConfigVariable);
496 : 139156 : item->name = opt_name;
497 : 139156 : item->value = opt_value;
498 : 139156 : item->errmsg = NULL;
499 : 139156 : item->filename = pstrdup(config_file);
500 : 139156 : item->sourceline = ConfigFileLineno - 1;
501 : 139156 : item->ignore = false;
502 : 139156 : item->applied = false;
503 : 139156 : item->next = NULL;
504 [ + + ]: 139156 : if (*head_p == NULL)
505 : 12298 : *head_p = item;
506 : : else
507 : 126858 : (*tail_p)->next = item;
508 : 139156 : *tail_p = item;
509 : : }
510 : :
511 : : /* break out of loop if read EOF, else loop for next line */
512 [ + + ]: 139359 : if (token == 0)
513 : 8 : break;
514 : 139351 : continue;
515 : :
516 : 0 : parse_error:
517 : : /* release storage if we allocated any on this line */
518 [ # # ]: 0 : if (opt_name)
519 : 0 : pfree(opt_name);
520 [ # # ]: 0 : if (opt_value)
521 : 0 : pfree(opt_value);
522 : :
523 : : /* report the error */
524 [ # # # # ]: 0 : if (token == GUC_EOL || token == 0)
525 : : {
526 [ # # ]: 0 : ereport(elevel,
527 : : (errcode(ERRCODE_SYNTAX_ERROR),
528 : : errmsg("syntax error in file \"%s\" line %u, near end of line",
529 : : config_file, ConfigFileLineno - 1)));
530 : 0 : record_config_file_error("syntax error",
531 : 0 : config_file, ConfigFileLineno - 1,
532 : : head_p, tail_p);
533 : : }
534 : : else
535 : : {
536 [ # # ]: 0 : ereport(elevel,
537 : : (errcode(ERRCODE_SYNTAX_ERROR),
538 : : errmsg("syntax error in file \"%s\" line %u, near token \"%s\"",
539 : : config_file, ConfigFileLineno, yytext)));
540 : 0 : record_config_file_error("syntax error",
541 : : config_file, ConfigFileLineno,
542 : : head_p, tail_p);
543 : : }
544 : 0 : OK = false;
545 : 0 : errorcount++;
546 : :
547 : : /*
548 : : * To avoid producing too much noise when fed a totally bogus file,
549 : : * give up after 100 syntax errors per file (an arbitrary number).
550 : : * Also, if we're only logging the errors at DEBUG level anyway, might
551 : : * as well give up immediately. (This prevents postmaster children
552 : : * from bloating the logs with duplicate complaints.)
553 : : */
554 [ # # # # ]: 0 : if (errorcount >= 100 || elevel <= DEBUG1)
555 : : {
556 [ # # ]: 0 : ereport(elevel,
557 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
558 : : errmsg("too many syntax errors found, abandoning file \"%s\"",
559 : : config_file)));
560 : 0 : break;
561 : : }
562 : :
563 : : /* resync to next end-of-line or EOF */
564 [ # # # # ]: 0 : while (token != GUC_EOL && token != 0)
565 : 0 : token = yylex(scanner);
566 : : /* break out of loop on EOF */
567 [ # # ]: 0 : if (token == 0)
568 : 0 : break;
569 : : }
570 : :
571 : 14798 : cleanup:
572 [ + - ]: 14806 : if (scanner)
573 : : {
574 : 14806 : yy_delete_buffer(lex_buffer, scanner);
575 : 14806 : yylex_destroy(scanner);
576 : : }
577 : : /* Each recursion level must save and restore these static variables. */
578 : 14806 : ConfigFileLineno = save_ConfigFileLineno;
579 : 14806 : GUC_flex_fatal_jmp = save_GUC_flex_fatal_jmp;
580 : 14806 : return OK;
581 : : }
582 : :
583 : : /*
584 : : * Read and parse all config files in a subdirectory in alphabetical order
585 : : *
586 : : * includedir is the absolute or relative path to the subdirectory to scan.
587 : : *
588 : : * calling_file/calling_lineno identify the source of the request.
589 : : * Pass NULL/0 if not recursing from an inclusion request.
590 : : *
591 : : * See ParseConfigFp for further details.
592 : : */
593 : : bool
594 : 0 : ParseConfigDirectory(const char *includedir,
595 : : const char *calling_file, int calling_lineno,
596 : : int depth, int elevel,
597 : : ConfigVariable **head_p,
598 : : ConfigVariable **tail_p)
599 : : {
600 : : char *err_msg;
601 : : char **filenames;
602 : : int num_filenames;
603 : :
604 : 0 : filenames = GetConfFilesInDir(includedir, calling_file, elevel,
605 : : &num_filenames, &err_msg);
606 : :
607 [ # # ]: 0 : if (!filenames)
608 : : {
609 : 0 : record_config_file_error(err_msg, calling_file, calling_lineno, head_p,
610 : : tail_p);
611 : 0 : return false;
612 : : }
613 : :
614 [ # # ]: 0 : for (int i = 0; i < num_filenames; i++)
615 : : {
616 [ # # ]: 0 : if (!ParseConfigFile(filenames[i], true,
617 : : calling_file, calling_lineno,
618 : : depth, elevel,
619 : : head_p, tail_p))
620 : 0 : return false;
621 : : }
622 : :
623 : 0 : return true;
624 : : }
625 : :
626 : : /*
627 : : * Free a list of ConfigVariables, including the names and the values
628 : : */
629 : : void
630 : 9110 : FreeConfigVariables(ConfigVariable *list)
631 : : {
632 : : ConfigVariable *item;
633 : :
634 : 9110 : item = list;
635 [ + + ]: 48441 : while (item)
636 : : {
637 : 39331 : ConfigVariable *next = item->next;
638 : :
639 : 39331 : FreeConfigVariable(item);
640 : 39331 : item = next;
641 : : }
642 : 9110 : }
643 : :
644 : : /*
645 : : * Free a single ConfigVariable
646 : : */
647 : : static void
648 : 39331 : FreeConfigVariable(ConfigVariable *item)
649 : : {
650 [ + - ]: 39331 : if (item->name)
651 : 39331 : pfree(item->name);
652 [ + - ]: 39331 : if (item->value)
653 : 39331 : pfree(item->value);
654 [ - + ]: 39331 : if (item->errmsg)
655 : 0 : pfree(item->errmsg);
656 [ + - ]: 39331 : if (item->filename)
657 : 39331 : pfree(item->filename);
658 : 39331 : pfree(item);
659 : 39331 : }
660 : :
661 : :
662 : : /*
663 : : * DeescapeQuotedString
664 : : *
665 : : * Strip the quotes surrounding the given string, and collapse any embedded
666 : : * '' sequences and backslash escapes.
667 : : *
668 : : * The string returned is palloc'd and should eventually be pfree'd by the
669 : : * caller.
670 : : *
671 : : * This is exported because it is also used by the bootstrap scanner.
672 : : */
673 : : char *
674 : 493235 : DeescapeQuotedString(const char *s)
675 : : {
676 : : char *newStr;
677 : : int len,
678 : : i,
679 : : j;
680 : :
681 : : /* We just Assert that there are leading and trailing quotes */
682 : : Assert(s != NULL && s[0] == '\'');
683 : 493235 : len = strlen(s);
684 : : Assert(len >= 2);
685 : : Assert(s[len - 1] == '\'');
686 : :
687 : : /* Skip the leading quote; we'll handle the trailing quote below */
688 : 493235 : s++, len--;
689 : :
690 : : /* Since len still includes trailing quote, this is enough space */
691 : 493235 : newStr = palloc(len);
692 : :
693 [ + + ]: 9254059 : for (i = 0, j = 0; i < len; i++)
694 : : {
695 [ - + ]: 8760824 : if (s[i] == '\\')
696 : : {
697 : 0 : i++;
698 [ # # # # : 0 : switch (s[i])
# # # ]
699 : : {
700 : 0 : case 'b':
701 : 0 : newStr[j] = '\b';
702 : 0 : break;
703 : 0 : case 'f':
704 : 0 : newStr[j] = '\f';
705 : 0 : break;
706 : 0 : case 'n':
707 : 0 : newStr[j] = '\n';
708 : 0 : break;
709 : 0 : case 'r':
710 : 0 : newStr[j] = '\r';
711 : 0 : break;
712 : 0 : case 't':
713 : 0 : newStr[j] = '\t';
714 : 0 : break;
715 : 0 : case '0':
716 : : case '1':
717 : : case '2':
718 : : case '3':
719 : : case '4':
720 : : case '5':
721 : : case '6':
722 : : case '7':
723 : : {
724 : : int k;
725 : 0 : long octVal = 0;
726 : :
727 : 0 : for (k = 0;
728 [ # # # # : 0 : s[i + k] >= '0' && s[i + k] <= '7' && k < 3;
# # ]
729 : 0 : k++)
730 : 0 : octVal = (octVal << 3) + (s[i + k] - '0');
731 : 0 : i += k - 1;
732 : 0 : newStr[j] = ((char) octVal);
733 : : }
734 : 0 : break;
735 : 0 : default:
736 : 0 : newStr[j] = s[i];
737 : 0 : break;
738 : : } /* switch */
739 : : }
740 [ + + + + ]: 8760824 : else if (s[i] == '\'' && s[i + 1] == '\'')
741 : : {
742 : : /* doubled quote becomes just one quote */
743 : 3562 : newStr[j] = s[++i];
744 : : }
745 : : else
746 : 8757262 : newStr[j] = s[i];
747 : 8760824 : j++;
748 : : }
749 : :
750 : : /* We copied the ending quote to newStr, so replace with \0 */
751 : : Assert(j > 0 && j <= len);
752 : 493235 : newStr[--j] = '\0';
753 : :
754 : 493235 : return newStr;
755 : : }
|