Age Owner Branch data TLA Line data Source code
1 : : %top{
2 : : /*
3 : : * Scanner for the configuration file
4 : : *
5 : : * Copyright (c) 2000-2025, 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 : :
5058 peter_e@gmx.net 93 :CBC 2352659 : \n ConfigFileLineno++; return GUC_EOL;
94 : : [ \t\r]+ /* eat whitespace */
95 : 840511 : #.* /* eat comment (.* matches anything until newline) */
9229 96 : 1887438 :
5058 97 : 126087 : {ID} return GUC_ID;
98 : 114 : {QUALIFIED_ID} return GUC_QUALIFIED_ID;
99 : 114 : {STRING} return GUC_STRING;
9060 bruce@momjian.us 100 : 43245 : {UNQUOTED_STRING} return GUC_UNQUOTED_STRING;
5058 peter_e@gmx.net 101 : 100 : {INTEGER} return GUC_INTEGER;
102 : 25860 : {REAL} return GUC_REAL;
5058 peter_e@gmx.net 103 :UBC 0 : = return GUC_EQUALS;
9229 peter_e@gmx.net 104 :CBC 97647 :
5058 peter_e@gmx.net 105 :UBC 0 : . return GUC_ERROR;
106 : :
9229 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
7676 tgl@sss.pgh.pa.us 120 :CBC 2767 : 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 : : */
5088 130 [ + + - + : 2767 : 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 [ + + ]: 2767 : 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 : : */
3723 145 : 2767 : config_cxt = AllocSetContextCreate(CurrentMemoryContext,
146 : : "config file processing",
147 : : ALLOCSET_DEFAULT_SIZES);
148 : 2767 : caller_cxt = MemoryContextSwitchTo(config_cxt);
149 : :
150 : : /*
151 : : * Read and apply the config file. We don't need to examine the result.
152 : : */
153 : 2767 : (void) ProcessConfigFileInternal(context, true, elevel);
154 : :
155 : : /* Clean up */
156 : 2762 : MemoryContextSwitchTo(caller_cxt);
157 : 2762 : MemoryContextDelete(config_cxt);
158 : 2762 : }
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 : 4555 : 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;
6963 bruce@momjian.us 182 : 4555 : 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 : : */
2202 tgl@sss.pgh.pa.us 189 [ - + ]: 4555 : if (strspn(config_file, " \t\r\n") == strlen(config_file))
190 : : {
2202 tgl@sss.pgh.pa.us 191 [ # # ]:UBC 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 : : */
1016 michael@paquier.xyz 206 [ - + ]:CBC 4555 : if (depth > CONF_FILE_MAX_DEPTH)
207 : : {
7126 tgl@sss.pgh.pa.us 208 [ # # ]:UBC 0 : ereport(elevel,
209 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
210 : : errmsg("could not open configuration file \"%s\": maximum nesting depth exceeded",
211 : : config_file)));
3723 212 : 0 : record_config_file_error("nesting depth exceeded",
213 : : calling_file, calling_lineno,
214 : : head_p, tail_p);
7126 215 : 0 : return false;
216 : : }
217 : :
4335 heikki.linnakangas@i 218 :CBC 4555 : 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 : : */
2202 tgl@sss.pgh.pa.us 226 [ + + - + ]: 4555 : if (calling_file && strcmp(abs_path, calling_file) == 0)
227 : : {
2202 tgl@sss.pgh.pa.us 228 [ # # ]:UBC 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 : :
4335 heikki.linnakangas@i 239 :CBC 4555 : fp = AllocateFile(abs_path, "r");
7188 tgl@sss.pgh.pa.us 240 [ + + ]: 4555 : if (!fp)
241 : : {
5014 andrew@dunslane.net 242 [ - + ]: 100 : if (strict)
243 : : {
5014 andrew@dunslane.net 244 [ # # ]:UBC 0 : ereport(elevel,
245 : : (errcode_for_file_access(),
246 : : errmsg("could not open configuration file \"%s\": %m",
247 : : abs_path)));
3723 tgl@sss.pgh.pa.us 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);
4246 rhaas@postgresql.org 252 : 0 : OK = false;
253 : : }
254 : : else
255 : : {
3877 tgl@sss.pgh.pa.us 256 [ + - ]:CBC 100 : ereport(LOG,
257 : : (errmsg("skipping missing configuration file \"%s\"",
258 : : abs_path)));
259 : : }
4246 rhaas@postgresql.org 260 : 100 : goto cleanup;
261 : : }
262 : :
4335 heikki.linnakangas@i 263 : 4455 : OK = ParseConfigFp(fp, abs_path, depth, elevel, head_p, tail_p);
264 : :
4246 rhaas@postgresql.org 265 : 4555 : cleanup:
266 [ + + ]: 4555 : if (fp)
267 : 4455 : FreeFile(fp);
4335 heikki.linnakangas@i 268 : 4555 : pfree(abs_path);
269 : :
5391 rhaas@postgresql.org 270 : 4555 : return OK;
271 : : }
272 : :
273 : : /*
274 : : * Capture an error message in the ConfigVariable list returned by
275 : : * config file parsing.
276 : : */
277 : : void
3723 tgl@sss.pgh.pa.us 278 :UBC 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(sizeof *item);
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
4981 rhaas@postgresql.org 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
5391 rhaas@postgresql.org 350 :CBC 9131 : ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
351 : : ConfigVariable **head_p, ConfigVariable **tail_p)
352 : : {
4974 tgl@sss.pgh.pa.us 353 : 9131 : volatile bool OK = true;
4981 rhaas@postgresql.org 354 : 9131 : unsigned int save_ConfigFileLineno = ConfigFileLineno;
355 : 9131 : sigjmp_buf *save_GUC_flex_fatal_jmp = GUC_flex_fatal_jmp;
356 : : sigjmp_buf flex_fatal_jmp;
357 : : yyscan_t scanner;
358 : : struct yyguts_t *yyg; /* needed for yytext macro */
359 : 9131 : volatile YY_BUFFER_STATE lex_buffer = NULL;
360 : : int errorcount;
361 : : int token;
362 : :
363 [ + - ]: 9131 : if (sigsetjmp(flex_fatal_jmp, 1) == 0)
364 : 9131 : 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 : : */
4981 rhaas@postgresql.org 372 [ # # ]:UBC 0 : elog(elevel, "%s at file \"%s\" line %u",
373 : : GUC_flex_fatal_errmsg, config_file, ConfigFileLineno);
3723 tgl@sss.pgh.pa.us 374 : 0 : record_config_file_error(GUC_flex_fatal_errmsg,
375 : : config_file, ConfigFileLineno,
376 : : head_p, tail_p);
4981 rhaas@postgresql.org 377 : 0 : OK = false;
378 : 0 : goto cleanup;
379 : : }
380 : :
381 : : /*
382 : : * Parse
383 : : */
7676 tgl@sss.pgh.pa.us 384 :CBC 9131 : ConfigFileLineno = 1;
5088 385 : 9131 : errorcount = 0;
386 : :
255 peter@eisentraut.org 387 [ - + ]: 9131 : if (yylex_init(&scanner) != 0)
255 peter@eisentraut.org 388 [ # # ]:UBC 0 : elog(elevel, "yylex_init() failed: %m");
255 peter@eisentraut.org 389 :CBC 9131 : yyg = (struct yyguts_t *) scanner;
390 : :
391 : 9131 : lex_buffer = yy_create_buffer(fp, YY_BUF_SIZE, scanner);
392 : 9131 : yy_switch_to_buffer(lex_buffer, scanner);
393 : :
394 : : /* This loop iterates once per logical line */
395 [ + + ]: 2361790 : while ((token = yylex(scanner)))
7676 tgl@sss.pgh.pa.us 396 : 97697 : {
5088 397 : 2352665 : char *opt_name = NULL;
398 : 2352665 : char *opt_value = NULL;
399 : : ConfigVariable *item;
400 : :
7188 401 [ + + ]: 2352665 : if (token == GUC_EOL) /* empty or comment line */
402 : 2254962 : continue;
403 : :
404 : : /* first token on line is option name */
405 [ + + - + ]: 97703 : if (token != GUC_ID && token != GUC_QUALIFIED_ID)
7188 tgl@sss.pgh.pa.us 406 :UBC 0 : goto parse_error;
7188 tgl@sss.pgh.pa.us 407 :CBC 97703 : opt_name = pstrdup(yytext);
408 : :
409 : : /* next we have an optional equal sign; discard if present */
255 peter@eisentraut.org 410 : 97703 : token = yylex(scanner);
7188 tgl@sss.pgh.pa.us 411 [ + + ]: 97703 : if (token == GUC_EQUALS)
255 peter@eisentraut.org 412 : 97647 : token = yylex(scanner);
413 : :
414 : : /* now we must have the option value */
7188 tgl@sss.pgh.pa.us 415 [ + + + + ]: 97703 : if (token != GUC_ID &&
5401 peter_e@gmx.net 416 [ + + ]: 25960 : token != GUC_STRING &&
417 [ + - ]: 100 : token != GUC_INTEGER &&
418 [ - + ]: 100 : token != GUC_REAL &&
419 : : token != GUC_UNQUOTED_STRING)
7188 tgl@sss.pgh.pa.us 420 :UBC 0 : goto parse_error;
7188 tgl@sss.pgh.pa.us 421 [ + + ]:CBC 97703 : if (token == GUC_STRING) /* strip quotes and escapes */
1798 422 : 43245 : opt_value = DeescapeQuotedString(yytext);
423 : : else
7188 424 : 54458 : opt_value = pstrdup(yytext);
425 : :
426 : : /* now we'd like an end of line, or possibly EOF */
255 peter@eisentraut.org 427 : 97703 : token = yylex(scanner);
5994 tgl@sss.pgh.pa.us 428 [ + + ]: 97703 : if (token != GUC_EOL)
429 : : {
430 [ - + ]: 6 : if (token != 0)
5994 tgl@sss.pgh.pa.us 431 :UBC 0 : goto parse_error;
432 : : /* treat EOF like \n for line numbering purposes, cf bug 4752 */
5994 tgl@sss.pgh.pa.us 433 :CBC 6 : ConfigFileLineno++;
434 : : }
435 : :
436 : : /* OK, process the option name and value */
4730 heikki.linnakangas@i 437 [ - + ]: 97703 : 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 : : */
3723 tgl@sss.pgh.pa.us 443 [ # # ]:UBC 0 : if (!ParseConfigDirectory(opt_value,
444 : 0 : config_file, ConfigFileLineno - 1,
445 : : depth + 1, elevel,
446 : : head_p, tail_p))
5014 andrew@dunslane.net 447 : 0 : OK = false;
255 peter@eisentraut.org 448 : 0 : yy_switch_to_buffer(lex_buffer, scanner);
5014 andrew@dunslane.net 449 : 0 : pfree(opt_name);
450 : 0 : pfree(opt_value);
451 : : }
4730 heikki.linnakangas@i 452 [ - + ]:CBC 97703 : 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 : : */
3723 tgl@sss.pgh.pa.us 458 [ # # ]:UBC 0 : if (!ParseConfigFile(opt_value, false,
459 : 0 : config_file, ConfigFileLineno - 1,
460 : : depth + 1, elevel,
461 : : head_p, tail_p))
4730 heikki.linnakangas@i 462 : 0 : OK = false;
255 peter@eisentraut.org 463 : 0 : yy_switch_to_buffer(lex_buffer, scanner);
4420 alvherre@alvh.no-ip. 464 : 0 : pfree(opt_name);
465 : 0 : pfree(opt_value);
466 : : }
5014 andrew@dunslane.net 467 [ + + ]:CBC 97703 : 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 : : */
3723 tgl@sss.pgh.pa.us 473 [ - + ]: 56 : if (!ParseConfigFile(opt_value, true,
474 : 56 : config_file, ConfigFileLineno - 1,
475 : : depth + 1, elevel,
476 : : head_p, tail_p))
7126 tgl@sss.pgh.pa.us 477 :UBC 0 : OK = false;
255 peter@eisentraut.org 478 :CBC 56 : yy_switch_to_buffer(lex_buffer, scanner);
7126 tgl@sss.pgh.pa.us 479 : 56 : pfree(opt_name);
480 : 56 : pfree(opt_value);
481 : : }
482 : : else
483 : : {
484 : : /* ordinary variable, append to list */
7188 485 : 97647 : item = palloc(sizeof *item);
486 : 97647 : item->name = opt_name;
487 : 97647 : item->value = opt_value;
3723 488 : 97647 : item->errmsg = NULL;
6205 alvherre@alvh.no-ip. 489 : 97647 : item->filename = pstrdup(config_file);
3723 tgl@sss.pgh.pa.us 490 : 97647 : item->sourceline = ConfigFileLineno - 1;
491 : 97647 : item->ignore = false;
492 : 97647 : item->applied = false;
7188 493 : 97647 : item->next = NULL;
7126 494 [ + + ]: 97647 : if (*head_p == NULL)
495 : 7219 : *head_p = item;
496 : : else
497 : 90428 : (*tail_p)->next = item;
498 : 97647 : *tail_p = item;
499 : : }
500 : :
501 : : /* break out of loop if read EOF, else loop for next line */
7188 502 [ + + ]: 97703 : if (token == 0)
503 : 6 : break;
5088 504 : 97697 : continue;
505 : :
3723 tgl@sss.pgh.pa.us 506 :UBC 0 : parse_error:
507 : : /* release storage if we allocated any on this line */
5088 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)));
3723 520 : 0 : record_config_file_error("syntax error",
521 : 0 : config_file, ConfigFileLineno - 1,
522 : : head_p, tail_p);
523 : : }
524 : : else
525 : : {
5088 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)));
3723 530 : 0 : record_config_file_error("syntax error",
531 : : config_file, ConfigFileLineno,
532 : : head_p, tail_p);
533 : : }
5088 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)
255 peter@eisentraut.org 555 : 0 : token = yylex(scanner);
556 : : /* break out of loop on EOF */
5088 tgl@sss.pgh.pa.us 557 [ # # ]: 0 : if (token == 0)
558 : 0 : break;
559 : : }
560 : :
4981 rhaas@postgresql.org 561 :CBC 9125 : cleanup:
255 peter@eisentraut.org 562 : 9131 : yy_delete_buffer(lex_buffer, scanner);
563 : 9131 : yylex_destroy(scanner);
564 : : /* Each recursion level must save and restore these static variables. */
4981 rhaas@postgresql.org 565 : 9131 : ConfigFileLineno = save_ConfigFileLineno;
566 : 9131 : GUC_flex_fatal_jmp = save_GUC_flex_fatal_jmp;
7126 tgl@sss.pgh.pa.us 567 : 9131 : return OK;
568 : : }
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
4730 heikki.linnakangas@i 581 :UBC 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 : : char *err_msg;
588 : : char **filenames;
589 : : int num_filenames;
590 : :
1034 michael@paquier.xyz 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 : : 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 : : calling_file, calling_lineno,
605 : : depth, elevel,
606 : : head_p, tail_p))
607 : 0 : return false;
608 : : }
609 : :
610 : 0 : return true;
611 : : }
612 : :
613 : : /*
614 : : * Free a list of ConfigVariables, including the names and the values
615 : : */
616 : : void
5391 rhaas@postgresql.org 617 :CBC 4676 : FreeConfigVariables(ConfigVariable *list)
618 : : {
619 : : ConfigVariable *item;
620 : :
7126 tgl@sss.pgh.pa.us 621 : 4676 : item = list;
622 [ + + ]: 24990 : while (item)
623 : : {
5391 rhaas@postgresql.org 624 : 20314 : ConfigVariable *next = item->next;
625 : :
4043 fujii@postgresql.org 626 : 20314 : FreeConfigVariable(item);
7126 tgl@sss.pgh.pa.us 627 : 20314 : item = next;
628 : : }
629 : 4676 : }
630 : :
631 : : /*
632 : : * Free a single ConfigVariable
633 : : */
634 : : static void
3877 635 : 20314 : FreeConfigVariable(ConfigVariable *item)
636 : : {
3723 637 [ + - ]: 20314 : if (item->name)
638 : 20314 : pfree(item->name);
639 [ + - ]: 20314 : if (item->value)
640 : 20314 : pfree(item->value);
641 [ - + ]: 20314 : if (item->errmsg)
3723 tgl@sss.pgh.pa.us 642 :UBC 0 : pfree(item->errmsg);
3723 tgl@sss.pgh.pa.us 643 [ + - ]:CBC 20314 : if (item->filename)
644 : 20314 : pfree(item->filename);
3877 645 : 20314 : pfree(item);
646 : 20314 : }
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 *
1798 661 : 410904 : DeescapeQuotedString(const char *s)
662 : : {
663 : : char *newStr;
664 : : int len,
665 : : i,
666 : : j;
667 : :
668 : : /* We just Assert that there are leading and trailing quotes */
7290 669 [ + - - + ]: 410904 : Assert(s != NULL && s[0] == '\'');
9060 bruce@momjian.us 670 : 410904 : len = strlen(s);
7290 tgl@sss.pgh.pa.us 671 [ - + ]: 410904 : Assert(len >= 2);
3723 672 [ - + ]: 410904 : Assert(s[len - 1] == '\'');
673 : :
674 : : /* Skip the leading quote; we'll handle the trailing quote below */
7290 675 : 410904 : s++, len--;
676 : :
677 : : /* Since len still includes trailing quote, this is enough space */
678 : 410904 : newStr = palloc(len);
679 : :
9060 bruce@momjian.us 680 [ + + ]: 7744184 : for (i = 0, j = 0; i < len; i++)
681 : : {
682 [ - + ]: 7333280 : if (s[i] == '\\')
683 : : {
9060 bruce@momjian.us 684 :UBC 0 : i++;
685 [ # # # # : 0 : switch (s[i])
# # # ]
686 : : {
687 : 0 : case 'b':
688 : 0 : newStr[j] = '\b';
689 : 0 : break;
690 : 0 : case 'f':
691 : 0 : newStr[j] = '\f';
692 : 0 : break;
693 : 0 : case 'n':
694 : 0 : newStr[j] = '\n';
695 : 0 : break;
696 : 0 : case 'r':
697 : 0 : newStr[j] = '\r';
698 : 0 : break;
699 : 0 : case 't':
700 : 0 : newStr[j] = '\t';
701 : 0 : break;
702 : 0 : 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 : : 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 : : }
721 : 0 : break;
722 : 0 : default:
723 : 0 : newStr[j] = s[i];
724 : 0 : break;
725 : : } /* switch */
726 : : }
3723 tgl@sss.pgh.pa.us 727 [ + + + + ]:CBC 7333280 : else if (s[i] == '\'' && s[i + 1] == '\'')
728 : : {
729 : : /* doubled quote becomes just one quote */
7290 730 : 3128 : newStr[j] = s[++i];
731 : : }
732 : : else
9060 bruce@momjian.us 733 : 7330152 : newStr[j] = s[i];
734 : 7333280 : j++;
735 : : }
736 : :
737 : : /* We copied the ending quote to newStr, so replace with \0 */
7290 tgl@sss.pgh.pa.us 738 [ + - - + ]: 410904 : Assert(j > 0 && j <= len);
739 : 410904 : newStr[--j] = '\0';
740 : :
9060 bruce@momjian.us 741 : 410904 : return newStr;
742 : : }
|