LCOV - code coverage report
Current view: top level - src/port - path.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 82.8 % 291 241
Test Date: 2026-05-25 09:16:16 Functions: 93.9 % 33 31
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * path.c
       4              :  *    portable path handling routines
       5              :  *
       6              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7              :  * Portions Copyright (c) 1994, Regents of the University of California
       8              :  *
       9              :  *
      10              :  * IDENTIFICATION
      11              :  *    src/port/path.c
      12              :  *
      13              :  *-------------------------------------------------------------------------
      14              :  */
      15              : 
      16              : #ifndef FRONTEND
      17              : #include "postgres.h"
      18              : #else
      19              : #include "postgres_fe.h"
      20              : #endif
      21              : 
      22              : #include <ctype.h>
      23              : #include <sys/stat.h>
      24              : #ifdef WIN32
      25              : #ifdef _WIN32_IE
      26              : #undef _WIN32_IE
      27              : #endif
      28              : #define _WIN32_IE 0x0500
      29              : #ifdef near
      30              : #undef near
      31              : #endif
      32              : #define near
      33              : #include <shlobj.h>
      34              : #else
      35              : #include <pwd.h>
      36              : #include <unistd.h>
      37              : #endif
      38              : 
      39              : #include "mb/pg_wchar.h"
      40              : #include "pg_config_paths.h"
      41              : 
      42              : 
      43              : #ifndef WIN32
      44              : #define IS_PATH_VAR_SEP(ch) ((ch) == ':')
      45              : #else
      46              : #define IS_PATH_VAR_SEP(ch) ((ch) == ';')
      47              : #endif
      48              : 
      49              : #ifdef WIN32
      50              : static void debackslash_path(char *path, int encoding);
      51              : static int  pg_sjis_mblen(const unsigned char *s);
      52              : #endif
      53              : static void make_relative_path(char *ret_path, const char *target_path,
      54              :                                const char *bin_path, const char *my_exec_path);
      55              : static char *trim_directory(char *path);
      56              : static void trim_trailing_separator(char *path);
      57              : static char *append_subdir_to_path(char *path, char *subdir);
      58              : 
      59              : 
      60              : /*
      61              :  * skip_drive
      62              :  *
      63              :  * On Windows, a path may begin with "C:" or "//network/".  Advance over
      64              :  * this and point to the effective start of the path.
      65              :  */
      66              : #ifdef WIN32
      67              : 
      68              : static char *
      69              : skip_drive(const char *path)
      70              : {
      71              :     if (IS_DIR_SEP(path[0]) && IS_DIR_SEP(path[1]))
      72              :     {
      73              :         path += 2;
      74              :         while (*path && !IS_DIR_SEP(*path))
      75              :             path++;
      76              :     }
      77              :     else if (isalpha((unsigned char) path[0]) && path[1] == ':')
      78              :     {
      79              :         path += 2;
      80              :     }
      81              :     return (char *) path;
      82              : }
      83              : #else
      84              : 
      85              : #define skip_drive(path)    (path)
      86              : #endif
      87              : 
      88              : /*
      89              :  *  has_drive_prefix
      90              :  *
      91              :  * Return true if the given pathname has a drive prefix.
      92              :  */
      93              : bool
      94            0 : has_drive_prefix(const char *path)
      95              : {
      96              : #ifdef WIN32
      97              :     return skip_drive(path) != path;
      98              : #else
      99            0 :     return false;
     100              : #endif
     101              : }
     102              : 
     103              : /*
     104              :  *  first_dir_separator
     105              :  *
     106              :  * Find the location of the first directory separator, return
     107              :  * NULL if not found.
     108              :  */
     109              : char *
     110        63149 : first_dir_separator(const char *filename)
     111              : {
     112              :     const char *p;
     113              : 
     114       448188 :     for (p = skip_drive(filename); *p; p++)
     115       401070 :         if (IS_DIR_SEP(*p))
     116        16031 :             return unconstify(char *, p);
     117        47118 :     return NULL;
     118              : }
     119              : 
     120              : /*
     121              :  *  first_path_var_separator
     122              :  *
     123              :  * Find the location of the first path separator (i.e. ':' on
     124              :  * Unix, ';' on Windows), return NULL if not found.
     125              :  */
     126              : char *
     127        27608 : first_path_var_separator(const char *pathlist)
     128              : {
     129              :     const char *p;
     130              : 
     131              :     /* skip_drive is not needed */
     132       498249 :     for (p = pathlist; *p; p++)
     133       476073 :         if (IS_PATH_VAR_SEP(*p))
     134         5432 :             return unconstify(char *, p);
     135        22176 :     return NULL;
     136              : }
     137              : 
     138              : /*
     139              :  *  last_dir_separator
     140              :  *
     141              :  * Find the location of the last directory separator, return
     142              :  * NULL if not found.
     143              :  */
     144              : char *
     145       191390 : last_dir_separator(const char *filename)
     146              : {
     147              :     const char *p,
     148       191390 :                *ret = NULL;
     149              : 
     150      4054902 :     for (p = skip_drive(filename); *p; p++)
     151      3863512 :         if (IS_DIR_SEP(*p))
     152       574320 :             ret = p;
     153       191390 :     return unconstify(char *, ret);
     154              : }
     155              : 
     156              : 
     157              : #ifdef WIN32
     158              : 
     159              : /*
     160              :  * Convert '\' to '/' within the given path, assuming the path
     161              :  * is in the specified encoding.
     162              :  */
     163              : static void
     164              : debackslash_path(char *path, int encoding)
     165              : {
     166              :     char       *p;
     167              : 
     168              :     /*
     169              :      * Of the supported encodings, only Shift-JIS has multibyte characters
     170              :      * that can include a byte equal to '\' (0x5C).  So rather than implement
     171              :      * a fully encoding-aware conversion, we special-case SJIS.  (Invoking the
     172              :      * general encoding-aware logic in wchar.c is impractical here for
     173              :      * assorted reasons.)
     174              :      */
     175              :     if (encoding == PG_SJIS)
     176              :     {
     177              :         for (p = path; *p; p += pg_sjis_mblen((const unsigned char *) p))
     178              :         {
     179              :             if (*p == '\\')
     180              :                 *p = '/';
     181              :         }
     182              :     }
     183              :     else
     184              :     {
     185              :         for (p = path; *p; p++)
     186              :         {
     187              :             if (*p == '\\')
     188              :                 *p = '/';
     189              :         }
     190              :     }
     191              : }
     192              : 
     193              : /*
     194              :  * SJIS character length
     195              :  *
     196              :  * This must match the behavior of
     197              :  *      pg_encoding_mblen_bounded(PG_SJIS, s)
     198              :  * In particular, unlike the version of pg_sjis_mblen in src/common/wchar.c,
     199              :  * do not allow caller to accidentally step past end-of-string.
     200              :  */
     201              : static int
     202              : pg_sjis_mblen(const unsigned char *s)
     203              : {
     204              :     int         len;
     205              : 
     206              :     if (*s >= 0xa1 && *s <= 0xdf)
     207              :         len = 1;                /* 1 byte kana? */
     208              :     else if (IS_HIGHBIT_SET(*s) && s[1] != '\0')
     209              :         len = 2;                /* kanji? */
     210              :     else
     211              :         len = 1;                /* should be ASCII */
     212              :     return len;
     213              : }
     214              : 
     215              : #endif                          /* WIN32 */
     216              : 
     217              : 
     218              : /*
     219              :  *  make_native_path - on WIN32, change '/' to '\' in the path
     220              :  *
     221              :  *  This reverses the '\'-to-'/' transformation of debackslash_path.
     222              :  *  We need not worry about encodings here, since '/' does not appear
     223              :  *  as a byte of a multibyte character in any supported encoding.
     224              :  *
     225              :  *  This is required because WIN32 COPY is an internal CMD.EXE
     226              :  *  command and doesn't process forward slashes in the same way
     227              :  *  as external commands.  Quoting the first argument to COPY
     228              :  *  does not convert forward to backward slashes, but COPY does
     229              :  *  properly process quoted forward slashes in the second argument.
     230              :  *
     231              :  *  COPY works with quoted forward slashes in the first argument
     232              :  *  only if the current directory is the same as the directory
     233              :  *  of the first argument.
     234              :  */
     235              : void
     236          958 : make_native_path(char *filename)
     237              : {
     238              : #ifdef WIN32
     239              :     char       *p;
     240              : 
     241              :     for (p = filename; *p; p++)
     242              :         if (*p == '/')
     243              :             *p = '\\';
     244              : #endif
     245          958 : }
     246              : 
     247              : 
     248              : /*
     249              :  * This function cleans up the paths for use with either cmd.exe or Msys
     250              :  * on Windows. We need them to use filenames without spaces, for which a
     251              :  * short filename is the safest equivalent, eg:
     252              :  *      C:/Progra~1/
     253              :  *
     254              :  * Presently, this is only used on paths that we can assume are in a
     255              :  * server-safe encoding, so there's no need for an encoding-aware variant.
     256              :  */
     257              : void
     258         7215 : cleanup_path(char *path)
     259              : {
     260              : #ifdef WIN32
     261              :     /*
     262              :      * GetShortPathName() will fail if the path does not exist, or short names
     263              :      * are disabled on this file system.  In both cases, we just return the
     264              :      * original path.  This is particularly useful for --sysconfdir, which
     265              :      * might not exist.
     266              :      */
     267              :     GetShortPathName(path, path, MAXPGPATH - 1);
     268              : 
     269              :     /* Replace '\' with '/' */
     270              :     /* All server-safe encodings are alike here, so just use PG_SQL_ASCII */
     271              :     debackslash_path(path, PG_SQL_ASCII);
     272              : #endif
     273         7215 : }
     274              : 
     275              : 
     276              : /*
     277              :  * join_path_components - join two path components, inserting a slash
     278              :  *
     279              :  * We omit the slash if either given component is empty.
     280              :  *
     281              :  * ret_path is the output area (must be of size MAXPGPATH)
     282              :  *
     283              :  * ret_path can be the same as head, but not the same as tail.
     284              :  */
     285              : void
     286        58926 : join_path_components(char *ret_path,
     287              :                      const char *head, const char *tail)
     288              : {
     289        58926 :     if (ret_path != head)
     290         2129 :         strlcpy(ret_path, head, MAXPGPATH);
     291              : 
     292              :     /*
     293              :      * We used to try to simplify some cases involving "." and "..", but now
     294              :      * we just leave that to be done by canonicalize_path() later.
     295              :      */
     296              : 
     297        58926 :     if (*tail)
     298              :     {
     299              :         /* only separate with slash if head wasn't empty */
     300        58926 :         snprintf(ret_path + strlen(ret_path), MAXPGPATH - strlen(ret_path),
     301              :                  "%s%s",
     302        58926 :                  (*(skip_drive(head)) != '\0') ? "/" : "",
     303              :                  tail);
     304              :     }
     305        58926 : }
     306              : 
     307              : 
     308              : /* State-machine states for canonicalize_path */
     309              : typedef enum
     310              : {
     311              :     ABSOLUTE_PATH_INIT,         /* Just past the leading '/' (and Windows
     312              :                                  * drive name if any) of an absolute path */
     313              :     ABSOLUTE_WITH_N_DEPTH,      /* We collected 'pathdepth' directories in an
     314              :                                  * absolute path */
     315              :     RELATIVE_PATH_INIT,         /* At start of a relative path */
     316              :     RELATIVE_WITH_N_DEPTH,      /* We collected 'pathdepth' directories in a
     317              :                                  * relative path */
     318              :     RELATIVE_WITH_PARENT_REF,   /* Relative path containing only double-dots */
     319              : } canonicalize_state;
     320              : 
     321              : /*
     322              :  * canonicalize_path()
     323              :  *
     324              :  *  Clean up path by:
     325              :  *      o  make Win32 path use Unix slashes
     326              :  *      o  remove trailing quote on Win32
     327              :  *      o  remove trailing slash
     328              :  *      o  remove duplicate (adjacent) separators
     329              :  *      o  remove '.' (unless path reduces to only '.')
     330              :  *      o  process '..' ourselves, removing it if possible
     331              :  *  Modifies path in-place.
     332              :  *
     333              :  * This comes in two variants: encoding-aware and not.  The non-aware version
     334              :  * is only safe to use on strings that are in a server-safe encoding.
     335              :  */
     336              : void
     337       538384 : canonicalize_path(char *path)
     338              : {
     339              :     /* All server-safe encodings are alike here, so just use PG_SQL_ASCII */
     340       538384 :     canonicalize_path_enc(path, PG_SQL_ASCII);
     341       538384 : }
     342              : 
     343              : void
     344       538453 : canonicalize_path_enc(char *path, int encoding)
     345              : {
     346              :     char       *p,
     347              :                *to_p;
     348              :     char       *spath;
     349              :     char       *parsed;
     350              :     char       *unparse;
     351       538453 :     bool        was_sep = false;
     352              :     canonicalize_state state;
     353       538453 :     int         pathdepth = 0;  /* counts collected regular directory names */
     354              : 
     355              : #ifdef WIN32
     356              : 
     357              :     /*
     358              :      * The Windows command processor will accept suitably quoted paths with
     359              :      * forward slashes, but barfs badly with mixed forward and back slashes.
     360              :      * Hence, start by converting all back slashes to forward slashes.
     361              :      */
     362              :     debackslash_path(path, encoding);
     363              : 
     364              :     /*
     365              :      * In Win32, if you do: prog.exe "a b" "\c\d\" the system will pass \c\d"
     366              :      * as argv[2], so trim off trailing quote.
     367              :      */
     368              :     p = path + strlen(path);
     369              :     if (p > path && *(p - 1) == '"')
     370              :         *(p - 1) = '/';
     371              : #endif
     372              : 
     373              :     /*
     374              :      * Removing the trailing slash on a path means we never get ugly double
     375              :      * trailing slashes. Also, Win32 can't stat() a directory with a trailing
     376              :      * slash. Don't remove a leading slash, though.
     377              :      */
     378       538453 :     trim_trailing_separator(path);
     379              : 
     380              :     /*
     381              :      * Remove duplicate adjacent separators
     382              :      */
     383       538453 :     p = path;
     384              : #ifdef WIN32
     385              :     /* Don't remove leading double-slash on Win32 */
     386              :     if (*p)
     387              :         p++;
     388              : #endif
     389       538453 :     to_p = p;
     390     14532291 :     for (; *p; p++, to_p++)
     391              :     {
     392              :         /* Handle many adjacent slashes, like "/a///b" */
     393     13993838 :         while (*p == '/' && was_sep)
     394            0 :             p++;
     395     13993838 :         if (to_p != p)
     396            0 :             *to_p = *p;
     397     13993838 :         was_sep = (*p == '/');
     398              :     }
     399       538453 :     *to_p = '\0';
     400              : 
     401              :     /*
     402              :      * Remove any uses of "." and process ".." ourselves
     403              :      *
     404              :      * Note that "/../.." should reduce to just "/", while "../.." has to be
     405              :      * kept as-is.  Also note that we want a Windows drive spec to be visible
     406              :      * to trim_directory(), but it's not part of the logic that's looking at
     407              :      * the name components; hence distinction between path and spath.
     408              :      *
     409              :      * This loop overwrites the path in-place.  This is safe since we'll never
     410              :      * make the path longer.  "unparse" points to where we are reading the
     411              :      * path, "parse" to where we are writing.
     412              :      */
     413       538453 :     spath = skip_drive(path);
     414       538453 :     if (*spath == '\0')
     415           47 :         return;                 /* empty path is returned as-is */
     416              : 
     417       538406 :     if (*spath == '/')
     418              :     {
     419       144584 :         state = ABSOLUTE_PATH_INIT;
     420              :         /* Skip the leading slash for absolute path */
     421       144584 :         parsed = unparse = (spath + 1);
     422              :     }
     423              :     else
     424              :     {
     425       393822 :         state = RELATIVE_PATH_INIT;
     426       393822 :         parsed = unparse = spath;
     427              :     }
     428              : 
     429      3037346 :     while (*unparse != '\0')
     430              :     {
     431              :         char       *unparse_next;
     432              :         bool        is_double_dot;
     433              : 
     434              :         /* Split off this dir name, and set unparse_next to the next one */
     435      2498940 :         unparse_next = unparse;
     436     14387655 :         while (*unparse_next && *unparse_next != '/')
     437     11888715 :             unparse_next++;
     438      2498940 :         if (*unparse_next != '\0')
     439      1960539 :             *unparse_next++ = '\0';
     440              : 
     441              :         /* Identify type of this dir name */
     442      2498940 :         if (strcmp(unparse, ".") == 0)
     443              :         {
     444              :             /* We can ignore "." components in all cases */
     445        38801 :             unparse = unparse_next;
     446        38801 :             continue;
     447              :         }
     448              : 
     449      2460139 :         if (strcmp(unparse, "..") == 0)
     450          182 :             is_double_dot = true;
     451              :         else
     452              :         {
     453              :             /* adjacent separators were eliminated above */
     454              :             Assert(*unparse != '\0');
     455      2459957 :             is_double_dot = false;
     456              :         }
     457              : 
     458      2460139 :         switch (state)
     459              :         {
     460       144624 :             case ABSOLUTE_PATH_INIT:
     461              :                 /* We can ignore ".." immediately after / */
     462       144624 :                 if (!is_double_dot)
     463              :                 {
     464              :                     /* Append first dir name (we already have leading slash) */
     465       144594 :                     parsed = append_subdir_to_path(parsed, unparse);
     466       144594 :                     state = ABSOLUTE_WITH_N_DEPTH;
     467       144594 :                     pathdepth++;
     468              :                 }
     469       144624 :                 break;
     470      1180241 :             case ABSOLUTE_WITH_N_DEPTH:
     471      1180241 :                 if (is_double_dot)
     472              :                 {
     473              :                     /* Remove last parsed dir */
     474              :                     /* (trim_directory won't remove the leading slash) */
     475           72 :                     *parsed = '\0';
     476           72 :                     parsed = trim_directory(path);
     477           72 :                     if (--pathdepth == 0)
     478           25 :                         state = ABSOLUTE_PATH_INIT;
     479              :                 }
     480              :                 else
     481              :                 {
     482              :                     /* Append normal dir */
     483      1180169 :                     *parsed++ = '/';
     484      1180169 :                     parsed = append_subdir_to_path(parsed, unparse);
     485      1180169 :                     pathdepth++;
     486              :                 }
     487      1180241 :                 break;
     488       393780 :             case RELATIVE_PATH_INIT:
     489       393780 :                 if (is_double_dot)
     490              :                 {
     491              :                     /* Append irreducible double-dot (..) */
     492           30 :                     parsed = append_subdir_to_path(parsed, unparse);
     493           30 :                     state = RELATIVE_WITH_PARENT_REF;
     494              :                 }
     495              :                 else
     496              :                 {
     497              :                     /* Append normal dir */
     498       393750 :                     parsed = append_subdir_to_path(parsed, unparse);
     499       393750 :                     state = RELATIVE_WITH_N_DEPTH;
     500       393750 :                     pathdepth++;
     501              :                 }
     502       393780 :                 break;
     503       741454 :             case RELATIVE_WITH_N_DEPTH:
     504       741454 :                 if (is_double_dot)
     505              :                 {
     506              :                     /* Remove last parsed dir */
     507           45 :                     *parsed = '\0';
     508           45 :                     parsed = trim_directory(path);
     509           45 :                     if (--pathdepth == 0)
     510              :                     {
     511              :                         /*
     512              :                          * If the output path is now empty, we're back to the
     513              :                          * INIT state.  However, we could have processed a
     514              :                          * path like "../dir/.." and now be down to "..", in
     515              :                          * which case enter the correct state for that.
     516              :                          */
     517           35 :                         if (parsed == spath)
     518           20 :                             state = RELATIVE_PATH_INIT;
     519              :                         else
     520           15 :                             state = RELATIVE_WITH_PARENT_REF;
     521              :                     }
     522              :                 }
     523              :                 else
     524              :                 {
     525              :                     /* Append normal dir */
     526       741409 :                     *parsed++ = '/';
     527       741409 :                     parsed = append_subdir_to_path(parsed, unparse);
     528       741409 :                     pathdepth++;
     529              :                 }
     530       741454 :                 break;
     531           40 :             case RELATIVE_WITH_PARENT_REF:
     532           40 :                 if (is_double_dot)
     533              :                 {
     534              :                     /* Append next irreducible double-dot (..) */
     535            5 :                     *parsed++ = '/';
     536            5 :                     parsed = append_subdir_to_path(parsed, unparse);
     537              :                 }
     538              :                 else
     539              :                 {
     540              :                     /* Append normal dir */
     541           35 :                     *parsed++ = '/';
     542           35 :                     parsed = append_subdir_to_path(parsed, unparse);
     543              : 
     544              :                     /*
     545              :                      * We can now start counting normal dirs.  But if later
     546              :                      * double-dots make us remove this dir again, we'd better
     547              :                      * revert to RELATIVE_WITH_PARENT_REF not INIT state.
     548              :                      */
     549           35 :                     state = RELATIVE_WITH_N_DEPTH;
     550           35 :                     pathdepth = 1;
     551              :                 }
     552           40 :                 break;
     553              :         }
     554              : 
     555      2460139 :         unparse = unparse_next;
     556              :     }
     557              : 
     558              :     /*
     559              :      * If our output path is empty at this point, insert ".".  We don't want
     560              :      * to do this any earlier because it'd result in an extra dot in corner
     561              :      * cases such as "../dir/..".  Since we rejected the wholly-empty-path
     562              :      * case above, there is certainly room.
     563              :      */
     564       538406 :     if (parsed == spath)
     565           62 :         *parsed++ = '.';
     566              : 
     567              :     /* And finally, ensure the output path is nul-terminated. */
     568       538406 :     *parsed = '\0';
     569              : }
     570              : 
     571              : /*
     572              :  * Detect whether a path contains any parent-directory references ("..")
     573              :  *
     574              :  * The input *must* have been put through canonicalize_path previously.
     575              :  */
     576              : bool
     577       366510 : path_contains_parent_reference(const char *path)
     578              : {
     579              :     /*
     580              :      * Once canonicalized, an absolute path cannot contain any ".." at all,
     581              :      * while a relative path could contain ".."(s) only at the start.  So it
     582              :      * is sufficient to check the start of the path, after skipping any
     583              :      * Windows drive/network specifier.
     584              :      */
     585       366510 :     path = skip_drive(path);    /* C: shouldn't affect our conclusion */
     586              : 
     587       366510 :     if (path[0] == '.' &&
     588           25 :         path[1] == '.' &&
     589            0 :         (path[2] == '\0' || path[2] == '/'))
     590            0 :         return true;
     591              : 
     592       366510 :     return false;
     593              : }
     594              : 
     595              : /*
     596              :  * Detect whether a path is only in or below the current working directory.
     597              :  *
     598              :  * The input *must* have been put through canonicalize_path previously.
     599              :  *
     600              :  * An absolute path that matches the current working directory should
     601              :  * return false (we only want relative to the cwd).
     602              :  */
     603              : bool
     604       366510 : path_is_relative_and_below_cwd(const char *path)
     605              : {
     606       366510 :     if (is_absolute_path(path))
     607            0 :         return false;
     608              :     /* don't allow anything above the cwd */
     609       366510 :     else if (path_contains_parent_reference(path))
     610            0 :         return false;
     611              : #ifdef WIN32
     612              : 
     613              :     /*
     614              :      * On Win32, a drive letter _not_ followed by a slash, e.g. 'E:abc', is
     615              :      * relative to the cwd on that drive, or the drive's root directory if
     616              :      * that drive has no cwd.  Because the path itself cannot tell us which is
     617              :      * the case, we have to assume the worst, i.e. that it is not below the
     618              :      * cwd.  We could use GetFullPathName() to find the full path but that
     619              :      * could change if the current directory for the drive changes underneath
     620              :      * us, so we just disallow it.
     621              :      */
     622              :     else if (isalpha((unsigned char) path[0]) && path[1] == ':' &&
     623              :              !IS_DIR_SEP(path[2]))
     624              :         return false;
     625              : #endif
     626              :     else
     627       366510 :         return true;
     628              : }
     629              : 
     630              : /*
     631              :  * Detect whether a path is safe for use during archive extraction.
     632              :  *
     633              :  * This applies canonicalize_path(), then it checks that the path does
     634              :  * not contain any parent directory references.
     635              :  */
     636              : bool
     637       359339 : path_is_safe_for_extraction(const char *path)
     638              : {
     639              :     char        buf[MAXPGPATH];
     640              : 
     641       359339 :     strlcpy(buf, path, sizeof(buf));
     642       359339 :     canonicalize_path(buf);
     643              : 
     644       359339 :     return path_is_relative_and_below_cwd(buf);
     645              : }
     646              : 
     647              : /*
     648              :  * Detect whether path1 is a prefix of path2 (including equality).
     649              :  *
     650              :  * This is pretty trivial, but it seems better to export a function than
     651              :  * to export IS_DIR_SEP.
     652              :  */
     653              : bool
     654           83 : path_is_prefix_of_path(const char *path1, const char *path2)
     655              : {
     656           83 :     int         path1_len = strlen(path1);
     657              : 
     658           83 :     if (strncmp(path1, path2, path1_len) == 0 &&
     659            0 :         (IS_DIR_SEP(path2[path1_len]) || path2[path1_len] == '\0'))
     660            0 :         return true;
     661           83 :     return false;
     662              : }
     663              : 
     664              : /*
     665              :  * Extracts the actual name of the program as called -
     666              :  * stripped of .exe suffix if any
     667              :  */
     668              : const char *
     669        33976 : get_progname(const char *argv0)
     670              : {
     671              :     const char *nodir_name;
     672              :     char       *progname;
     673              : 
     674        33976 :     nodir_name = last_dir_separator(argv0);
     675        33976 :     if (nodir_name)
     676        26389 :         nodir_name++;
     677              :     else
     678         7587 :         nodir_name = skip_drive(argv0);
     679              : 
     680              :     /*
     681              :      * Make a copy in case argv[0] is modified by ps_status. Leaks memory, but
     682              :      * called only once.
     683              :      */
     684        33976 :     progname = strdup(nodir_name);
     685        33976 :     if (progname == NULL)
     686              :     {
     687            0 :         fprintf(stderr, "%s: out of memory\n", nodir_name);
     688            0 :         abort();                /* This could exit the postmaster */
     689              :     }
     690              : 
     691              : #if defined(__CYGWIN__) || defined(WIN32)
     692              :     /* strip ".exe" suffix, regardless of case */
     693              :     if (strlen(progname) > sizeof(EXE) - 1 &&
     694              :         pg_strcasecmp(progname + strlen(progname) - (sizeof(EXE) - 1), EXE) == 0)
     695              :         progname[strlen(progname) - (sizeof(EXE) - 1)] = '\0';
     696              : #endif
     697              : 
     698        33976 :     return progname;
     699              : }
     700              : 
     701              : 
     702              : /*
     703              :  * dir_strcmp: strcmp except any two DIR_SEP characters are considered equal,
     704              :  * and we honor filesystem case insensitivity if known
     705              :  */
     706              : static int
     707        51115 : dir_strcmp(const char *s1, const char *s2)
     708              : {
     709       204460 :     while (*s1 && *s2)
     710              :     {
     711       153345 :         if (
     712              : #ifndef WIN32
     713       153345 :             *s1 != *s2
     714              : #else
     715              :         /* On windows, paths are case-insensitive */
     716              :             pg_tolower((unsigned char) *s1) != pg_tolower((unsigned char) *s2)
     717              : #endif
     718            0 :             && !(IS_DIR_SEP(*s1) && IS_DIR_SEP(*s2)))
     719            0 :             return (int) *s1 - (int) *s2;
     720       153345 :         s1++, s2++;
     721              :     }
     722        51115 :     if (*s1)
     723            0 :         return 1;               /* s1 longer */
     724        51115 :     if (*s2)
     725            0 :         return -1;              /* s2 longer */
     726        51115 :     return 0;
     727              : }
     728              : 
     729              : 
     730              : /*
     731              :  * make_relative_path - make a path relative to the actual binary location
     732              :  *
     733              :  * This function exists to support relocation of installation trees.
     734              :  *
     735              :  *  ret_path is the output area (must be of size MAXPGPATH)
     736              :  *  target_path is the compiled-in path to the directory we want to find
     737              :  *  bin_path is the compiled-in path to the directory of executables
     738              :  *  my_exec_path is the actual location of my executable
     739              :  *
     740              :  * We determine the common prefix of target_path and bin_path, then compare
     741              :  * the remainder of bin_path to the last directory component(s) of
     742              :  * my_exec_path.  If they match, build the result as the part of my_exec_path
     743              :  * preceding the match, joined to the remainder of target_path.  If no match,
     744              :  * return target_path as-is.
     745              :  *
     746              :  * For example:
     747              :  *      target_path  = '/usr/local/share/postgresql'
     748              :  *      bin_path     = '/usr/local/bin'
     749              :  *      my_exec_path = '/opt/pgsql/bin/postgres'
     750              :  * Given these inputs, the common prefix is '/usr/local/', the tail of
     751              :  * bin_path is 'bin' which does match the last directory component of
     752              :  * my_exec_path, so we would return '/opt/pgsql/share/postgresql'
     753              :  */
     754              : static void
     755        52277 : make_relative_path(char *ret_path, const char *target_path,
     756              :                    const char *bin_path, const char *my_exec_path)
     757              : {
     758              :     int         prefix_len;
     759              :     int         tail_start;
     760              :     int         tail_len;
     761              :     int         i;
     762              : 
     763              :     /*
     764              :      * Determine the common prefix --- note we require it to end on a
     765              :      * directory separator, consider eg '/usr/lib' and '/usr/libexec'.
     766              :      */
     767        52277 :     prefix_len = 0;
     768       940986 :     for (i = 0; target_path[i] && bin_path[i]; i++)
     769              :     {
     770       940986 :         if (IS_DIR_SEP(target_path[i]) && IS_DIR_SEP(bin_path[i]))
     771       209108 :             prefix_len = i + 1;
     772       731878 :         else if (target_path[i] != bin_path[i])
     773        52277 :             break;
     774              :     }
     775        52277 :     if (prefix_len == 0)
     776            0 :         goto no_match;          /* no common prefix? */
     777        52277 :     tail_len = strlen(bin_path) - prefix_len;
     778              : 
     779              :     /*
     780              :      * Set up my_exec_path without the actual executable name, and
     781              :      * canonicalize to simplify comparison to bin_path.
     782              :      */
     783        52277 :     strlcpy(ret_path, my_exec_path, MAXPGPATH);
     784        52277 :     trim_directory(ret_path);   /* remove my executable name */
     785        52277 :     canonicalize_path(ret_path);
     786              : 
     787              :     /*
     788              :      * Tail match?
     789              :      */
     790        52277 :     tail_start = (int) strlen(ret_path) - tail_len;
     791        52277 :     if (tail_start > 0 &&
     792       103392 :         IS_DIR_SEP(ret_path[tail_start - 1]) &&
     793        51115 :         dir_strcmp(ret_path + tail_start, bin_path + prefix_len) == 0)
     794              :     {
     795        51115 :         ret_path[tail_start] = '\0';
     796        51115 :         trim_trailing_separator(ret_path);
     797        51115 :         join_path_components(ret_path, ret_path, target_path + prefix_len);
     798        51115 :         canonicalize_path(ret_path);
     799        51115 :         return;
     800              :     }
     801              : 
     802         1162 : no_match:
     803         1162 :     strlcpy(ret_path, target_path, MAXPGPATH);
     804         1162 :     canonicalize_path(ret_path);
     805              : }
     806              : 
     807              : 
     808              : /*
     809              :  * make_absolute_path
     810              :  *
     811              :  * If the given pathname isn't already absolute, make it so, interpreting
     812              :  * it relative to the current working directory.
     813              :  *
     814              :  * Also canonicalizes the path.  The result is always a malloc'd copy.
     815              :  *
     816              :  * In backend, failure cases result in ereport(ERROR); in frontend,
     817              :  * we write a complaint on stderr and return NULL.
     818              :  *
     819              :  * Note: interpretation of relative-path arguments during postmaster startup
     820              :  * should happen before doing ChangeToDataDir(), else the user will probably
     821              :  * not like the results.
     822              :  */
     823              : char *
     824         3067 : make_absolute_path(const char *path)
     825              : {
     826              :     char       *new;
     827              : 
     828              :     /* Returning null for null input is convenient for some callers */
     829         3067 :     if (path == NULL)
     830            0 :         return NULL;
     831              : 
     832         3067 :     if (!is_absolute_path(path))
     833              :     {
     834              :         char       *buf;
     835              :         size_t      buflen;
     836              : 
     837          420 :         buflen = MAXPGPATH;
     838              :         for (;;)
     839              :         {
     840            0 :             buf = malloc(buflen);
     841          420 :             if (!buf)
     842              :             {
     843              : #ifndef FRONTEND
     844            0 :                 ereport(ERROR,
     845              :                         (errcode(ERRCODE_OUT_OF_MEMORY),
     846              :                          errmsg("out of memory")));
     847              : #else
     848            0 :                 fprintf(stderr, _("out of memory\n"));
     849            0 :                 return NULL;
     850              : #endif
     851              :             }
     852              : 
     853          420 :             if (getcwd(buf, buflen))
     854          420 :                 break;
     855            0 :             else if (errno == ERANGE)
     856              :             {
     857            0 :                 free(buf);
     858            0 :                 buflen *= 2;
     859            0 :                 continue;
     860              :             }
     861              :             else
     862              :             {
     863            0 :                 int         save_errno = errno;
     864              : 
     865            0 :                 free(buf);
     866            0 :                 errno = save_errno;
     867              : #ifndef FRONTEND
     868            0 :                 elog(ERROR, "could not get current working directory: %m");
     869              : #else
     870            0 :                 fprintf(stderr, _("could not get current working directory: %m\n"));
     871            0 :                 return NULL;
     872              : #endif
     873              :             }
     874              :         }
     875              : 
     876          420 :         new = malloc(strlen(buf) + strlen(path) + 2);
     877          420 :         if (!new)
     878              :         {
     879            0 :             free(buf);
     880              : #ifndef FRONTEND
     881            0 :             ereport(ERROR,
     882              :                     (errcode(ERRCODE_OUT_OF_MEMORY),
     883              :                      errmsg("out of memory")));
     884              : #else
     885            0 :             fprintf(stderr, _("out of memory\n"));
     886            0 :             return NULL;
     887              : #endif
     888              :         }
     889          420 :         sprintf(new, "%s/%s", buf, path);
     890          420 :         free(buf);
     891              :     }
     892              :     else
     893              :     {
     894         2647 :         new = strdup(path);
     895         2647 :         if (!new)
     896              :         {
     897              : #ifndef FRONTEND
     898            0 :             ereport(ERROR,
     899              :                     (errcode(ERRCODE_OUT_OF_MEMORY),
     900              :                      errmsg("out of memory")));
     901              : #else
     902            0 :             fprintf(stderr, _("out of memory\n"));
     903            0 :             return NULL;
     904              : #endif
     905              :         }
     906              :     }
     907              : 
     908              :     /* Make sure punctuation is canonical, too */
     909         3067 :     canonicalize_path(new);
     910              : 
     911         3067 :     return new;
     912              : }
     913              : 
     914              : 
     915              : /*
     916              :  *  get_share_path
     917              :  */
     918              : void
     919        11169 : get_share_path(const char *my_exec_path, char *ret_path)
     920              : {
     921        11169 :     make_relative_path(ret_path, PGSHAREDIR, PGBINDIR, my_exec_path);
     922        11169 : }
     923              : 
     924              : /*
     925              :  *  get_etc_path
     926              :  */
     927              : void
     928        13781 : get_etc_path(const char *my_exec_path, char *ret_path)
     929              : {
     930        13781 :     make_relative_path(ret_path, SYSCONFDIR, PGBINDIR, my_exec_path);
     931        13781 : }
     932              : 
     933              : /*
     934              :  *  get_include_path
     935              :  */
     936              : void
     937          626 : get_include_path(const char *my_exec_path, char *ret_path)
     938              : {
     939          626 :     make_relative_path(ret_path, INCLUDEDIR, PGBINDIR, my_exec_path);
     940          626 : }
     941              : 
     942              : /*
     943              :  *  get_pkginclude_path
     944              :  */
     945              : void
     946          566 : get_pkginclude_path(const char *my_exec_path, char *ret_path)
     947              : {
     948          566 :     make_relative_path(ret_path, PKGINCLUDEDIR, PGBINDIR, my_exec_path);
     949          566 : }
     950              : 
     951              : /*
     952              :  *  get_includeserver_path
     953              :  */
     954              : void
     955          555 : get_includeserver_path(const char *my_exec_path, char *ret_path)
     956              : {
     957          555 :     make_relative_path(ret_path, INCLUDEDIRSERVER, PGBINDIR, my_exec_path);
     958          555 : }
     959              : 
     960              : /*
     961              :  *  get_lib_path
     962              :  */
     963              : void
     964          555 : get_lib_path(const char *my_exec_path, char *ret_path)
     965              : {
     966          555 :     make_relative_path(ret_path, LIBDIR, PGBINDIR, my_exec_path);
     967          555 : }
     968              : 
     969              : /*
     970              :  *  get_pkglib_path
     971              :  */
     972              : void
     973         2402 : get_pkglib_path(const char *my_exec_path, char *ret_path)
     974              : {
     975         2402 :     make_relative_path(ret_path, PKGLIBDIR, PGBINDIR, my_exec_path);
     976         2402 : }
     977              : 
     978              : /*
     979              :  *  get_locale_path
     980              :  */
     981              : void
     982        20958 : get_locale_path(const char *my_exec_path, char *ret_path)
     983              : {
     984        20958 :     make_relative_path(ret_path, LOCALEDIR, PGBINDIR, my_exec_path);
     985        20958 : }
     986              : 
     987              : /*
     988              :  *  get_doc_path
     989              :  */
     990              : void
     991          555 : get_doc_path(const char *my_exec_path, char *ret_path)
     992              : {
     993          555 :     make_relative_path(ret_path, DOCDIR, PGBINDIR, my_exec_path);
     994          555 : }
     995              : 
     996              : /*
     997              :  *  get_html_path
     998              :  */
     999              : void
    1000          555 : get_html_path(const char *my_exec_path, char *ret_path)
    1001              : {
    1002          555 :     make_relative_path(ret_path, HTMLDIR, PGBINDIR, my_exec_path);
    1003          555 : }
    1004              : 
    1005              : /*
    1006              :  *  get_man_path
    1007              :  */
    1008              : void
    1009          555 : get_man_path(const char *my_exec_path, char *ret_path)
    1010              : {
    1011          555 :     make_relative_path(ret_path, MANDIR, PGBINDIR, my_exec_path);
    1012          555 : }
    1013              : 
    1014              : 
    1015              : /*
    1016              :  *  get_home_path
    1017              :  *
    1018              :  * On Unix, this actually returns the user's home directory.  On Windows
    1019              :  * it returns the PostgreSQL-specific application data folder.
    1020              :  */
    1021              : bool
    1022            0 : get_home_path(char *ret_path)
    1023              : {
    1024              : #ifndef WIN32
    1025              :     /*
    1026              :      * We first consult $HOME.  If that's unset, try to get the info from
    1027              :      * <pwd.h>.
    1028              :      */
    1029              :     const char *home;
    1030              : 
    1031            0 :     home = getenv("HOME");
    1032            0 :     if (home && home[0])
    1033              :     {
    1034            0 :         strlcpy(ret_path, home, MAXPGPATH);
    1035            0 :         return true;
    1036              :     }
    1037              :     else
    1038              :     {
    1039              :         struct passwd pwbuf;
    1040              :         struct passwd *pw;
    1041              :         char        buf[1024];
    1042              :         int         rc;
    1043              : 
    1044            0 :         rc = getpwuid_r(geteuid(), &pwbuf, buf, sizeof buf, &pw);
    1045            0 :         if (rc != 0 || !pw)
    1046            0 :             return false;
    1047            0 :         strlcpy(ret_path, pw->pw_dir, MAXPGPATH);
    1048            0 :         return true;
    1049              :     }
    1050              : #else
    1051              :     char       *tmppath;
    1052              : 
    1053              :     /*
    1054              :      * Note: We use getenv() here because the more modern SHGetFolderPath()
    1055              :      * would force the backend to link with shell32.lib, which eats valuable
    1056              :      * desktop heap.  XXX This function is used only in psql, which already
    1057              :      * brings in shell32 via libpq.  Moving this function to its own file
    1058              :      * would keep it out of the backend, freeing it from this concern.
    1059              :      */
    1060              :     tmppath = getenv("APPDATA");
    1061              :     if (!tmppath)
    1062              :         return false;
    1063              :     snprintf(ret_path, MAXPGPATH, "%s/postgresql", tmppath);
    1064              :     return true;
    1065              : #endif
    1066              : }
    1067              : 
    1068              : 
    1069              : /*
    1070              :  * get_parent_directory
    1071              :  *
    1072              :  * Modify the given string in-place to name the parent directory of the
    1073              :  * named file.
    1074              :  *
    1075              :  * If the input is just a file name with no directory part, the result is
    1076              :  * an empty string, not ".".  This is appropriate when the next step is
    1077              :  * join_path_components(), but might need special handling otherwise.
    1078              :  *
    1079              :  * Caution: this will not produce desirable results if the string ends
    1080              :  * with "..".  For most callers this is not a problem since the string
    1081              :  * is already known to name a regular file.  If in doubt, apply
    1082              :  * canonicalize_path() first.
    1083              :  */
    1084              : void
    1085         8711 : get_parent_directory(char *path)
    1086              : {
    1087         8711 :     trim_directory(path);
    1088         8711 : }
    1089              : 
    1090              : 
    1091              : /*
    1092              :  *  trim_directory
    1093              :  *
    1094              :  *  Trim trailing directory from path, that is, remove any trailing slashes,
    1095              :  *  the last pathname component, and the slash just ahead of it --- but never
    1096              :  *  remove a leading slash.
    1097              :  *
    1098              :  * For the convenience of canonicalize_path, the path's new end location
    1099              :  * is returned.
    1100              :  */
    1101              : static char *
    1102        61105 : trim_directory(char *path)
    1103              : {
    1104              :     char       *p;
    1105              : 
    1106        61105 :     path = skip_drive(path);
    1107              : 
    1108        61105 :     if (path[0] == '\0')
    1109            0 :         return path;
    1110              : 
    1111              :     /* back up over trailing slash(es) */
    1112        61105 :     for (p = path + strlen(path) - 1; IS_DIR_SEP(*p) && p > path; p--)
    1113              :         ;
    1114              :     /* back up over directory name */
    1115       594092 :     for (; !IS_DIR_SEP(*p) && p > path; p--)
    1116              :         ;
    1117              :     /* if multiple slashes before directory name, remove 'em all */
    1118        61105 :     for (; p > path && IS_DIR_SEP(*(p - 1)); p--)
    1119              :         ;
    1120              :     /* don't erase a leading slash */
    1121        61105 :     if (p == path && IS_DIR_SEP(*p))
    1122           25 :         p++;
    1123        61105 :     *p = '\0';
    1124        61105 :     return p;
    1125              : }
    1126              : 
    1127              : 
    1128              : /*
    1129              :  *  trim_trailing_separator
    1130              :  *
    1131              :  * trim off trailing slashes, but not a leading slash
    1132              :  */
    1133              : static void
    1134       589568 : trim_trailing_separator(char *path)
    1135              : {
    1136              :     char       *p;
    1137              : 
    1138       589568 :     path = skip_drive(path);
    1139       589568 :     p = path + strlen(path);
    1140       589568 :     if (p > path)
    1141       650455 :         for (p--; p > path && IS_DIR_SEP(*p); p--)
    1142        60934 :             *p = '\0';
    1143       589568 : }
    1144              : 
    1145              : /*
    1146              :  *  append_subdir_to_path
    1147              :  *
    1148              :  * Append the currently-considered subdirectory name to the output
    1149              :  * path in canonicalize_path.  Return the new end location of the
    1150              :  * output path.
    1151              :  *
    1152              :  * Since canonicalize_path updates the path in-place, we must use
    1153              :  * memmove not memcpy, and we don't yet terminate the path with '\0'.
    1154              :  */
    1155              : static char *
    1156      2459992 : append_subdir_to_path(char *path, char *subdir)
    1157              : {
    1158      2459992 :     size_t      len = strlen(subdir);
    1159              : 
    1160              :     /* No need to copy data if path and subdir are the same. */
    1161      2459992 :     if (path != subdir)
    1162        89778 :         memmove(path, subdir, len);
    1163              : 
    1164      2459992 :     return path + len;
    1165              : }
        

Generated by: LCOV version 2.0-1