LCOV - code coverage report
Current view: top level - src/backend/utils/misc - guc-file.l (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 189 370 51.1 %
Date: 2019-09-19 17:07:13 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-2019, 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             : ProcessConfigFile(GucContext context)
     124        3910 : {
     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             :     elevel = IsUnderPostmaster ? DEBUG2 : LOG;
     141        3910 : 
     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             :     config_cxt = AllocSetContextCreate(CurrentMemoryContext,
     149        3910 :                                        "config file processing",
     150             :                                        ALLOCSET_DEFAULT_SIZES);
     151             :     caller_cxt = MemoryContextSwitchTo(config_cxt);
     152        3910 : 
     153             :     /*
     154             :      * Read and apply the config file.  We don't need to examine the result.
     155             :      */
     156             :     (void) ProcessConfigFileInternal(context, true, elevel);
     157        3910 : 
     158             :     /* Clean up */
     159             :     MemoryContextSwitchTo(caller_cxt);
     160        3908 :     MemoryContextDelete(config_cxt);
     161        3908 : }
     162        3908 : 
     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             : ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
     172        3914 : {
     173             :     bool        error = false;
     174        3914 :     bool        applying = false;
     175        3914 :     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             :     ConfFileWithError = ConfigFileName;
     183        3914 :     head = tail = NULL;
     184        3914 : 
     185             :     if (!ParseConfigFile(ConfigFileName, true,
     186        3914 :                          NULL, 0, 0, elevel,
     187             :                          &head, &tail))
     188             :     {
     189             :         /* Syntax error(s) detected in the file, so bail out */
     190             :         error = true;
     191           0 :         goto bail_out;
     192           0 :     }
     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             :     if (DataDir)
     201        3914 :     {
     202             :         if (!ParseConfigFile(PG_AUTOCONF_FILENAME, false,
     203        2052 :                              NULL, 0, 0, elevel,
     204             :                              &head, &tail))
     205             :         {
     206             :             /* Syntax error(s) detected in the file, so bail out */
     207             :             error = true;
     208           0 :             ConfFileWithError = PG_AUTOCONF_FILENAME;
     209           0 :             goto bail_out;
     210           0 :         }
     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             :         ConfigVariable *newlist = NULL;
     223        1862 : 
     224             :         /*
     225             :          * Prune all items except the last "data_directory" from the list.
     226             :          */
     227             :         for (item = head; item; item = item->next)
     228       26502 :         {
     229             :             if (!item->ignore &&
     230       49280 :                 strcmp(item->name, "data_directory") == 0)
     231       24640 :                 newlist = item;
     232           0 :         }
     233             : 
     234             :         if (newlist)
     235        1862 :             newlist->next = NULL;
     236           0 :         head = tail = newlist;
     237        1862 : 
     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             :         if (head == NULL)
     246        1862 :             goto bail_out;
     247        1862 :     }
     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             :     for (i = 0; i < num_guc_variables; i++)
     255      658756 :     {
     256             :         struct config_generic *gconf = guc_variables[i];
     257      656704 : 
     258             :         gconf->status &= ~GUC_IS_IN_FILE;
     259      656704 :     }
     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             :     for (item = head; item; item = item->next)
     275       32604 :     {
     276             :         struct config_generic *record;
     277             : 
     278             :         /* Ignore anything already marked as ignorable */
     279             :         if (item->ignore)
     280       30552 :             continue;
     281           0 : 
     282             :         /*
     283             :          * Try to find the variable; but do not create a custom placeholder if
     284             :          * it's not there already.
     285             :          */
     286             :         record = find_option(item->name, false, elevel);
     287       30552 : 
     288             :         if (record)
     289       30552 :         {
     290             :             /* If it's already marked, then this is a duplicate entry */
     291             :             if (record->status & GUC_IS_IN_FILE)
     292       30550 :             {
     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             :                 for (pitem = head; pitem != item; pitem = pitem->next)
     301       50448 :                 {
     302             :                     if (!pitem->ignore &&
     303       94106 :                         strcmp(pitem->name, item->name) == 0)
     304       45460 :                         pitem->ignore = true;
     305        1802 :                 }
     306             :             }
     307             :             /* Now mark it as present in file */
     308             :             record->status |= GUC_IS_IN_FILE;
     309       30550 :         }
     310             :         else if (strchr(item->name, GUC_QUALIFIER_SEPARATOR) == NULL)
     311           2 :         {
     312             :             /* Invalid non-custom variable, so complain */
     313             :             ereport(elevel,
     314           0 :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
     315             :                      errmsg("unrecognized configuration parameter \"%s\" in file \"%s\" line %u",
     316             :                             item->name,
     317             :                             item->filename, item->sourceline)));
     318             :             item->errmsg = pstrdup("unrecognized configuration parameter");
     319           0 :             error = true;
     320           0 :             ConfFileWithError = item->filename;
     321           0 :         }
     322             :     }
     323             : 
     324             :     /*
     325             :      * If we've detected any errors so far, we don't want to risk applying any
     326             :      * changes.
     327             :      */
     328             :     if (error)
     329        2052 :         goto bail_out;
     330           0 : 
     331             :     /* Otherwise, set flag that we're beginning to apply changes */
     332             :     applying = true;
     333        2052 : 
     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             :     for (i = 0; i < num_guc_variables; i++)
     341      658756 :     {
     342             :         struct config_generic *gconf = guc_variables[i];
     343      656704 :         GucStack   *stack;
     344             : 
     345             :         if (gconf->reset_source != PGC_S_FILE ||
     346      661952 :             (gconf->status & GUC_IS_IN_FILE))
     347        5248 :             continue;
     348      656704 :         if (gconf->context < PGC_SIGHUP)
     349           0 :         {
     350             :             ereport(elevel,
     351           0 :                     (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
     352             :                      errmsg("parameter \"%s\" cannot be changed without restarting the server",
     353             :                             gconf->name)));
     354             :             record_config_file_error(psprintf("parameter \"%s\" cannot be changed without restarting the server",
     355           0 :                                               gconf->name),
     356             :                                      NULL, 0,
     357             :                                      &head, &tail);
     358             :             error = true;
     359           0 :             continue;
     360           0 :         }
     361             : 
     362             :         /* No more to do if we're just doing show_all_file_settings() */
     363             :         if (!applySettings)
     364           0 :             continue;
     365           0 : 
     366             :         /*
     367             :          * Reset any "file" sources to "default", else set_config_option will
     368             :          * not override those settings.
     369             :          */
     370             :         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           0 :         {
     376             :             if (stack->source == PGC_S_FILE)
     377           0 :                 stack->source = PGC_S_DEFAULT;
     378           0 :         }
     379             : 
     380             :         /* Now we can re-apply the wired-in default (i.e., the boot_val) */
     381             :         if (set_config_option(gconf->name, NULL,
     382           0 :                               context, PGC_S_DEFAULT,
     383             :                               GUC_ACTION_SET, true, 0, false) > 0)
     384             :         {
     385             :             /* Log the change if appropriate */
     386             :             if (context == PGC_SIGHUP)
     387           0 :                 ereport(elevel,
     388           0 :                         (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             :     if (context == PGC_SIGHUP && applySettings)
     408        2052 :     {
     409             :         InitializeGUCOptionsFromEnvironment();
     410         186 :         pg_timezone_abbrev_initialize();
     411         186 :         /* this selects SQL_ASCII in processes not connected to a database */
     412             :         SetConfigOption("client_encoding", GetDatabaseEncodingName(),
     413         186 :                         PGC_BACKEND, PGC_S_DYNAMIC_DEFAULT);
     414             :     }
     415             : 
     416             :     /*
     417             :      * Now apply the values from the config file.
     418             :      */
     419             :     for (item = head; item; item = item->next)
     420       32602 :     {
     421             :         char       *pre_value = NULL;
     422       30552 :         int         scres;
     423             : 
     424             :         /* Ignore anything marked as ignorable */
     425             :         if (item->ignore)
     426       30552 :             continue;
     427        1802 : 
     428             :         /* In SIGHUP cases in the postmaster, we want to report changes */
     429             :         if (context == PGC_SIGHUP && applySettings && !IsUnderPostmaster)
     430       28750 :         {
     431             :             const char *preval = GetConfigOption(item->name, true, false);
     432        1366 : 
     433             :             /* If option doesn't exist yet or is NULL, treat as empty string */
     434             :             if (!preval)
     435        1366 :                 preval = "";
     436           0 :             /* must dup, else might have dangling pointer below */
     437             :             pre_value = pstrdup(preval);
     438        1366 :         }
     439             : 
     440             :         scres = set_config_option(item->name, item->value,
     441       28750 :                                   context, PGC_S_FILE,
     442             :                                   GUC_ACTION_SET, applySettings, 0, false);
     443             :         if (scres > 0)
     444       28748 :         {
     445             :             /* variable was updated, so log the change if appropriate */
     446             :             if (pre_value)
     447       26692 :             {
     448             :                 const char *post_value = GetConfigOption(item->name, true, false);
     449         860 : 
     450             :                 if (!post_value)
     451         860 :                     post_value = "";
     452           0 :                 if (strcmp(pre_value, post_value) != 0)
     453         860 :                     ereport(elevel,
     454          36 :                             (errmsg("parameter \"%s\" changed to \"%s\"",
     455             :                                     item->name, item->value)));
     456             :             }
     457             :             item->applied = true;
     458       26692 :         }
     459             :         else if (scres == 0)
     460        2056 :         {
     461             :             error = true;
     462           0 :             item->errmsg = pstrdup("setting could not be applied");
     463           0 :             ConfFileWithError = item->filename;
     464           0 :         }
     465             :         else
     466             :         {
     467             :             /* no error, but variable's active value was not changed */
     468             :             item->applied = true;
     469        2056 :         }
     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             :         if (scres != 0 && applySettings)
     478       28748 :             set_config_sourcefile(item->name, item->filename,
     479       28682 :                                   item->sourceline);
     480             : 
     481             :         if (pre_value)
     482       28748 :             pfree(pre_value);
     483        1366 :     }
     484             : 
     485             :     /* Remember when we last successfully loaded the config file. */
     486             :     if (applySettings)
     487        2050 :         PgReloadTime = GetCurrentTimestamp();
     488        2046 : 
     489             : bail_out:
     490             :     if (error && applySettings)
     491        3912 :     {
     492             :         /* During postmaster startup, any error is fatal */
     493             :         if (context == PGC_POSTMASTER)
     494           0 :             ereport(ERROR,
     495           0 :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
     496             :                      errmsg("configuration file \"%s\" contains errors",
     497             :                             ConfFileWithError)));
     498             :         else if (applying)
     499           0 :             ereport(elevel,
     500           0 :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
     501             :                      errmsg("configuration file \"%s\" contains errors; unaffected changes were applied",
     502             :                             ConfFileWithError)));
     503             :         else
     504             :             ereport(elevel,
     505           0 :                     (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             :     return head;
     512        3912 : }
     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             : AbsoluteConfigLocation(const char *location, const char *calling_file)
     521        6014 : {
     522             :     char        abs_path[MAXPGPATH];
     523             : 
     524             :     if (is_absolute_path(location))
     525        6014 :         return pstrdup(location);
     526        3914 :     else
     527             :     {
     528             :         if (calling_file != NULL)
     529        2100 :         {
     530             :             strlcpy(abs_path, calling_file, sizeof(abs_path));
     531          48 :             get_parent_directory(abs_path);
     532          48 :             join_path_components(abs_path, abs_path, location);
     533          48 :             canonicalize_path(abs_path);
     534          48 :         }
     535             :         else
     536             :         {
     537             :             AssertState(DataDir);
     538             :             join_path_components(abs_path, DataDir, location);
     539        2052 :             canonicalize_path(abs_path);
     540        2052 :         }
     541             :         return pstrdup(abs_path);
     542        2100 :     }
     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             : ParseConfigFile(const char *config_file, bool strict,
     561        6014 :                 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             :     bool        OK = true;
     568        6014 :     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             :     if (strspn(config_file, " \t\r\n") == strlen(config_file))
     575        6014 :     {
     576             :         ereport(elevel,
     577           0 :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     578             :                  errmsg("empty configuration file name: \"%s\"",
     579             :                         config_file)));
     580             :         record_config_file_error("empty configuration file name",
     581           0 :                                  calling_file, calling_lineno,
     582             :                                  head_p, tail_p);
     583             :         return false;
     584           0 :     }
     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             :     if (depth > 10)
     592        6014 :     {
     593             :         ereport(elevel,
     594           0 :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     595             :                  errmsg("could not open configuration file \"%s\": maximum nesting depth exceeded",
     596             :                         config_file)));
     597             :         record_config_file_error("nesting depth exceeded",
     598           0 :                                  calling_file, calling_lineno,
     599             :                                  head_p, tail_p);
     600             :         return false;
     601           0 :     }
     602             : 
     603             :     abs_path = AbsoluteConfigLocation(config_file, calling_file);
     604        6014 : 
     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             :     if (calling_file && strcmp(abs_path, calling_file) == 0)
     612        6014 :     {
     613             :         ereport(elevel,
     614           0 :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     615             :                  errmsg("configuration file recursion in \"%s\"",
     616             :                         calling_file)));
     617             :         record_config_file_error("configuration file recursion",
     618           0 :                                  calling_file, calling_lineno,
     619             :                                  head_p, tail_p);
     620             :         pfree(abs_path);
     621           0 :         return false;
     622           0 :     }
     623             : 
     624             :     fp = AllocateFile(abs_path, "r");
     625        6014 :     if (!fp)
     626        6014 :     {
     627             :         if (strict)
     628         640 :         {
     629             :             ereport(elevel,
     630           0 :                     (errcode_for_file_access(),
     631             :                      errmsg("could not open configuration file \"%s\": %m",
     632             :                             abs_path)));
     633             :             record_config_file_error(psprintf("could not open file \"%s\"",
     634           0 :                                               abs_path),
     635             :                                      calling_file, calling_lineno,
     636             :                                      head_p, tail_p);
     637             :             OK = false;
     638           0 :         }
     639             :         else
     640             :         {
     641             :             ereport(LOG,
     642         640 :                     (errmsg("skipping missing configuration file \"%s\"",
     643             :                             abs_path)));
     644             :         }
     645             :         goto cleanup;
     646         640 :     }
     647             : 
     648             :     OK = ParseConfigFp(fp, abs_path, depth, elevel, head_p, tail_p);
     649        5374 : 
     650             : cleanup:
     651             :     if (fp)
     652        6014 :         FreeFile(fp);
     653        5374 :     pfree(abs_path);
     654        6014 : 
     655             :     return OK;
     656        6014 : }
     657             : 
     658             : /*
     659             :  * Capture an error message in the ConfigVariable list returned by
     660             :  * config file parsing.
     661             :  */
     662             : static void
     663             : record_config_file_error(const char *errmsg,
     664           0 :                          const char *config_file,
     665             :                          int lineno,
     666             :                          ConfigVariable **head_p,
     667             :                          ConfigVariable **tail_p)
     668             : {
     669             :     ConfigVariable *item;
     670             : 
     671             :     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           0 :     else
     683             :         (*tail_p)->next = item;
     684           0 :     *tail_p = item;
     685           0 : }
     686           0 : 
     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             : GUC_flex_fatal(const char *msg)
     697           0 : {
     698             :     GUC_flex_fatal_errmsg = msg;
     699           0 :     siglongjmp(*GUC_flex_fatal_jmp, 1);
     700           0 :     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             : ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
     735        6606 :               ConfigVariable **head_p, ConfigVariable **tail_p)
     736             : {
     737             :     volatile bool OK = true;
     738        6606 :     unsigned int save_ConfigFileLineno = ConfigFileLineno;
     739        6606 :     sigjmp_buf *save_GUC_flex_fatal_jmp = GUC_flex_fatal_jmp;
     740        6606 :     sigjmp_buf  flex_fatal_jmp;
     741             :     volatile YY_BUFFER_STATE lex_buffer = NULL;
     742        6606 :     int         errorcount;
     743             :     int         token;
     744             : 
     745             :     if (sigsetjmp(flex_fatal_jmp, 1) == 0)
     746        6606 :         GUC_flex_fatal_jmp = &flex_fatal_jmp;
     747        6606 :     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             :         elog(elevel, "%s at file \"%s\" line %u",
     755           0 :              GUC_flex_fatal_errmsg, config_file, ConfigFileLineno);
     756             :         record_config_file_error(GUC_flex_fatal_errmsg,
     757           0 :                                  config_file, ConfigFileLineno,
     758             :                                  head_p, tail_p);
     759             :         OK = false;
     760           0 :         goto cleanup;
     761           0 :     }
     762             : 
     763             :     /*
     764             :      * Parse
     765             :      */
     766             :     ConfigFileLineno = 1;
     767        6606 :     errorcount = 0;
     768        6606 : 
     769             :     lex_buffer = yy_create_buffer(fp, YY_BUF_SIZE);
     770        6606 :     yy_switch_to_buffer(lex_buffer);
     771        6606 : 
     772             :     /* This loop iterates once per logical line */
     773             :     while ((token = yylex()))
     774     2024316 :     {
     775             :         char       *opt_name = NULL;
     776     2011152 :         char       *opt_value = NULL;
     777     2011152 :         ConfigVariable *item;
     778             : 
     779             :         if (token == GUC_EOL)   /* empty or comment line */
     780     2011152 :             continue;
     781     1950136 : 
     782             :         /* first token on line is option name */
     783             :         if (token != GUC_ID && token != GUC_QUALIFIED_ID)
     784       61016 :             goto parse_error;
     785           0 :         opt_name = pstrdup(yytext);
     786       61016 : 
     787             :         /* next we have an optional equal sign; discard if present */
     788             :         token = yylex();
     789       61016 :         if (token == GUC_EQUALS)
     790       61016 :             token = yylex();
     791       60968 : 
     792             :         /* now we must have the option value */
     793             :         if (token != GUC_ID &&
     794       61016 :             token != GUC_STRING &&
     795       17010 :             token != GUC_INTEGER &&
     796           8 :             token != GUC_REAL &&
     797           0 :             token != GUC_UNQUOTED_STRING)
     798             :             goto parse_error;
     799           0 :         if (token == GUC_STRING)    /* strip quotes and escapes */
     800       61016 :             opt_value = GUC_scanstr(yytext);
     801       31490 :         else
     802             :             opt_value = pstrdup(yytext);
     803       29526 : 
     804             :         /* now we'd like an end of line, or possibly EOF */
     805             :         token = yylex();
     806       61016 :         if (token != GUC_EOL)
     807       61016 :         {
     808             :             if (token != 0)
     809          48 :                 goto parse_error;
     810           0 :             /* treat EOF like \n for line numbering purposes, cf bug 4752 */
     811             :             ConfigFileLineno++;
     812          48 :         }
     813             : 
     814             :         /* OK, process the option name and value */
     815             :         if (guc_name_compare(opt_name, "include_dir") == 0)
     816       61016 :         {
     817             :             /*
     818             :              * An include_dir directive isn't a variable and should be
     819             :              * processed immediately.
     820             :              */
     821             :             if (!ParseConfigDirectory(opt_value,
     822           0 :                                       config_file, ConfigFileLineno - 1,
     823           0 :                                       depth + 1, elevel,
     824             :                                       head_p, tail_p))
     825             :                 OK = false;
     826           0 :             yy_switch_to_buffer(lex_buffer);
     827           0 :             pfree(opt_name);
     828           0 :             pfree(opt_value);
     829           0 :         }
     830             :         else if (guc_name_compare(opt_name, "include_if_exists") == 0)
     831       61016 :         {
     832             :             /*
     833             :              * An include_if_exists directive isn't a variable and should be
     834             :              * processed immediately.
     835             :              */
     836             :             if (!ParseConfigFile(opt_value, false,
     837           0 :                                  config_file, ConfigFileLineno - 1,
     838           0 :                                  depth + 1, elevel,
     839             :                                  head_p, tail_p))
     840             :                 OK = false;
     841           0 :             yy_switch_to_buffer(lex_buffer);
     842           0 :             pfree(opt_name);
     843           0 :             pfree(opt_value);
     844           0 :         }
     845             :         else if (guc_name_compare(opt_name, "include") == 0)
     846       61016 :         {
     847             :             /*
     848             :              * An include directive isn't a variable and should be processed
     849             :              * immediately.
     850             :              */
     851             :             if (!ParseConfigFile(opt_value, true,
     852          96 :                                  config_file, ConfigFileLineno - 1,
     853          48 :                                  depth + 1, elevel,
     854             :                                  head_p, tail_p))
     855             :                 OK = false;
     856           0 :             yy_switch_to_buffer(lex_buffer);
     857          48 :             pfree(opt_name);
     858          48 :             pfree(opt_value);
     859          48 :         }
     860             :         else
     861             :         {
     862             :             /* ordinary variable, append to list */
     863             :             item = palloc(sizeof *item);
     864       60968 :             item->name = opt_name;
     865       60968 :             item->value = opt_value;
     866       60968 :             item->errmsg = NULL;
     867       60968 :             item->filename = pstrdup(config_file);
     868       60968 :             item->sourceline = ConfigFileLineno - 1;
     869       60968 :             item->ignore = false;
     870       60968 :             item->applied = false;
     871       60968 :             item->next = NULL;
     872       60968 :             if (*head_p == NULL)
     873       60968 :                 *head_p = item;
     874        3858 :             else
     875             :                 (*tail_p)->next = item;
     876       57110 :             *tail_p = item;
     877       60968 :         }
     878             : 
     879             :         /* break out of loop if read EOF, else loop for next line */
     880             :         if (token == 0)
     881       61016 :             break;
     882          48 :         continue;
     883       60968 : 
     884             : parse_error:
     885             :         /* release storage if we allocated any on this line */
     886             :         if (opt_name)
     887           0 :             pfree(opt_name);
     888           0 :         if (opt_value)
     889           0 :             pfree(opt_value);
     890           0 : 
     891             :         /* report the error */
     892             :         if (token == GUC_EOL || token == 0)
     893           0 :         {
     894             :             ereport(elevel,
     895           0 :                     (errcode(ERRCODE_SYNTAX_ERROR),
     896             :               errmsg("syntax error in file \"%s\" line %u, near end of line",
     897             :                      config_file, ConfigFileLineno - 1)));
     898             :             record_config_file_error("syntax error",
     899           0 :                                      config_file, ConfigFileLineno - 1,
     900           0 :                                      head_p, tail_p);
     901             :         }
     902             :         else
     903             :         {
     904             :             ereport(elevel,
     905           0 :                     (errcode(ERRCODE_SYNTAX_ERROR),
     906             :              errmsg("syntax error in file \"%s\" line %u, near token \"%s\"",
     907             :                     config_file, ConfigFileLineno, yytext)));
     908             :             record_config_file_error("syntax error",
     909           0 :                                      config_file, ConfigFileLineno,
     910             :                                      head_p, tail_p);
     911             :         }
     912             :         OK = false;
     913           0 :         errorcount++;
     914           0 : 
     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             :         if (errorcount >= 100 || elevel <= DEBUG1)
     923           0 :         {
     924             :             ereport(elevel,
     925           0 :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     926             :                errmsg("too many syntax errors found, abandoning file \"%s\"",
     927             :                       config_file)));
     928             :             break;
     929           0 :         }
     930             : 
     931             :         /* resync to next end-of-line or EOF */
     932             :         while (token != GUC_EOL && token != 0)
     933           0 :             token = yylex();
     934           0 :         /* break out of loop on EOF */
     935             :         if (token == 0)
     936           0 :             break;
     937           0 :     }
     938             : 
     939             : cleanup:
     940             :     yy_delete_buffer(lex_buffer);
     941        6606 :     /* Each recursion level must save and restore these static variables. */
     942             :     ConfigFileLineno = save_ConfigFileLineno;
     943        6606 :     GUC_flex_fatal_jmp = save_GUC_flex_fatal_jmp;
     944        6606 :     return OK;
     945        6606 : }
     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             : ParseConfigDirectory(const char *includedir,
     959           0 :                      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             :     if (strspn(includedir, " \t\r\n") == strlen(includedir))
     978           0 :     {
     979             :         ereport(elevel,
     980           0 :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     981             :                  errmsg("empty configuration directory name: \"%s\"",
     982             :                         includedir)));
     983             :         record_config_file_error("empty configuration directory name",
     984           0 :                                  calling_file, calling_lineno,
     985             :                                  head_p, tail_p);
     986             :         return false;
     987           0 :     }
     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             :     directory = AbsoluteConfigLocation(includedir, calling_file);
     995           0 :     d = AllocateDir(directory);
     996           0 :     if (d == NULL)
     997           0 :     {
     998             :         ereport(elevel,
     999           0 :                 (errcode_for_file_access(),
    1000             :                  errmsg("could not open configuration directory \"%s\": %m",
    1001             :                         directory)));
    1002             :         record_config_file_error(psprintf("could not open directory \"%s\"",
    1003           0 :                                           directory),
    1004             :                                  calling_file, calling_lineno,
    1005             :                                  head_p, tail_p);
    1006             :         status = false;
    1007           0 :         goto cleanup;
    1008           0 :     }
    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             :     size_filenames = 32;
    1015           0 :     filenames = (char **) palloc(size_filenames * sizeof(char *));
    1016           0 :     num_filenames = 0;
    1017           0 : 
    1018             :     while ((de = ReadDir(d, directory)) != NULL)
    1019           0 :     {
    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             :         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           0 : 
    1035             :         join_path_components(filename, directory, de->d_name);
    1036           0 :         canonicalize_path(filename);
    1037           0 :         if (stat(filename, &st) == 0)
    1038           0 :         {
    1039             :             if (!S_ISDIR(st.st_mode))
    1040           0 :             {
    1041             :                 /* Add file to array, increasing its size in blocks of 32 */
    1042             :                 if (num_filenames >= size_filenames)
    1043           0 :                 {
    1044             :                     size_filenames += 32;
    1045           0 :                     filenames = (char **) repalloc(filenames,
    1046           0 :                                             size_filenames * sizeof(char *));
    1047             :                 }
    1048             :                 filenames[num_filenames] = pstrdup(filename);
    1049           0 :                 num_filenames++;
    1050           0 :             }
    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             :             ereport(elevel,
    1060           0 :                     (errcode_for_file_access(),
    1061             :                      errmsg("could not stat file \"%s\": %m",
    1062             :                             filename)));
    1063             :             record_config_file_error(psprintf("could not stat file \"%s\"",
    1064           0 :                                               filename),
    1065             :                                      calling_file, calling_lineno,
    1066             :                                      head_p, tail_p);
    1067             :             status = false;
    1068           0 :             goto cleanup;
    1069           0 :         }
    1070             :     }
    1071             : 
    1072             :     if (num_filenames > 0)
    1073           0 :     {
    1074             :         int         i;
    1075             : 
    1076             :         qsort(filenames, num_filenames, sizeof(char *), pg_qsort_strcmp);
    1077           0 :         for (i = 0; i < num_filenames; i++)
    1078           0 :         {
    1079             :             if (!ParseConfigFile(filenames[i], true,
    1080           0 :                                  calling_file, calling_lineno,
    1081             :                                  depth, elevel,
    1082             :                                  head_p, tail_p))
    1083             :             {
    1084             :                 status = false;
    1085           0 :                 goto cleanup;
    1086           0 :             }
    1087             :         }
    1088             :     }
    1089             :     status = true;
    1090           0 : 
    1091             : cleanup:
    1092             :     if (d)
    1093           0 :         FreeDir(d);
    1094           0 :     pfree(directory);
    1095           0 :     return status;
    1096           0 : }
    1097             : 
    1098             : /*
    1099             :  * Free a list of ConfigVariables, including the names and the values
    1100             :  */
    1101             : void
    1102             : FreeConfigVariables(ConfigVariable *list)
    1103        1232 : {
    1104             :     ConfigVariable *item;
    1105             : 
    1106             :     item = list;
    1107        1232 :     while (item)
    1108        8250 :     {
    1109             :         ConfigVariable *next = item->next;
    1110        5786 : 
    1111             :         FreeConfigVariable(item);
    1112        5786 :         item = next;
    1113        5786 :     }
    1114             : }
    1115        1232 : 
    1116             : /*
    1117             :  * Free a single ConfigVariable
    1118             :  */
    1119             : static void
    1120             : FreeConfigVariable(ConfigVariable *item)
    1121        5786 : {
    1122             :     if (item->name)
    1123        5786 :         pfree(item->name);
    1124        5786 :     if (item->value)
    1125        5786 :         pfree(item->value);
    1126        5786 :     if (item->errmsg)
    1127        5786 :         pfree(item->errmsg);
    1128           0 :     if (item->filename)
    1129        5786 :         pfree(item->filename);
    1130        5786 :     pfree(item);
    1131        5786 : }
    1132        5786 : 
    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             : GUC_scanstr(const char *s)
    1145       31490 : {
    1146             :     char       *newStr;
    1147             :     int         len,
    1148             :                 i,
    1149             :                 j;
    1150             : 
    1151             :     Assert(s != NULL && s[0] == '\'');
    1152             :     len = strlen(s);
    1153       31490 :     Assert(len >= 2);
    1154             :     Assert(s[len - 1] == '\'');
    1155             : 
    1156             :     /* Skip the leading quote; we'll handle the trailing quote below */
    1157             :     s++, len--;
    1158       31490 : 
    1159             :     /* Since len still includes trailing quote, this is enough space */
    1160             :     newStr = palloc(len);
    1161       31490 : 
    1162             :     for (i = 0, j = 0; i < len; i++)
    1163      401404 :     {
    1164             :         if (s[i] == '\\')
    1165      369914 :         {
    1166             :             i++;
    1167           0 :             switch (s[i])
    1168           0 :             {
    1169             :                 case 'b':
    1170             :                     newStr[j] = '\b';
    1171           0 :                     break;
    1172           0 :                 case 'f':
    1173             :                     newStr[j] = '\f';
    1174           0 :                     break;
    1175           0 :                 case 'n':
    1176             :                     newStr[j] = '\n';
    1177           0 :                     break;
    1178           0 :                 case 'r':
    1179             :                     newStr[j] = '\r';
    1180           0 :                     break;
    1181           0 :                 case 't':
    1182             :                     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             :                         long        octVal = 0;
    1195           0 : 
    1196             :                         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           0 :                     }
    1203             :                     break;
    1204           0 :                 default:
    1205             :                     newStr[j] = s[i];
    1206           0 :                     break;
    1207           0 :             }                   /* switch */
    1208             :         }
    1209             :         else if (s[i] == '\'' && s[i + 1] == '\'')
    1210      369914 :         {
    1211             :             /* doubled quote becomes just one quote */
    1212             :             newStr[j] = s[++i];
    1213           0 :         }
    1214             :         else
    1215             :             newStr[j] = s[i];
    1216      369914 :         j++;
    1217      369914 :     }
    1218             : 
    1219             :     /* We copied the ending quote to newStr, so replace with \0 */
    1220             :     Assert(j > 0 && j <= len);
    1221             :     newStr[--j] = '\0';
    1222       31490 : 
    1223             :     return newStr;
    1224       31490 : }

Generated by: LCOV version 1.13