LCOV - code coverage report
Current view: top level - src/port - path.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 82.6 % 287 237
Test Date: 2026-03-03 14:15:12 Functions: 93.8 % 32 30
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        58698 : first_dir_separator(const char *filename)
     111              : {
     112              :     const char *p;
     113              : 
     114       416780 :     for (p = skip_drive(filename); *p; p++)
     115       373059 :         if (IS_DIR_SEP(*p))
     116        14977 :             return unconstify(char *, p);
     117        43721 :     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        25489 : first_path_var_separator(const char *pathlist)
     128              : {
     129              :     const char *p;
     130              : 
     131              :     /* skip_drive is not needed */
     132       451711 :     for (p = pathlist; *p; p++)
     133       431071 :         if (IS_PATH_VAR_SEP(*p))
     134         4849 :             return unconstify(char *, p);
     135        20640 :     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       179879 : last_dir_separator(const char *filename)
     146              : {
     147              :     const char *p,
     148       179879 :                *ret = NULL;
     149              : 
     150      3819509 :     for (p = skip_drive(filename); *p; p++)
     151      3639630 :         if (IS_DIR_SEP(*p))
     152       539108 :             ret = p;
     153       179879 :     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          931 : 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          931 : }
     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         6669 : 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         6669 : }
     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        53079 : join_path_components(char *ret_path,
     287              :                      const char *head, const char *tail)
     288              : {
     289        53079 :     if (ret_path != head)
     290         1988 :         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        53079 :     if (*tail)
     298              :     {
     299              :         /* only separate with slash if head wasn't empty */
     300        53079 :         snprintf(ret_path + strlen(ret_path), MAXPGPATH - strlen(ret_path),
     301              :                  "%s%s",
     302        53079 :                  (*(skip_drive(head)) != '\0') ? "/" : "",
     303              :                  tail);
     304              :     }
     305        53079 : }
     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       162596 : canonicalize_path(char *path)
     338              : {
     339              :     /* All server-safe encodings are alike here, so just use PG_SQL_ASCII */
     340       162596 :     canonicalize_path_enc(path, PG_SQL_ASCII);
     341       162596 : }
     342              : 
     343              : void
     344       162664 : canonicalize_path_enc(char *path, int encoding)
     345              : {
     346              :     char       *p,
     347              :                *to_p;
     348              :     char       *spath;
     349              :     char       *parsed;
     350              :     char       *unparse;
     351       162664 :     bool        was_sep = false;
     352              :     canonicalize_state state;
     353       162664 :     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       162664 :     trim_trailing_separator(path);
     379              : 
     380              :     /*
     381              :      * Remove duplicate adjacent separators
     382              :      */
     383       162664 :     p = path;
     384              : #ifdef WIN32
     385              :     /* Don't remove leading double-slash on Win32 */
     386              :     if (*p)
     387              :         p++;
     388              : #endif
     389       162664 :     to_p = p;
     390      8698725 :     for (; *p; p++, to_p++)
     391              :     {
     392              :         /* Handle many adjacent slashes, like "/a///b" */
     393      8536061 :         while (*p == '/' && was_sep)
     394            0 :             p++;
     395      8536061 :         if (to_p != p)
     396            0 :             *to_p = *p;
     397      8536061 :         was_sep = (*p == '/');
     398              :     }
     399       162664 :     *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       162664 :     spath = skip_drive(path);
     414       162664 :     if (*spath == '\0')
     415           42 :         return;                 /* empty path is returned as-is */
     416              : 
     417       162622 :     if (*spath == '/')
     418              :     {
     419       131288 :         state = ABSOLUTE_PATH_INIT;
     420              :         /* Skip the leading slash for absolute path */
     421       131288 :         parsed = unparse = (spath + 1);
     422              :     }
     423              :     else
     424              :     {
     425        31334 :         state = RELATIVE_PATH_INIT;
     426        31334 :         parsed = unparse = spath;
     427              :     }
     428              : 
     429      1481035 :     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      1318413 :         unparse_next = unparse;
     436      8567392 :         while (*unparse_next && *unparse_next != '/')
     437      7248979 :             unparse_next++;
     438      1318413 :         if (*unparse_next != '\0')
     439      1155794 :             *unparse_next++ = '\0';
     440              : 
     441              :         /* Identify type of this dir name */
     442      1318413 :         if (strcmp(unparse, ".") == 0)
     443              :         {
     444              :             /* We can ignore "." components in all cases */
     445        27700 :             unparse = unparse_next;
     446        27700 :             continue;
     447              :         }
     448              : 
     449      1290713 :         if (strcmp(unparse, "..") == 0)
     450          122 :             is_double_dot = true;
     451              :         else
     452              :         {
     453              :             /* adjacent separators were eliminated above */
     454              :             Assert(*unparse != '\0');
     455      1290591 :             is_double_dot = false;
     456              :         }
     457              : 
     458      1290713 :         switch (state)
     459              :         {
     460       131312 :             case ABSOLUTE_PATH_INIT:
     461              :                 /* We can ignore ".." immediately after / */
     462       131312 :                 if (!is_double_dot)
     463              :                 {
     464              :                     /* Append first dir name (we already have leading slash) */
     465       131294 :                     parsed = append_subdir_to_path(parsed, unparse);
     466       131294 :                     state = ABSOLUTE_WITH_N_DEPTH;
     467       131294 :                     pathdepth++;
     468              :                 }
     469       131312 :                 break;
     470      1071117 :             case ABSOLUTE_WITH_N_DEPTH:
     471      1071117 :                 if (is_double_dot)
     472              :                 {
     473              :                     /* Remove last parsed dir */
     474              :                     /* (trim_directory won't remove the leading slash) */
     475           56 :                     *parsed = '\0';
     476           56 :                     parsed = trim_directory(path);
     477           56 :                     if (--pathdepth == 0)
     478           15 :                         state = ABSOLUTE_PATH_INIT;
     479              :                 }
     480              :                 else
     481              :                 {
     482              :                     /* Append normal dir */
     483      1071061 :                     *parsed++ = '/';
     484      1071061 :                     parsed = append_subdir_to_path(parsed, unparse);
     485      1071061 :                     pathdepth++;
     486              :                 }
     487      1071117 :                 break;
     488        31316 :             case RELATIVE_PATH_INIT:
     489        31316 :                 if (is_double_dot)
     490              :                 {
     491              :                     /* Append irreducible double-dot (..) */
     492           18 :                     parsed = append_subdir_to_path(parsed, unparse);
     493           18 :                     state = RELATIVE_WITH_PARENT_REF;
     494              :                 }
     495              :                 else
     496              :                 {
     497              :                     /* Append normal dir */
     498        31298 :                     parsed = append_subdir_to_path(parsed, unparse);
     499        31298 :                     state = RELATIVE_WITH_N_DEPTH;
     500        31298 :                     pathdepth++;
     501              :                 }
     502        31316 :                 break;
     503        56944 :             case RELATIVE_WITH_N_DEPTH:
     504        56944 :                 if (is_double_dot)
     505              :                 {
     506              :                     /* Remove last parsed dir */
     507           27 :                     *parsed = '\0';
     508           27 :                     parsed = trim_directory(path);
     509           27 :                     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           21 :                         if (parsed == spath)
     518           12 :                             state = RELATIVE_PATH_INIT;
     519              :                         else
     520            9 :                             state = RELATIVE_WITH_PARENT_REF;
     521              :                     }
     522              :                 }
     523              :                 else
     524              :                 {
     525              :                     /* Append normal dir */
     526        56917 :                     *parsed++ = '/';
     527        56917 :                     parsed = append_subdir_to_path(parsed, unparse);
     528        56917 :                     pathdepth++;
     529              :                 }
     530        56944 :                 break;
     531           24 :             case RELATIVE_WITH_PARENT_REF:
     532           24 :                 if (is_double_dot)
     533              :                 {
     534              :                     /* Append next irreducible double-dot (..) */
     535            3 :                     *parsed++ = '/';
     536            3 :                     parsed = append_subdir_to_path(parsed, unparse);
     537              :                 }
     538              :                 else
     539              :                 {
     540              :                     /* Append normal dir */
     541           21 :                     *parsed++ = '/';
     542           21 :                     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           21 :                     state = RELATIVE_WITH_N_DEPTH;
     550           21 :                     pathdepth = 1;
     551              :                 }
     552           24 :                 break;
     553              :         }
     554              : 
     555      1290713 :         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       162622 :     if (parsed == spath)
     565           30 :         *parsed++ = '.';
     566              : 
     567              :     /* And finally, ensure the output path is nul-terminated. */
     568       162622 :     *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         6835 : 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         6835 :     path = skip_drive(path);    /* C: shouldn't affect our conclusion */
     586              : 
     587         6835 :     if (path[0] == '.' &&
     588            4 :         path[1] == '.' &&
     589            0 :         (path[2] == '\0' || path[2] == '/'))
     590            0 :         return true;
     591              : 
     592         6835 :     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         6835 : path_is_relative_and_below_cwd(const char *path)
     605              : {
     606         6835 :     if (is_absolute_path(path))
     607            0 :         return false;
     608              :     /* don't allow anything above the cwd */
     609         6835 :     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         6835 :         return true;
     628              : }
     629              : 
     630              : /*
     631              :  * Detect whether path1 is a prefix of path2 (including equality).
     632              :  *
     633              :  * This is pretty trivial, but it seems better to export a function than
     634              :  * to export IS_DIR_SEP.
     635              :  */
     636              : bool
     637           77 : path_is_prefix_of_path(const char *path1, const char *path2)
     638              : {
     639           77 :     int         path1_len = strlen(path1);
     640              : 
     641           77 :     if (strncmp(path1, path2, path1_len) == 0 &&
     642            0 :         (IS_DIR_SEP(path2[path1_len]) || path2[path1_len] == '\0'))
     643            0 :         return true;
     644           77 :     return false;
     645              : }
     646              : 
     647              : /*
     648              :  * Extracts the actual name of the program as called -
     649              :  * stripped of .exe suffix if any
     650              :  */
     651              : const char *
     652        31421 : get_progname(const char *argv0)
     653              : {
     654              :     const char *nodir_name;
     655              :     char       *progname;
     656              : 
     657        31421 :     nodir_name = last_dir_separator(argv0);
     658        31421 :     if (nodir_name)
     659        24747 :         nodir_name++;
     660              :     else
     661         6674 :         nodir_name = skip_drive(argv0);
     662              : 
     663              :     /*
     664              :      * Make a copy in case argv[0] is modified by ps_status. Leaks memory, but
     665              :      * called only once.
     666              :      */
     667        31421 :     progname = strdup(nodir_name);
     668        31421 :     if (progname == NULL)
     669              :     {
     670            0 :         fprintf(stderr, "%s: out of memory\n", nodir_name);
     671            0 :         abort();                /* This could exit the postmaster */
     672              :     }
     673              : 
     674              : #if defined(__CYGWIN__) || defined(WIN32)
     675              :     /* strip ".exe" suffix, regardless of case */
     676              :     if (strlen(progname) > sizeof(EXE) - 1 &&
     677              :         pg_strcasecmp(progname + strlen(progname) - (sizeof(EXE) - 1), EXE) == 0)
     678              :         progname[strlen(progname) - (sizeof(EXE) - 1)] = '\0';
     679              : #endif
     680              : 
     681        31421 :     return progname;
     682              : }
     683              : 
     684              : 
     685              : /*
     686              :  * dir_strcmp: strcmp except any two DIR_SEP characters are considered equal,
     687              :  * and we honor filesystem case insensitivity if known
     688              :  */
     689              : static int
     690        46110 : dir_strcmp(const char *s1, const char *s2)
     691              : {
     692       184440 :     while (*s1 && *s2)
     693              :     {
     694       138330 :         if (
     695              : #ifndef WIN32
     696       138330 :             *s1 != *s2
     697              : #else
     698              :         /* On windows, paths are case-insensitive */
     699              :             pg_tolower((unsigned char) *s1) != pg_tolower((unsigned char) *s2)
     700              : #endif
     701            0 :             && !(IS_DIR_SEP(*s1) && IS_DIR_SEP(*s2)))
     702            0 :             return (int) *s1 - (int) *s2;
     703       138330 :         s1++, s2++;
     704              :     }
     705        46110 :     if (*s1)
     706            0 :         return 1;               /* s1 longer */
     707        46110 :     if (*s2)
     708            0 :         return -1;              /* s2 longer */
     709        46110 :     return 0;
     710              : }
     711              : 
     712              : 
     713              : /*
     714              :  * make_relative_path - make a path relative to the actual binary location
     715              :  *
     716              :  * This function exists to support relocation of installation trees.
     717              :  *
     718              :  *  ret_path is the output area (must be of size MAXPGPATH)
     719              :  *  target_path is the compiled-in path to the directory we want to find
     720              :  *  bin_path is the compiled-in path to the directory of executables
     721              :  *  my_exec_path is the actual location of my executable
     722              :  *
     723              :  * We determine the common prefix of target_path and bin_path, then compare
     724              :  * the remainder of bin_path to the last directory component(s) of
     725              :  * my_exec_path.  If they match, build the result as the part of my_exec_path
     726              :  * preceding the match, joined to the remainder of target_path.  If no match,
     727              :  * return target_path as-is.
     728              :  *
     729              :  * For example:
     730              :  *      target_path  = '/usr/local/share/postgresql'
     731              :  *      bin_path     = '/usr/local/bin'
     732              :  *      my_exec_path = '/opt/pgsql/bin/postgres'
     733              :  * Given these inputs, the common prefix is '/usr/local/', the tail of
     734              :  * bin_path is 'bin' which does match the last directory component of
     735              :  * my_exec_path, so we would return '/opt/pgsql/share/postgresql'
     736              :  */
     737              : static void
     738        47196 : make_relative_path(char *ret_path, const char *target_path,
     739              :                    const char *bin_path, const char *my_exec_path)
     740              : {
     741              :     int         prefix_len;
     742              :     int         tail_start;
     743              :     int         tail_len;
     744              :     int         i;
     745              : 
     746              :     /*
     747              :      * Determine the common prefix --- note we require it to end on a
     748              :      * directory separator, consider eg '/usr/lib' and '/usr/libexec'.
     749              :      */
     750        47196 :     prefix_len = 0;
     751       849528 :     for (i = 0; target_path[i] && bin_path[i]; i++)
     752              :     {
     753       849528 :         if (IS_DIR_SEP(target_path[i]) && IS_DIR_SEP(bin_path[i]))
     754       188784 :             prefix_len = i + 1;
     755       660744 :         else if (target_path[i] != bin_path[i])
     756        47196 :             break;
     757              :     }
     758        47196 :     if (prefix_len == 0)
     759            0 :         goto no_match;          /* no common prefix? */
     760        47196 :     tail_len = strlen(bin_path) - prefix_len;
     761              : 
     762              :     /*
     763              :      * Set up my_exec_path without the actual executable name, and
     764              :      * canonicalize to simplify comparison to bin_path.
     765              :      */
     766        47196 :     strlcpy(ret_path, my_exec_path, MAXPGPATH);
     767        47196 :     trim_directory(ret_path);   /* remove my executable name */
     768        47196 :     canonicalize_path(ret_path);
     769              : 
     770              :     /*
     771              :      * Tail match?
     772              :      */
     773        47196 :     tail_start = (int) strlen(ret_path) - tail_len;
     774        47196 :     if (tail_start > 0 &&
     775        93306 :         IS_DIR_SEP(ret_path[tail_start - 1]) &&
     776        46110 :         dir_strcmp(ret_path + tail_start, bin_path + prefix_len) == 0)
     777              :     {
     778        46110 :         ret_path[tail_start] = '\0';
     779        46110 :         trim_trailing_separator(ret_path);
     780        46110 :         join_path_components(ret_path, ret_path, target_path + prefix_len);
     781        46110 :         canonicalize_path(ret_path);
     782        46110 :         return;
     783              :     }
     784              : 
     785         1086 : no_match:
     786         1086 :     strlcpy(ret_path, target_path, MAXPGPATH);
     787         1086 :     canonicalize_path(ret_path);
     788              : }
     789              : 
     790              : 
     791              : /*
     792              :  * make_absolute_path
     793              :  *
     794              :  * If the given pathname isn't already absolute, make it so, interpreting
     795              :  * it relative to the current working directory.
     796              :  *
     797              :  * Also canonicalizes the path.  The result is always a malloc'd copy.
     798              :  *
     799              :  * In backend, failure cases result in ereport(ERROR); in frontend,
     800              :  * we write a complaint on stderr and return NULL.
     801              :  *
     802              :  * Note: interpretation of relative-path arguments during postmaster startup
     803              :  * should happen before doing ChangeToDataDir(), else the user will probably
     804              :  * not like the results.
     805              :  */
     806              : char *
     807         2844 : make_absolute_path(const char *path)
     808              : {
     809              :     char       *new;
     810              : 
     811              :     /* Returning null for null input is convenient for some callers */
     812         2844 :     if (path == NULL)
     813            0 :         return NULL;
     814              : 
     815         2844 :     if (!is_absolute_path(path))
     816              :     {
     817              :         char       *buf;
     818              :         size_t      buflen;
     819              : 
     820          407 :         buflen = MAXPGPATH;
     821              :         for (;;)
     822              :         {
     823            0 :             buf = malloc(buflen);
     824          407 :             if (!buf)
     825              :             {
     826              : #ifndef FRONTEND
     827            0 :                 ereport(ERROR,
     828              :                         (errcode(ERRCODE_OUT_OF_MEMORY),
     829              :                          errmsg("out of memory")));
     830              : #else
     831            0 :                 fprintf(stderr, _("out of memory\n"));
     832            0 :                 return NULL;
     833              : #endif
     834              :             }
     835              : 
     836          407 :             if (getcwd(buf, buflen))
     837          407 :                 break;
     838            0 :             else if (errno == ERANGE)
     839              :             {
     840            0 :                 free(buf);
     841            0 :                 buflen *= 2;
     842            0 :                 continue;
     843              :             }
     844              :             else
     845              :             {
     846            0 :                 int         save_errno = errno;
     847              : 
     848            0 :                 free(buf);
     849            0 :                 errno = save_errno;
     850              : #ifndef FRONTEND
     851            0 :                 elog(ERROR, "could not get current working directory: %m");
     852              : #else
     853            0 :                 fprintf(stderr, _("could not get current working directory: %m\n"));
     854            0 :                 return NULL;
     855              : #endif
     856              :             }
     857              :         }
     858              : 
     859          407 :         new = malloc(strlen(buf) + strlen(path) + 2);
     860          407 :         if (!new)
     861              :         {
     862            0 :             free(buf);
     863              : #ifndef FRONTEND
     864            0 :             ereport(ERROR,
     865              :                     (errcode(ERRCODE_OUT_OF_MEMORY),
     866              :                      errmsg("out of memory")));
     867              : #else
     868            0 :             fprintf(stderr, _("out of memory\n"));
     869            0 :             return NULL;
     870              : #endif
     871              :         }
     872          407 :         sprintf(new, "%s/%s", buf, path);
     873          407 :         free(buf);
     874              :     }
     875              :     else
     876              :     {
     877         2437 :         new = strdup(path);
     878         2437 :         if (!new)
     879              :         {
     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              :     }
     890              : 
     891              :     /* Make sure punctuation is canonical, too */
     892         2844 :     canonicalize_path(new);
     893              : 
     894         2844 :     return new;
     895              : }
     896              : 
     897              : 
     898              : /*
     899              :  *  get_share_path
     900              :  */
     901              : void
     902         9385 : get_share_path(const char *my_exec_path, char *ret_path)
     903              : {
     904         9385 :     make_relative_path(ret_path, PGSHAREDIR, PGBINDIR, my_exec_path);
     905         9385 : }
     906              : 
     907              : /*
     908              :  *  get_etc_path
     909              :  */
     910              : void
     911        12908 : get_etc_path(const char *my_exec_path, char *ret_path)
     912              : {
     913        12908 :     make_relative_path(ret_path, SYSCONFDIR, PGBINDIR, my_exec_path);
     914        12908 : }
     915              : 
     916              : /*
     917              :  *  get_include_path
     918              :  */
     919              : void
     920          582 : get_include_path(const char *my_exec_path, char *ret_path)
     921              : {
     922          582 :     make_relative_path(ret_path, INCLUDEDIR, PGBINDIR, my_exec_path);
     923          582 : }
     924              : 
     925              : /*
     926              :  *  get_pkginclude_path
     927              :  */
     928              : void
     929          524 : get_pkginclude_path(const char *my_exec_path, char *ret_path)
     930              : {
     931          524 :     make_relative_path(ret_path, PKGINCLUDEDIR, PGBINDIR, my_exec_path);
     932          524 : }
     933              : 
     934              : /*
     935              :  *  get_includeserver_path
     936              :  */
     937              : void
     938          513 : get_includeserver_path(const char *my_exec_path, char *ret_path)
     939              : {
     940          513 :     make_relative_path(ret_path, INCLUDEDIRSERVER, PGBINDIR, my_exec_path);
     941          513 : }
     942              : 
     943              : /*
     944              :  *  get_lib_path
     945              :  */
     946              : void
     947          513 : get_lib_path(const char *my_exec_path, char *ret_path)
     948              : {
     949          513 :     make_relative_path(ret_path, LIBDIR, PGBINDIR, my_exec_path);
     950          513 : }
     951              : 
     952              : /*
     953              :  *  get_pkglib_path
     954              :  */
     955              : void
     956         2214 : get_pkglib_path(const char *my_exec_path, char *ret_path)
     957              : {
     958         2214 :     make_relative_path(ret_path, PKGLIBDIR, PGBINDIR, my_exec_path);
     959         2214 : }
     960              : 
     961              : /*
     962              :  *  get_locale_path
     963              :  */
     964              : void
     965        19018 : get_locale_path(const char *my_exec_path, char *ret_path)
     966              : {
     967        19018 :     make_relative_path(ret_path, LOCALEDIR, PGBINDIR, my_exec_path);
     968        19018 : }
     969              : 
     970              : /*
     971              :  *  get_doc_path
     972              :  */
     973              : void
     974          513 : get_doc_path(const char *my_exec_path, char *ret_path)
     975              : {
     976          513 :     make_relative_path(ret_path, DOCDIR, PGBINDIR, my_exec_path);
     977          513 : }
     978              : 
     979              : /*
     980              :  *  get_html_path
     981              :  */
     982              : void
     983          513 : get_html_path(const char *my_exec_path, char *ret_path)
     984              : {
     985          513 :     make_relative_path(ret_path, HTMLDIR, PGBINDIR, my_exec_path);
     986          513 : }
     987              : 
     988              : /*
     989              :  *  get_man_path
     990              :  */
     991              : void
     992          513 : get_man_path(const char *my_exec_path, char *ret_path)
     993              : {
     994          513 :     make_relative_path(ret_path, MANDIR, PGBINDIR, my_exec_path);
     995          513 : }
     996              : 
     997              : 
     998              : /*
     999              :  *  get_home_path
    1000              :  *
    1001              :  * On Unix, this actually returns the user's home directory.  On Windows
    1002              :  * it returns the PostgreSQL-specific application data folder.
    1003              :  */
    1004              : bool
    1005            0 : get_home_path(char *ret_path)
    1006              : {
    1007              : #ifndef WIN32
    1008              :     /*
    1009              :      * We first consult $HOME.  If that's unset, try to get the info from
    1010              :      * <pwd.h>.
    1011              :      */
    1012              :     const char *home;
    1013              : 
    1014            0 :     home = getenv("HOME");
    1015            0 :     if (home && home[0])
    1016              :     {
    1017            0 :         strlcpy(ret_path, home, MAXPGPATH);
    1018            0 :         return true;
    1019              :     }
    1020              :     else
    1021              :     {
    1022              :         struct passwd pwbuf;
    1023              :         struct passwd *pw;
    1024              :         char        buf[1024];
    1025              :         int         rc;
    1026              : 
    1027            0 :         rc = getpwuid_r(geteuid(), &pwbuf, buf, sizeof buf, &pw);
    1028            0 :         if (rc != 0 || !pw)
    1029            0 :             return false;
    1030            0 :         strlcpy(ret_path, pw->pw_dir, MAXPGPATH);
    1031            0 :         return true;
    1032              :     }
    1033              : #else
    1034              :     char       *tmppath;
    1035              : 
    1036              :     /*
    1037              :      * Note: We use getenv() here because the more modern SHGetFolderPath()
    1038              :      * would force the backend to link with shell32.lib, which eats valuable
    1039              :      * desktop heap.  XXX This function is used only in psql, which already
    1040              :      * brings in shell32 via libpq.  Moving this function to its own file
    1041              :      * would keep it out of the backend, freeing it from this concern.
    1042              :      */
    1043              :     tmppath = getenv("APPDATA");
    1044              :     if (!tmppath)
    1045              :         return false;
    1046              :     snprintf(ret_path, MAXPGPATH, "%s/postgresql", tmppath);
    1047              :     return true;
    1048              : #endif
    1049              : }
    1050              : 
    1051              : 
    1052              : /*
    1053              :  * get_parent_directory
    1054              :  *
    1055              :  * Modify the given string in-place to name the parent directory of the
    1056              :  * named file.
    1057              :  *
    1058              :  * If the input is just a file name with no directory part, the result is
    1059              :  * an empty string, not ".".  This is appropriate when the next step is
    1060              :  * join_path_components(), but might need special handling otherwise.
    1061              :  *
    1062              :  * Caution: this will not produce desirable results if the string ends
    1063              :  * with "..".  For most callers this is not a problem since the string
    1064              :  * is already known to name a regular file.  If in doubt, apply
    1065              :  * canonicalize_path() first.
    1066              :  */
    1067              : void
    1068         8059 : get_parent_directory(char *path)
    1069              : {
    1070         8059 :     trim_directory(path);
    1071         8059 : }
    1072              : 
    1073              : 
    1074              : /*
    1075              :  *  trim_directory
    1076              :  *
    1077              :  *  Trim trailing directory from path, that is, remove any trailing slashes,
    1078              :  *  the last pathname component, and the slash just ahead of it --- but never
    1079              :  *  remove a leading slash.
    1080              :  *
    1081              :  * For the convenience of canonicalize_path, the path's new end location
    1082              :  * is returned.
    1083              :  */
    1084              : static char *
    1085        55338 : trim_directory(char *path)
    1086              : {
    1087              :     char       *p;
    1088              : 
    1089        55338 :     path = skip_drive(path);
    1090              : 
    1091        55338 :     if (path[0] == '\0')
    1092            0 :         return path;
    1093              : 
    1094              :     /* back up over trailing slash(es) */
    1095        55338 :     for (p = path + strlen(path) - 1; IS_DIR_SEP(*p) && p > path; p--)
    1096              :         ;
    1097              :     /* back up over directory name */
    1098       540062 :     for (; !IS_DIR_SEP(*p) && p > path; p--)
    1099              :         ;
    1100              :     /* if multiple slashes before directory name, remove 'em all */
    1101        55338 :     for (; p > path && IS_DIR_SEP(*(p - 1)); p--)
    1102              :         ;
    1103              :     /* don't erase a leading slash */
    1104        55338 :     if (p == path && IS_DIR_SEP(*p))
    1105           15 :         p++;
    1106        55338 :     *p = '\0';
    1107        55338 :     return p;
    1108              : }
    1109              : 
    1110              : 
    1111              : /*
    1112              :  *  trim_trailing_separator
    1113              :  *
    1114              :  * trim off trailing slashes, but not a leading slash
    1115              :  */
    1116              : static void
    1117       208774 : trim_trailing_separator(char *path)
    1118              : {
    1119              :     char       *p;
    1120              : 
    1121       208774 :     path = skip_drive(path);
    1122       208774 :     p = path + strlen(path);
    1123       208774 :     if (p > path)
    1124       255887 :         for (p--; p > path && IS_DIR_SEP(*p); p--)
    1125        47155 :             *p = '\0';
    1126       208774 : }
    1127              : 
    1128              : /*
    1129              :  *  append_subdir_to_path
    1130              :  *
    1131              :  * Append the currently-considered subdirectory name to the output
    1132              :  * path in canonicalize_path.  Return the new end location of the
    1133              :  * output path.
    1134              :  *
    1135              :  * Since canonicalize_path updates the path in-place, we must use
    1136              :  * memmove not memcpy, and we don't yet terminate the path with '\0'.
    1137              :  */
    1138              : static char *
    1139      1290612 : append_subdir_to_path(char *path, char *subdir)
    1140              : {
    1141      1290612 :     size_t      len = strlen(subdir);
    1142              : 
    1143              :     /* No need to copy data if path and subdir are the same. */
    1144      1290612 :     if (path != subdir)
    1145        59874 :         memmove(path, subdir, len);
    1146              : 
    1147      1290612 :     return path + len;
    1148              : }
        

Generated by: LCOV version 2.0-1