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