LCOV - code coverage report
Current view: top level - src/backend/utils/misc - guc-file.l (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 190 382 49.7 %
Date: 2020-06-05 19:06:29 Functions: 8 11 72.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*-pgsql-c-*- */
       2             : /*
       3             :  * Scanner for the configuration file
       4             :  *
       5             :  * Copyright (c) 2000-2020, PostgreSQL Global Development Group
       6             :  *
       7             :  * src/backend/utils/misc/guc-file.l
       8             :  */
       9             : 
      10             : %{
      11             : 
      12             : #include "postgres.h"
      13             : 
      14             : #include <ctype.h>
      15             : #include <unistd.h>
      16             : 
      17             : #include "mb/pg_wchar.h"
      18             : #include "miscadmin.h"
      19             : #include "storage/fd.h"
      20             : #include "utils/guc.h"
      21             : 
      22             : 
      23             : /*
      24             :  * flex emits a yy_fatal_error() function that it calls in response to
      25             :  * critical errors like malloc failure, file I/O errors, and detection of
      26             :  * internal inconsistency.  That function prints a message and calls exit().
      27             :  * Mutate it to instead call our handler, which jumps out of the parser.
      28             :  */
      29             : #undef fprintf
      30             : #define fprintf(file, fmt, msg) GUC_flex_fatal(msg)
      31             : 
      32             : enum
      33             : {
      34             :     GUC_ID = 1,
      35             :     GUC_STRING = 2,
      36             :     GUC_INTEGER = 3,
      37             :     GUC_REAL = 4,
      38             :     GUC_EQUALS = 5,
      39             :     GUC_UNQUOTED_STRING = 6,
      40             :     GUC_QUALIFIED_ID = 7,
      41             :     GUC_EOL = 99,
      42             :     GUC_ERROR = 100
      43             : };
      44             : 
      45             : static unsigned int ConfigFileLineno;
      46             : static const char *GUC_flex_fatal_errmsg;
      47             : static sigjmp_buf *GUC_flex_fatal_jmp;
      48             : 
      49             : static void FreeConfigVariable(ConfigVariable *item);
      50             : 
      51             : static void record_config_file_error(const char *errmsg,
      52             :                          const char *config_file,
      53             :                          int lineno,
      54             :                          ConfigVariable **head_p,
      55             :                          ConfigVariable **tail_p);
      56             : 
      57             : static int  GUC_flex_fatal(const char *msg);
      58             : static char *GUC_scanstr(const char *s);
      59             : 
      60             : /* LCOV_EXCL_START */
      61             : 
      62             : %}
      63             : 
      64             : %option 8bit
      65             : %option never-interactive
      66             : %option nodefault
      67             : %option noinput
      68             : %option nounput
      69             : %option noyywrap
      70             : %option warn
      71             : %option prefix="GUC_yy"
      72             : 
      73             : 
      74             : SIGN            ("-"|"+")
      75             : DIGIT           [0-9]
      76             : HEXDIGIT        [0-9a-fA-F]
      77             : 
      78             : UNIT_LETTER     [a-zA-Z]
      79             : 
      80             : INTEGER         {SIGN}?({DIGIT}+|0x{HEXDIGIT}+){UNIT_LETTER}*
      81             : 
      82             : EXPONENT        [Ee]{SIGN}?{DIGIT}+
      83             : REAL            {SIGN}?{DIGIT}*"."{DIGIT}*{EXPONENT}?
      84             : 
      85             : LETTER          [A-Za-z_\200-\377]
      86             : LETTER_OR_DIGIT [A-Za-z_0-9\200-\377]
      87             : 
      88             : ID              {LETTER}{LETTER_OR_DIGIT}*
      89             : QUALIFIED_ID    {ID}"."{ID}
      90             : 
      91             : UNQUOTED_STRING {LETTER}({LETTER_OR_DIGIT}|[-._:/])*
      92             : STRING          \'([^'\\\n]|\\.|\'\')*\'
      93             : 
      94             : %%
      95             : 
      96             : \n              ConfigFileLineno++; return GUC_EOL;
      97             : [ \t\r]+        /* eat whitespace */
      98             : #.*             /* eat comment (.* matches anything until newline) */
      99             : 
     100             : {ID}            return GUC_ID;
     101             : {QUALIFIED_ID}  return GUC_QUALIFIED_ID;
     102             : {STRING}        return GUC_STRING;
     103             : {UNQUOTED_STRING} return GUC_UNQUOTED_STRING;
     104             : {INTEGER}       return GUC_INTEGER;
     105             : {REAL}          return GUC_REAL;
     106             : =               return GUC_EQUALS;
     107             : 
     108             : .               return GUC_ERROR;
     109             : 
     110             : %%
     111             : 
     112             : /* LCOV_EXCL_STOP */
     113             : 
     114             : /*
     115             :  * Exported function to read and process the configuration file. The
     116             :  * parameter indicates in what context the file is being read --- either
     117             :  * postmaster startup (including standalone-backend startup) or SIGHUP.
     118             :  * All options mentioned in the configuration file are set to new values.
     119             :  * If a hard error occurs, no values will be changed.  (There can also be
     120             :  * errors that prevent just one value from being changed.)
     121             :  */
     122             : void
     123        4668 : ProcessConfigFile(GucContext context)
     124             : {
     125             :     int         elevel;
     126             :     MemoryContext config_cxt;
     127             :     MemoryContext caller_cxt;
     128             : 
     129             :     /*
     130             :      * Config files are processed on startup (by the postmaster only) and on
     131             :      * SIGHUP (by the postmaster and its children)
     132             :      */
     133             :     Assert((context == PGC_POSTMASTER && !IsUnderPostmaster) ||
     134             :            context == PGC_SIGHUP);
     135             : 
     136             :     /*
     137             :      * To avoid cluttering the log, only the postmaster bleats loudly about
     138             :      * problems with the config file.
     139             :      */
     140        4668 :     elevel = IsUnderPostmaster ? DEBUG2 : LOG;
     141             : 
     142             :     /*
     143             :      * This function is usually called within a process-lifespan memory
     144             :      * context.  To ensure that any memory leaked during GUC processing does
     145             :      * not accumulate across repeated SIGHUP cycles, do the work in a private
     146             :      * context that we can free at exit.
     147             :      */
     148        4668 :     config_cxt = AllocSetContextCreate(CurrentMemoryContext,
     149             :                                        "config file processing",
     150             :                                        ALLOCSET_DEFAULT_SIZES);
     151        4668 :     caller_cxt = MemoryContextSwitchTo(config_cxt);
     152             : 
     153             :     /*
     154             :      * Read and apply the config file.  We don't need to examine the result.
     155             :      */
     156        4668 :     (void) ProcessConfigFileInternal(context, true, elevel);
     157             : 
     158             :     /* Clean up */
     159        4666 :     MemoryContextSwitchTo(caller_cxt);
     160        4666 :     MemoryContextDelete(config_cxt);
     161        4666 : }
     162             : 
     163             : /*
     164             :  * This function handles both actual config file (re)loads and execution of
     165             :  * show_all_file_settings() (i.e., the pg_file_settings view).  In the latter
     166             :  * case we don't apply any of the settings, but we make all the usual validity
     167             :  * checks, and we return the ConfigVariable list so that it can be printed out
     168             :  * by show_all_file_settings().
     169             :  */
     170             : static ConfigVariable *
     171        4672 : ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
     172             : {
     173        4672 :     bool        error = false;
     174        4672 :     bool        applying = false;
     175             :     const char *ConfFileWithError;
     176             :     ConfigVariable *item,
     177             :                *head,
     178             :                *tail;
     179             :     int         i;
     180             : 
     181             :     /* Parse the main config file into a list of option names and values */
     182        4672 :     ConfFileWithError = ConfigFileName;
     183        4672 :     head = tail = NULL;
     184             : 
     185        4672 :     if (!ParseConfigFile(ConfigFileName, true,
     186             :                          NULL, 0, 0, elevel,
     187             :                          &head, &tail))
     188             :     {
     189             :         /* Syntax error(s) detected in the file, so bail out */
     190           0 :         error = true;
     191           0 :         goto bail_out;
     192             :     }
     193             : 
     194             :     /*
     195             :      * Parse the PG_AUTOCONF_FILENAME file, if present, after the main file to
     196             :      * replace any parameters set by ALTER SYSTEM command.  Because this file
     197             :      * is in the data directory, we can't read it until the DataDir has been
     198             :      * set.
     199             :      */
     200        4672 :     if (DataDir)
     201             :     {
     202        2488 :         if (!ParseConfigFile(PG_AUTOCONF_FILENAME, false,
     203             :                              NULL, 0, 0, elevel,
     204             :                              &head, &tail))
     205             :         {
     206             :             /* Syntax error(s) detected in the file, so bail out */
     207           0 :             error = true;
     208           0 :             ConfFileWithError = PG_AUTOCONF_FILENAME;
     209           0 :             goto bail_out;
     210             :         }
     211             :     }
     212             :     else
     213             :     {
     214             :         /*
     215             :          * If DataDir is not set, the PG_AUTOCONF_FILENAME file cannot be
     216             :          * read.  In this case, we don't want to accept any settings but
     217             :          * data_directory from postgresql.conf, because they might be
     218             :          * overwritten with settings in the PG_AUTOCONF_FILENAME file which
     219             :          * will be read later. OTOH, since data_directory isn't allowed in the
     220             :          * PG_AUTOCONF_FILENAME file, it will never be overwritten later.
     221             :          */
     222        2184 :         ConfigVariable *newlist = NULL;
     223             : 
     224             :         /*
     225             :          * Prune all items except the last "data_directory" from the list.
     226             :          */
     227       33182 :         for (item = head; item; item = item->next)
     228             :         {
     229       30998 :             if (!item->ignore &&
     230       30998 :                 strcmp(item->name, "data_directory") == 0)
     231           0 :                 newlist = item;
     232             :         }
     233             : 
     234        2184 :         if (newlist)
     235           0 :             newlist->next = NULL;
     236        2184 :         head = tail = newlist;
     237             : 
     238             :         /*
     239             :          * Quick exit if data_directory is not present in file.
     240             :          *
     241             :          * We need not do any further processing, in particular we don't set
     242             :          * PgReloadTime; that will be set soon by subsequent full loading of
     243             :          * the config file.
     244             :          */
     245        2184 :         if (head == NULL)
     246        2184 :             goto bail_out;
     247             :     }
     248             : 
     249             :     /*
     250             :      * Mark all extant GUC variables as not present in the config file. We
     251             :      * need this so that we can tell below which ones have been removed from
     252             :      * the file since we last processed it.
     253             :      */
     254      838514 :     for (i = 0; i < num_guc_variables; i++)
     255             :     {
     256      836026 :         struct config_generic *gconf = guc_variables[i];
     257             : 
     258      836026 :         gconf->status &= ~GUC_IS_IN_FILE;
     259             :     }
     260             : 
     261             :     /*
     262             :      * Check if all the supplied option names are valid, as an additional
     263             :      * quasi-syntactic check on the validity of the config file.  It is
     264             :      * important that the postmaster and all backends agree on the results of
     265             :      * this phase, else we will have strange inconsistencies about which
     266             :      * processes accept a config file update and which don't.  Hence, unknown
     267             :      * custom variable names have to be accepted without complaint.  For the
     268             :      * same reason, we don't attempt to validate the options' values here.
     269             :      *
     270             :      * In addition, the GUC_IS_IN_FILE flag is set on each existing GUC
     271             :      * variable mentioned in the file; and we detect duplicate entries in the
     272             :      * file and mark the earlier occurrences as ignorable.
     273             :      */
     274       43302 :     for (item = head; item; item = item->next)
     275             :     {
     276             :         struct config_generic *record;
     277             : 
     278             :         /* Ignore anything already marked as ignorable */
     279       40814 :         if (item->ignore)
     280           0 :             continue;
     281             : 
     282             :         /*
     283             :          * Try to find the variable; but do not create a custom placeholder if
     284             :          * it's not there already.
     285             :          */
     286       40814 :         record = find_option(item->name, false, elevel);
     287             : 
     288       40814 :         if (record)
     289             :         {
     290             :             /* If it's already marked, then this is a duplicate entry */
     291       40804 :             if (record->status & GUC_IS_IN_FILE)
     292             :             {
     293             :                 /*
     294             :                  * Mark the earlier occurrence(s) as dead/ignorable.  We could
     295             :                  * avoid the O(N^2) behavior here with some additional state,
     296             :                  * but it seems unlikely to be worth the trouble.
     297             :                  */
     298             :                 ConfigVariable *pitem;
     299             : 
     300       80576 :                 for (pitem = head; pitem != item; pitem = pitem->next)
     301             :                 {
     302       77764 :                     if (!pitem->ignore &&
     303       72304 :                         strcmp(pitem->name, item->name) == 0)
     304        2812 :                         pitem->ignore = true;
     305             :                 }
     306             :             }
     307             :             /* Now mark it as present in file */
     308       40804 :             record->status |= GUC_IS_IN_FILE;
     309             :         }
     310          10 :         else if (strchr(item->name, GUC_QUALIFIER_SEPARATOR) == NULL)
     311             :         {
     312             :             /* Invalid non-custom variable, so complain */
     313           0 :             ereport(elevel,
     314             :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
     315             :                      errmsg("unrecognized configuration parameter \"%s\" in file \"%s\" line %u",
     316             :                             item->name,
     317             :                             item->filename, item->sourceline)));
     318           0 :             item->errmsg = pstrdup("unrecognized configuration parameter");
     319           0 :             error = true;
     320           0 :             ConfFileWithError = item->filename;
     321             :         }
     322             :     }
     323             : 
     324             :     /*
     325             :      * If we've detected any errors so far, we don't want to risk applying any
     326             :      * changes.
     327             :      */
     328        2488 :     if (error)
     329           0 :         goto bail_out;
     330             : 
     331             :     /* Otherwise, set flag that we're beginning to apply changes */
     332        2488 :     applying = true;
     333             : 
     334             :     /*
     335             :      * Check for variables having been removed from the config file, and
     336             :      * revert their reset values (and perhaps also effective values) to the
     337             :      * boot-time defaults.  If such a variable can't be changed after startup,
     338             :      * report that and continue.
     339             :      */
     340      838514 :     for (i = 0; i < num_guc_variables; i++)
     341             :     {
     342      836026 :         struct config_generic *gconf = guc_variables[i];
     343             :         GucStack   *stack;
     344             : 
     345      836026 :         if (gconf->reset_source != PGC_S_FILE ||
     346        8540 :             (gconf->status & GUC_IS_IN_FILE))
     347      836026 :             continue;
     348           0 :         if (gconf->context < PGC_SIGHUP)
     349             :         {
     350           0 :             ereport(elevel,
     351             :                     (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
     352             :                      errmsg("parameter \"%s\" cannot be changed without restarting the server",
     353             :                             gconf->name)));
     354           0 :             record_config_file_error(psprintf("parameter \"%s\" cannot be changed without restarting the server",
     355             :                                               gconf->name),
     356             :                                      NULL, 0,
     357             :                                      &head, &tail);
     358           0 :             error = true;
     359           0 :             continue;
     360             :         }
     361             : 
     362             :         /* No more to do if we're just doing show_all_file_settings() */
     363           0 :         if (!applySettings)
     364           0 :             continue;
     365             : 
     366             :         /*
     367             :          * Reset any "file" sources to "default", else set_config_option will
     368             :          * not override those settings.
     369             :          */
     370           0 :         if (gconf->reset_source == PGC_S_FILE)
     371           0 :             gconf->reset_source = PGC_S_DEFAULT;
     372           0 :         if (gconf->source == PGC_S_FILE)
     373           0 :             gconf->source = PGC_S_DEFAULT;
     374           0 :         for (stack = gconf->stack; stack; stack = stack->prev)
     375             :         {
     376           0 :             if (stack->source == PGC_S_FILE)
     377           0 :                 stack->source = PGC_S_DEFAULT;
     378             :         }
     379             : 
     380             :         /* Now we can re-apply the wired-in default (i.e., the boot_val) */
     381           0 :         if (set_config_option(gconf->name, NULL,
     382             :                               context, PGC_S_DEFAULT,
     383             :                               GUC_ACTION_SET, true, 0, false) > 0)
     384             :         {
     385             :             /* Log the change if appropriate */
     386           0 :             if (context == PGC_SIGHUP)
     387           0 :                 ereport(elevel,
     388             :                         (errmsg("parameter \"%s\" removed from configuration file, reset to default",
     389             :                                 gconf->name)));
     390             :         }
     391             :     }
     392             : 
     393             :     /*
     394             :      * Restore any variables determined by environment variables or
     395             :      * dynamically-computed defaults.  This is a no-op except in the case
     396             :      * where one of these had been in the config file and is now removed.
     397             :      *
     398             :      * In particular, we *must not* do this during the postmaster's initial
     399             :      * loading of the file, since the timezone functions in particular should
     400             :      * be run only after initialization is complete.
     401             :      *
     402             :      * XXX this is an unmaintainable crock, because we have to know how to set
     403             :      * (or at least what to call to set) every variable that could potentially
     404             :      * have PGC_S_DYNAMIC_DEFAULT or PGC_S_ENV_VAR source. However, there's no
     405             :      * time to redesign it for 9.1.
     406             :      */
     407        2488 :     if (context == PGC_SIGHUP && applySettings)
     408             :     {
     409         300 :         InitializeGUCOptionsFromEnvironment();
     410         300 :         pg_timezone_abbrev_initialize();
     411             :         /* this selects SQL_ASCII in processes not connected to a database */
     412         300 :         SetConfigOption("client_encoding", GetDatabaseEncodingName(),
     413             :                         PGC_BACKEND, PGC_S_DYNAMIC_DEFAULT);
     414             :     }
     415             : 
     416             :     /*
     417             :      * Now apply the values from the config file.
     418             :      */
     419       43300 :     for (item = head; item; item = item->next)
     420             :     {
     421       40814 :         char       *pre_value = NULL;
     422             :         int         scres;
     423             : 
     424             :         /* Ignore anything marked as ignorable */
     425       40814 :         if (item->ignore)
     426        2812 :             continue;
     427             : 
     428             :         /* In SIGHUP cases in the postmaster, we want to report changes */
     429       38002 :         if (context == PGC_SIGHUP && applySettings && !IsUnderPostmaster)
     430             :         {
     431        2244 :             const char *preval = GetConfigOption(item->name, true, false);
     432             : 
     433             :             /* If option doesn't exist yet or is NULL, treat as empty string */
     434        2244 :             if (!preval)
     435           0 :                 preval = "";
     436             :             /* must dup, else might have dangling pointer below */
     437        2244 :             pre_value = pstrdup(preval);
     438             :         }
     439             : 
     440       38002 :         scres = set_config_option(item->name, item->value,
     441             :                                   context, PGC_S_FILE,
     442             :                                   GUC_ACTION_SET, applySettings, 0, false);
     443       38000 :         if (scres > 0)
     444             :         {
     445             :             /* variable was updated, so log the change if appropriate */
     446       34910 :             if (pre_value)
     447             :             {
     448        1460 :                 const char *post_value = GetConfigOption(item->name, true, false);
     449             : 
     450        1460 :                 if (!post_value)
     451           0 :                     post_value = "";
     452        1460 :                 if (strcmp(pre_value, post_value) != 0)
     453          78 :                     ereport(elevel,
     454             :                             (errmsg("parameter \"%s\" changed to \"%s\"",
     455             :                                     item->name, item->value)));
     456             :             }
     457       34910 :             item->applied = true;
     458             :         }
     459        3090 :         else if (scres == 0)
     460             :         {
     461           0 :             error = true;
     462           0 :             item->errmsg = pstrdup("setting could not be applied");
     463           0 :             ConfFileWithError = item->filename;
     464             :         }
     465             :         else
     466             :         {
     467             :             /* no error, but variable's active value was not changed */
     468        3090 :             item->applied = true;
     469             :         }
     470             : 
     471             :         /*
     472             :          * We should update source location unless there was an error, since
     473             :          * even if the active value didn't change, the reset value might have.
     474             :          * (In the postmaster, there won't be a difference, but it does matter
     475             :          * in backends.)
     476             :          */
     477       38000 :         if (scres != 0 && applySettings)
     478       37934 :             set_config_sourcefile(item->name, item->filename,
     479             :                                   item->sourceline);
     480             : 
     481       38000 :         if (pre_value)
     482        2244 :             pfree(pre_value);
     483             :     }
     484             : 
     485             :     /* Remember when we last successfully loaded the config file. */
     486        2486 :     if (applySettings)
     487        2482 :         PgReloadTime = GetCurrentTimestamp();
     488             : 
     489           4 : bail_out:
     490        4670 :     if (error && applySettings)
     491             :     {
     492             :         /* During postmaster startup, any error is fatal */
     493           0 :         if (context == PGC_POSTMASTER)
     494           0 :             ereport(ERROR,
     495             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
     496             :                      errmsg("configuration file \"%s\" contains errors",
     497             :                             ConfFileWithError)));
     498           0 :         else if (applying)
     499           0 :             ereport(elevel,
     500             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
     501             :                      errmsg("configuration file \"%s\" contains errors; unaffected changes were applied",
     502             :                             ConfFileWithError)));
     503             :         else
     504           0 :             ereport(elevel,
     505             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
     506             :                      errmsg("configuration file \"%s\" contains errors; no changes were applied",
     507             :                             ConfFileWithError)));
     508             :     }
     509             : 
     510             :     /* Successful or otherwise, return the collected data list */
     511        4670 :     return head;
     512             : }
     513             : 
     514             : /*
     515             :  * Given a configuration file or directory location that may be a relative
     516             :  * path, return an absolute one.  We consider the location to be relative to
     517             :  * the directory holding the calling file, or to DataDir if no calling file.
     518             :  */
     519             : static char *
     520        7216 : AbsoluteConfigLocation(const char *location, const char *calling_file)
     521             : {
     522             :     char        abs_path[MAXPGPATH];
     523             : 
     524        7216 :     if (is_absolute_path(location))
     525        4672 :         return pstrdup(location);
     526             :     else
     527             :     {
     528        2544 :         if (calling_file != NULL)
     529             :         {
     530          56 :             strlcpy(abs_path, calling_file, sizeof(abs_path));
     531          56 :             get_parent_directory(abs_path);
     532          56 :             join_path_components(abs_path, abs_path, location);
     533          56 :             canonicalize_path(abs_path);
     534             :         }
     535             :         else
     536             :         {
     537             :             AssertState(DataDir);
     538        2488 :             join_path_components(abs_path, DataDir, location);
     539        2488 :             canonicalize_path(abs_path);
     540             :         }
     541        2544 :         return pstrdup(abs_path);
     542             :     }
     543             : }
     544             : 
     545             : /*
     546             :  * Read and parse a single configuration file.  This function recurses
     547             :  * to handle "include" directives.
     548             :  *
     549             :  * If "strict" is true, treat failure to open the config file as an error,
     550             :  * otherwise just skip the file.
     551             :  *
     552             :  * calling_file/calling_lineno identify the source of the request.
     553             :  * Pass NULL/0 if not recursing from an inclusion request.
     554             :  *
     555             :  * See ParseConfigFp for further details.  This one merely adds opening the
     556             :  * config file rather than working from a caller-supplied file descriptor,
     557             :  * and absolute-ifying the path name if necessary.
     558             :  */
     559             : bool
     560        7216 : ParseConfigFile(const char *config_file, bool strict,
     561             :                 const char *calling_file, int calling_lineno,
     562             :                 int depth, int elevel,
     563             :                 ConfigVariable **head_p,
     564             :                 ConfigVariable **tail_p)
     565             : {
     566             :     char       *abs_path;
     567        7216 :     bool        OK = true;
     568             :     FILE       *fp;
     569             : 
     570             :     /*
     571             :      * Reject file name that is all-blank (including empty), as that leads to
     572             :      * confusion --- we'd try to read the containing directory as a file.
     573             :      */
     574        7216 :     if (strspn(config_file, " \t\r\n") == strlen(config_file))
     575             :     {
     576           0 :         ereport(elevel,
     577             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     578             :                  errmsg("empty configuration file name: \"%s\"",
     579             :                         config_file)));
     580           0 :         record_config_file_error("empty configuration file name",
     581             :                                  calling_file, calling_lineno,
     582             :                                  head_p, tail_p);
     583           0 :         return false;
     584             :     }
     585             : 
     586             :     /*
     587             :      * Reject too-deep include nesting depth.  This is just a safety check to
     588             :      * avoid dumping core due to stack overflow if an include file loops back
     589             :      * to itself.  The maximum nesting depth is pretty arbitrary.
     590             :      */
     591        7216 :     if (depth > 10)
     592             :     {
     593           0 :         ereport(elevel,
     594             :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     595             :                  errmsg("could not open configuration file \"%s\": maximum nesting depth exceeded",
     596             :                         config_file)));
     597           0 :         record_config_file_error("nesting depth exceeded",
     598             :                                  calling_file, calling_lineno,
     599             :                                  head_p, tail_p);
     600           0 :         return false;
     601             :     }
     602             : 
     603        7216 :     abs_path = AbsoluteConfigLocation(config_file, calling_file);
     604             : 
     605             :     /*
     606             :      * Reject direct recursion.  Indirect recursion is also possible, but it's
     607             :      * harder to detect and so doesn't seem worth the trouble.  (We test at
     608             :      * this step because the canonicalization done by AbsoluteConfigLocation
     609             :      * makes it more likely that a simple strcmp comparison will match.)
     610             :      */
     611        7216 :     if (calling_file && strcmp(abs_path, calling_file) == 0)
     612             :     {
     613           0 :         ereport(elevel,
     614             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     615             :                  errmsg("configuration file recursion in \"%s\"",
     616             :                         calling_file)));
     617           0 :         record_config_file_error("configuration file recursion",
     618             :                                  calling_file, calling_lineno,
     619             :                                  head_p, tail_p);
     620           0 :         pfree(abs_path);
     621           0 :         return false;
     622             :     }
     623             : 
     624        7216 :     fp = AllocateFile(abs_path, "r");
     625        7216 :     if (!fp)
     626             :     {
     627         716 :         if (strict)
     628             :         {
     629           0 :             ereport(elevel,
     630             :                     (errcode_for_file_access(),
     631             :                      errmsg("could not open configuration file \"%s\": %m",
     632             :                             abs_path)));
     633           0 :             record_config_file_error(psprintf("could not open file \"%s\"",
     634             :                                               abs_path),
     635             :                                      calling_file, calling_lineno,
     636             :                                      head_p, tail_p);
     637           0 :             OK = false;
     638             :         }
     639             :         else
     640             :         {
     641         716 :             ereport(LOG,
     642             :                     (errmsg("skipping missing configuration file \"%s\"",
     643             :                             abs_path)));
     644             :         }
     645         716 :         goto cleanup;
     646             :     }
     647             : 
     648        6500 :     OK = ParseConfigFp(fp, abs_path, depth, elevel, head_p, tail_p);
     649             : 
     650        7216 : cleanup:
     651        7216 :     if (fp)
     652        6500 :         FreeFile(fp);
     653        7216 :     pfree(abs_path);
     654             : 
     655        7216 :     return OK;
     656             : }
     657             : 
     658             : /*
     659             :  * Capture an error message in the ConfigVariable list returned by
     660             :  * config file parsing.
     661             :  */
     662             : static void
     663           0 : record_config_file_error(const char *errmsg,
     664             :                          const char *config_file,
     665             :                          int lineno,
     666             :                          ConfigVariable **head_p,
     667             :                          ConfigVariable **tail_p)
     668             : {
     669             :     ConfigVariable *item;
     670             : 
     671           0 :     item = palloc(sizeof *item);
     672           0 :     item->name = NULL;
     673           0 :     item->value = NULL;
     674           0 :     item->errmsg = pstrdup(errmsg);
     675           0 :     item->filename = config_file ? pstrdup(config_file) : NULL;
     676           0 :     item->sourceline = lineno;
     677           0 :     item->ignore = true;
     678           0 :     item->applied = false;
     679           0 :     item->next = NULL;
     680           0 :     if (*head_p == NULL)
     681           0 :         *head_p = item;
     682             :     else
     683           0 :         (*tail_p)->next = item;
     684           0 :     *tail_p = item;
     685           0 : }
     686             : 
     687             : /*
     688             :  * Flex fatal errors bring us here.  Stash the error message and jump back to
     689             :  * ParseConfigFp().  Assume all msg arguments point to string constants; this
     690             :  * holds for flex 2.5.31 (earliest we support) and flex 2.5.35 (latest as of
     691             :  * this writing).  Otherwise, we would need to copy the message.
     692             :  *
     693             :  * We return "int" since this takes the place of calls to fprintf().
     694             : */
     695             : static int
     696           0 : GUC_flex_fatal(const char *msg)
     697             : {
     698           0 :     GUC_flex_fatal_errmsg = msg;
     699           0 :     siglongjmp(*GUC_flex_fatal_jmp, 1);
     700             :     return 0;                   /* keep compiler quiet */
     701             : }
     702             : 
     703             : /*
     704             :  * Read and parse a single configuration file.  This function recurses
     705             :  * to handle "include" directives.
     706             :  *
     707             :  * Input parameters:
     708             :  *  fp: file pointer from AllocateFile for the configuration file to parse
     709             :  *  config_file: absolute or relative path name of the configuration file
     710             :  *  depth: recursion depth (should be 0 in the outermost call)
     711             :  *  elevel: error logging level to use
     712             :  * Input/Output parameters:
     713             :  *  head_p, tail_p: head and tail of linked list of name/value pairs
     714             :  *
     715             :  * *head_p and *tail_p must be initialized, either to NULL or valid pointers
     716             :  * to a ConfigVariable list, before calling the outer recursion level.  Any
     717             :  * name-value pairs read from the input file(s) will be appended to the list.
     718             :  * Error reports will also be appended to the list, if elevel < ERROR.
     719             :  *
     720             :  * Returns TRUE if successful, FALSE if an error occurred.  The error has
     721             :  * already been ereport'd, it is only necessary for the caller to clean up
     722             :  * its own state and release the ConfigVariable list.
     723             :  *
     724             :  * Note: if elevel >= ERROR then an error will not return control to the
     725             :  * caller, so there is no need to check the return value in that case.
     726             :  *
     727             :  * Note: this function is used to parse not only postgresql.conf, but
     728             :  * various other configuration files that use the same "name = value"
     729             :  * syntax.  Hence, do not do anything here or in the subsidiary routines
     730             :  * ParseConfigFile/ParseConfigDirectory that assumes we are processing
     731             :  * GUCs specifically.
     732             :  */
     733             : bool
     734        7824 : ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
     735             :               ConfigVariable **head_p, ConfigVariable **tail_p)
     736             : {
     737        7824 :     volatile bool OK = true;
     738        7824 :     unsigned int save_ConfigFileLineno = ConfigFileLineno;
     739        7824 :     sigjmp_buf *save_GUC_flex_fatal_jmp = GUC_flex_fatal_jmp;
     740             :     sigjmp_buf  flex_fatal_jmp;
     741        7824 :     volatile YY_BUFFER_STATE lex_buffer = NULL;
     742             :     int         errorcount;
     743             :     int         token;
     744             : 
     745        7824 :     if (sigsetjmp(flex_fatal_jmp, 1) == 0)
     746        7824 :         GUC_flex_fatal_jmp = &flex_fatal_jmp;
     747             :     else
     748             :     {
     749             :         /*
     750             :          * Regain control after a fatal, internal flex error.  It may have
     751             :          * corrupted parser state.  Consequently, abandon the file, but trust
     752             :          * that the state remains sane enough for yy_delete_buffer().
     753             :          */
     754           0 :         elog(elevel, "%s at file \"%s\" line %u",
     755             :              GUC_flex_fatal_errmsg, config_file, ConfigFileLineno);
     756           0 :         record_config_file_error(GUC_flex_fatal_errmsg,
     757             :                                  config_file, ConfigFileLineno,
     758             :                                  head_p, tail_p);
     759           0 :         OK = false;
     760           0 :         goto cleanup;
     761             :     }
     762             : 
     763             :     /*
     764             :      * Parse
     765             :      */
     766        7824 :     ConfigFileLineno = 1;
     767        7824 :     errorcount = 0;
     768             : 
     769        7824 :     lex_buffer = yy_create_buffer(fp, YY_BUF_SIZE);
     770        7824 :     yy_switch_to_buffer(lex_buffer);
     771             : 
     772             :     /* This loop iterates once per logical line */
     773     2576824 :     while ((token = yylex()))
     774             :     {
     775     2569000 :         char       *opt_name = NULL;
     776     2569000 :         char       *opt_value = NULL;
     777             :         ConfigVariable *item;
     778             : 
     779     2569000 :         if (token == GUC_EOL)   /* empty or comment line */
     780     2490256 :             continue;
     781             : 
     782             :         /* first token on line is option name */
     783       78744 :         if (token != GUC_ID && token != GUC_QUALIFIED_ID)
     784           0 :             goto parse_error;
     785       78744 :         opt_name = pstrdup(yytext);
     786             : 
     787             :         /* next we have an optional equal sign; discard if present */
     788       78744 :         token = yylex();
     789       78744 :         if (token == GUC_EQUALS)
     790       78688 :             token = yylex();
     791             : 
     792             :         /* now we must have the option value */
     793       78744 :         if (token != GUC_ID &&
     794       22264 :             token != GUC_STRING &&
     795           8 :             token != GUC_INTEGER &&
     796           0 :             token != GUC_REAL &&
     797             :             token != GUC_UNQUOTED_STRING)
     798           0 :             goto parse_error;
     799       78744 :         if (token == GUC_STRING)    /* strip quotes and escapes */
     800       39364 :             opt_value = GUC_scanstr(yytext);
     801             :         else
     802       39380 :             opt_value = pstrdup(yytext);
     803             : 
     804             :         /* now we'd like an end of line, or possibly EOF */
     805       78744 :         token = yylex();
     806       78744 :         if (token != GUC_EOL)
     807             :         {
     808           0 :             if (token != 0)
     809           0 :                 goto parse_error;
     810             :             /* treat EOF like \n for line numbering purposes, cf bug 4752 */
     811           0 :             ConfigFileLineno++;
     812             :         }
     813             : 
     814             :         /* OK, process the option name and value */
     815       78744 :         if (guc_name_compare(opt_name, "include_dir") == 0)
     816             :         {
     817             :             /*
     818             :              * An include_dir directive isn't a variable and should be
     819             :              * processed immediately.
     820             :              */
     821           0 :             if (!ParseConfigDirectory(opt_value,
     822           0 :                                       config_file, ConfigFileLineno - 1,
     823             :                                       depth + 1, elevel,
     824             :                                       head_p, tail_p))
     825           0 :                 OK = false;
     826           0 :             yy_switch_to_buffer(lex_buffer);
     827           0 :             pfree(opt_name);
     828           0 :             pfree(opt_value);
     829             :         }
     830       78744 :         else if (guc_name_compare(opt_name, "include_if_exists") == 0)
     831             :         {
     832             :             /*
     833             :              * An include_if_exists directive isn't a variable and should be
     834             :              * processed immediately.
     835             :              */
     836           0 :             if (!ParseConfigFile(opt_value, false,
     837           0 :                                  config_file, ConfigFileLineno - 1,
     838             :                                  depth + 1, elevel,
     839             :                                  head_p, tail_p))
     840           0 :                 OK = false;
     841           0 :             yy_switch_to_buffer(lex_buffer);
     842           0 :             pfree(opt_name);
     843           0 :             pfree(opt_value);
     844             :         }
     845       78744 :         else if (guc_name_compare(opt_name, "include") == 0)
     846             :         {
     847             :             /*
     848             :              * An include directive isn't a variable and should be processed
     849             :              * immediately.
     850             :              */
     851          56 :             if (!ParseConfigFile(opt_value, true,
     852          56 :                                  config_file, ConfigFileLineno - 1,
     853             :                                  depth + 1, elevel,
     854             :                                  head_p, tail_p))
     855           0 :                 OK = false;
     856          56 :             yy_switch_to_buffer(lex_buffer);
     857          56 :             pfree(opt_name);
     858          56 :             pfree(opt_value);
     859             :         }
     860             :         else
     861             :         {
     862             :             /* ordinary variable, append to list */
     863       78688 :             item = palloc(sizeof *item);
     864       78688 :             item->name = opt_name;
     865       78688 :             item->value = opt_value;
     866       78688 :             item->errmsg = NULL;
     867       78688 :             item->filename = pstrdup(config_file);
     868       78688 :             item->sourceline = ConfigFileLineno - 1;
     869       78688 :             item->ignore = false;
     870       78688 :             item->applied = false;
     871       78688 :             item->next = NULL;
     872       78688 :             if (*head_p == NULL)
     873        4552 :                 *head_p = item;
     874             :             else
     875       74136 :                 (*tail_p)->next = item;
     876       78688 :             *tail_p = item;
     877             :         }
     878             : 
     879             :         /* break out of loop if read EOF, else loop for next line */
     880       78744 :         if (token == 0)
     881           0 :             break;
     882       78744 :         continue;
     883             : 
     884           0 : parse_error:
     885             :         /* release storage if we allocated any on this line */
     886           0 :         if (opt_name)
     887           0 :             pfree(opt_name);
     888           0 :         if (opt_value)
     889           0 :             pfree(opt_value);
     890             : 
     891             :         /* report the error */
     892           0 :         if (token == GUC_EOL || token == 0)
     893             :         {
     894           0 :             ereport(elevel,
     895             :                     (errcode(ERRCODE_SYNTAX_ERROR),
     896             :               errmsg("syntax error in file \"%s\" line %u, near end of line",
     897             :                      config_file, ConfigFileLineno - 1)));
     898           0 :             record_config_file_error("syntax error",
     899           0 :                                      config_file, ConfigFileLineno - 1,
     900             :                                      head_p, tail_p);
     901             :         }
     902             :         else
     903             :         {
     904           0 :             ereport(elevel,
     905             :                     (errcode(ERRCODE_SYNTAX_ERROR),
     906             :              errmsg("syntax error in file \"%s\" line %u, near token \"%s\"",
     907             :                     config_file, ConfigFileLineno, yytext)));
     908           0 :             record_config_file_error("syntax error",
     909             :                                      config_file, ConfigFileLineno,
     910             :                                      head_p, tail_p);
     911             :         }
     912           0 :         OK = false;
     913           0 :         errorcount++;
     914             : 
     915             :         /*
     916             :          * To avoid producing too much noise when fed a totally bogus file,
     917             :          * give up after 100 syntax errors per file (an arbitrary number).
     918             :          * Also, if we're only logging the errors at DEBUG level anyway, might
     919             :          * as well give up immediately.  (This prevents postmaster children
     920             :          * from bloating the logs with duplicate complaints.)
     921             :          */
     922           0 :         if (errorcount >= 100 || elevel <= DEBUG1)
     923             :         {
     924           0 :             ereport(elevel,
     925             :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     926             :                errmsg("too many syntax errors found, abandoning file \"%s\"",
     927             :                       config_file)));
     928           0 :             break;
     929             :         }
     930             : 
     931             :         /* resync to next end-of-line or EOF */
     932           0 :         while (token != GUC_EOL && token != 0)
     933           0 :             token = yylex();
     934             :         /* break out of loop on EOF */
     935           0 :         if (token == 0)
     936           0 :             break;
     937             :     }
     938             : 
     939        7824 : cleanup:
     940        7824 :     yy_delete_buffer(lex_buffer);
     941             :     /* Each recursion level must save and restore these static variables. */
     942        7824 :     ConfigFileLineno = save_ConfigFileLineno;
     943        7824 :     GUC_flex_fatal_jmp = save_GUC_flex_fatal_jmp;
     944        7824 :     return OK;
     945             : }
     946             : 
     947             : /*
     948             :  * Read and parse all config files in a subdirectory in alphabetical order
     949             :  *
     950             :  * includedir is the absolute or relative path to the subdirectory to scan.
     951             :  *
     952             :  * calling_file/calling_lineno identify the source of the request.
     953             :  * Pass NULL/0 if not recursing from an inclusion request.
     954             :  *
     955             :  * See ParseConfigFp for further details.
     956             :  */
     957             : bool
     958           0 : ParseConfigDirectory(const char *includedir,
     959             :                      const char *calling_file, int calling_lineno,
     960             :                      int depth, int elevel,
     961             :                      ConfigVariable **head_p,
     962             :                      ConfigVariable **tail_p)
     963             : {
     964             :     char       *directory;
     965             :     DIR        *d;
     966             :     struct dirent *de;
     967             :     char      **filenames;
     968             :     int         num_filenames;
     969             :     int         size_filenames;
     970             :     bool        status;
     971             : 
     972             :     /*
     973             :      * Reject directory name that is all-blank (including empty), as that
     974             :      * leads to confusion --- we'd read the containing directory, typically
     975             :      * resulting in recursive inclusion of the same file(s).
     976             :      */
     977           0 :     if (strspn(includedir, " \t\r\n") == strlen(includedir))
     978             :     {
     979           0 :         ereport(elevel,
     980             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     981             :                  errmsg("empty configuration directory name: \"%s\"",
     982             :                         includedir)));
     983           0 :         record_config_file_error("empty configuration directory name",
     984             :                                  calling_file, calling_lineno,
     985             :                                  head_p, tail_p);
     986           0 :         return false;
     987             :     }
     988             : 
     989             :     /*
     990             :      * We don't check for recursion or too-deep nesting depth here; the
     991             :      * subsequent calls to ParseConfigFile will take care of that.
     992             :      */
     993             : 
     994           0 :     directory = AbsoluteConfigLocation(includedir, calling_file);
     995           0 :     d = AllocateDir(directory);
     996           0 :     if (d == NULL)
     997             :     {
     998           0 :         ereport(elevel,
     999             :                 (errcode_for_file_access(),
    1000             :                  errmsg("could not open configuration directory \"%s\": %m",
    1001             :                         directory)));
    1002           0 :         record_config_file_error(psprintf("could not open directory \"%s\"",
    1003             :                                           directory),
    1004             :                                  calling_file, calling_lineno,
    1005             :                                  head_p, tail_p);
    1006           0 :         status = false;
    1007           0 :         goto cleanup;
    1008             :     }
    1009             : 
    1010             :     /*
    1011             :      * Read the directory and put the filenames in an array, so we can sort
    1012             :      * them prior to processing the contents.
    1013             :      */
    1014           0 :     size_filenames = 32;
    1015           0 :     filenames = (char **) palloc(size_filenames * sizeof(char *));
    1016           0 :     num_filenames = 0;
    1017             : 
    1018           0 :     while ((de = ReadDir(d, directory)) != NULL)
    1019             :     {
    1020             :         struct stat st;
    1021             :         char        filename[MAXPGPATH];
    1022             : 
    1023             :         /*
    1024             :          * Only parse files with names ending in ".conf".  Explicitly reject
    1025             :          * files starting with ".".  This excludes things like "." and "..",
    1026             :          * as well as typical hidden files, backup files, and editor debris.
    1027             :          */
    1028           0 :         if (strlen(de->d_name) < 6)
    1029           0 :             continue;
    1030           0 :         if (de->d_name[0] == '.')
    1031           0 :             continue;
    1032           0 :         if (strcmp(de->d_name + strlen(de->d_name) - 5, ".conf") != 0)
    1033           0 :             continue;
    1034             : 
    1035           0 :         join_path_components(filename, directory, de->d_name);
    1036           0 :         canonicalize_path(filename);
    1037           0 :         if (stat(filename, &st) == 0)
    1038             :         {
    1039           0 :             if (!S_ISDIR(st.st_mode))
    1040             :             {
    1041             :                 /* Add file to array, increasing its size in blocks of 32 */
    1042           0 :                 if (num_filenames >= size_filenames)
    1043             :                 {
    1044           0 :                     size_filenames += 32;
    1045           0 :                     filenames = (char **) repalloc(filenames,
    1046             :                                             size_filenames * sizeof(char *));
    1047             :                 }
    1048           0 :                 filenames[num_filenames] = pstrdup(filename);
    1049           0 :                 num_filenames++;
    1050             :             }
    1051             :         }
    1052             :         else
    1053             :         {
    1054             :             /*
    1055             :              * stat does not care about permissions, so the most likely reason
    1056             :              * a file can't be accessed now is if it was removed between the
    1057             :              * directory listing and now.
    1058             :              */
    1059           0 :             ereport(elevel,
    1060             :                     (errcode_for_file_access(),
    1061             :                      errmsg("could not stat file \"%s\": %m",
    1062             :                             filename)));
    1063           0 :             record_config_file_error(psprintf("could not stat file \"%s\"",
    1064             :                                               filename),
    1065             :                                      calling_file, calling_lineno,
    1066             :                                      head_p, tail_p);
    1067           0 :             status = false;
    1068           0 :             goto cleanup;
    1069             :         }
    1070             :     }
    1071             : 
    1072           0 :     if (num_filenames > 0)
    1073             :     {
    1074             :         int         i;
    1075             : 
    1076           0 :         qsort(filenames, num_filenames, sizeof(char *), pg_qsort_strcmp);
    1077           0 :         for (i = 0; i < num_filenames; i++)
    1078             :         {
    1079           0 :             if (!ParseConfigFile(filenames[i], true,
    1080             :                                  calling_file, calling_lineno,
    1081             :                                  depth, elevel,
    1082             :                                  head_p, tail_p))
    1083             :             {
    1084           0 :                 status = false;
    1085           0 :                 goto cleanup;
    1086             :             }
    1087             :         }
    1088             :     }
    1089           0 :     status = true;
    1090             : 
    1091           0 : cleanup:
    1092           0 :     if (d)
    1093           0 :         FreeDir(d);
    1094           0 :     pfree(directory);
    1095           0 :     return status;
    1096             : }
    1097             : 
    1098             : /*
    1099             :  * Free a list of ConfigVariables, including the names and the values
    1100             :  */
    1101             : void
    1102        1324 : FreeConfigVariables(ConfigVariable *list)
    1103             : {
    1104             :     ConfigVariable *item;
    1105             : 
    1106        1324 :     item = list;
    1107        8210 :     while (item)
    1108             :     {
    1109        6886 :         ConfigVariable *next = item->next;
    1110             : 
    1111        6886 :         FreeConfigVariable(item);
    1112        6886 :         item = next;
    1113             :     }
    1114        1324 : }
    1115             : 
    1116             : /*
    1117             :  * Free a single ConfigVariable
    1118             :  */
    1119             : static void
    1120        6886 : FreeConfigVariable(ConfigVariable *item)
    1121             : {
    1122        6886 :     if (item->name)
    1123        6886 :         pfree(item->name);
    1124        6886 :     if (item->value)
    1125        6886 :         pfree(item->value);
    1126        6886 :     if (item->errmsg)
    1127           0 :         pfree(item->errmsg);
    1128        6886 :     if (item->filename)
    1129        6886 :         pfree(item->filename);
    1130        6886 :     pfree(item);
    1131        6886 : }
    1132             : 
    1133             : 
    1134             : /*
    1135             :  *      scanstr
    1136             :  *
    1137             :  * Strip the quotes surrounding the given string, and collapse any embedded
    1138             :  * '' sequences and backslash escapes.
    1139             :  *
    1140             :  * the string returned is palloc'd and should eventually be pfree'd by the
    1141             :  * caller.
    1142             :  */
    1143             : static char *
    1144       39364 : GUC_scanstr(const char *s)
    1145             : {
    1146             :     char       *newStr;
    1147             :     int         len,
    1148             :                 i,
    1149             :                 j;
    1150             : 
    1151             :     Assert(s != NULL && s[0] == '\'');
    1152       39364 :     len = strlen(s);
    1153             :     Assert(len >= 2);
    1154             :     Assert(s[len - 1] == '\'');
    1155             : 
    1156             :     /* Skip the leading quote; we'll handle the trailing quote below */
    1157       39364 :     s++, len--;
    1158             : 
    1159             :     /* Since len still includes trailing quote, this is enough space */
    1160       39364 :     newStr = palloc(len);
    1161             : 
    1162      502616 :     for (i = 0, j = 0; i < len; i++)
    1163             :     {
    1164      463252 :         if (s[i] == '\\')
    1165             :         {
    1166           0 :             i++;
    1167           0 :             switch (s[i])
    1168             :             {
    1169           0 :                 case 'b':
    1170           0 :                     newStr[j] = '\b';
    1171           0 :                     break;
    1172           0 :                 case 'f':
    1173           0 :                     newStr[j] = '\f';
    1174           0 :                     break;
    1175           0 :                 case 'n':
    1176           0 :                     newStr[j] = '\n';
    1177           0 :                     break;
    1178           0 :                 case 'r':
    1179           0 :                     newStr[j] = '\r';
    1180           0 :                     break;
    1181           0 :                 case 't':
    1182           0 :                     newStr[j] = '\t';
    1183           0 :                     break;
    1184           0 :                 case '0':
    1185             :                 case '1':
    1186             :                 case '2':
    1187             :                 case '3':
    1188             :                 case '4':
    1189             :                 case '5':
    1190             :                 case '6':
    1191             :                 case '7':
    1192             :                     {
    1193             :                         int         k;
    1194           0 :                         long        octVal = 0;
    1195             : 
    1196           0 :                         for (k = 0;
    1197           0 :                              s[i + k] >= '0' && s[i + k] <= '7' && k < 3;
    1198           0 :                              k++)
    1199           0 :                             octVal = (octVal << 3) + (s[i + k] - '0');
    1200           0 :                         i += k - 1;
    1201           0 :                         newStr[j] = ((char) octVal);
    1202             :                     }
    1203           0 :                     break;
    1204           0 :                 default:
    1205           0 :                     newStr[j] = s[i];
    1206           0 :                     break;
    1207             :             }                   /* switch */
    1208             :         }
    1209      463252 :         else if (s[i] == '\'' && s[i + 1] == '\'')
    1210             :         {
    1211             :             /* doubled quote becomes just one quote */
    1212          32 :             newStr[j] = s[++i];
    1213             :         }
    1214             :         else
    1215      463220 :             newStr[j] = s[i];
    1216      463252 :         j++;
    1217             :     }
    1218             : 
    1219             :     /* We copied the ending quote to newStr, so replace with \0 */
    1220             :     Assert(j > 0 && j <= len);
    1221       39364 :     newStr[--j] = '\0';
    1222             : 
    1223       39364 :     return newStr;
    1224             : }

Generated by: LCOV version 1.13