LCOV - code coverage report
Current view: top level - src/port - path.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 238 287 82.9 %
Date: 2025-02-22 07:14:56 Functions: 30 32 93.8 %
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-2025, 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       83448 : first_dir_separator(const char *filename)
     111             : {
     112             :     const char *p;
     113             : 
     114      512752 :     for (p = skip_drive(filename); *p; p++)
     115      489880 :         if (IS_DIR_SEP(*p))
     116       60576 :             return unconstify(char *, p);
     117       22872 :     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       17352 : first_path_var_separator(const char *pathlist)
     128             : {
     129             :     const char *p;
     130             : 
     131             :     /* skip_drive is not needed */
     132      571296 :     for (p = pathlist; *p; p++)
     133      562424 :         if (IS_PATH_VAR_SEP(*p))
     134        8480 :             return unconstify(char *, p);
     135        8872 :     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      336102 : last_dir_separator(const char *filename)
     146             : {
     147             :     const char *p,
     148      336102 :                *ret = NULL;
     149             : 
     150     7122274 :     for (p = skip_drive(filename); *p; p++)
     151     6786172 :         if (IS_DIR_SEP(*p))
     152      988012 :             ret = p;
     153      336102 :     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         536 : 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         536 : }
     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       11076 : 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       11076 : }
     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       99260 : join_path_components(char *ret_path,
     287             :                      const char *head, const char *tail)
     288             : {
     289       99260 :     if (ret_path != head)
     290        2792 :         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       99260 :     if (*tail)
     298             :     {
     299             :         /* only separate with slash if head wasn't empty */
     300       99260 :         snprintf(ret_path + strlen(ret_path), MAXPGPATH - strlen(ret_path),
     301             :                  "%s%s",
     302       99260 :                  (*(skip_drive(head)) != '\0') ? "/" : "",
     303             :                  tail);
     304             :     }
     305       99260 : }
     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      273366 : canonicalize_path(char *path)
     338             : {
     339             :     /* All server-safe encodings are alike here, so just use PG_SQL_ASCII */
     340      273366 :     canonicalize_path_enc(path, PG_SQL_ASCII);
     341      273366 : }
     342             : 
     343             : void
     344      273490 : canonicalize_path_enc(char *path, int encoding)
     345             : {
     346             :     char       *p,
     347             :                *to_p;
     348             :     char       *spath;
     349             :     char       *parsed;
     350             :     char       *unparse;
     351      273490 :     bool        was_sep = false;
     352             :     canonicalize_state state;
     353      273490 :     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      273490 :     trim_trailing_separator(path);
     379             : 
     380             :     /*
     381             :      * Remove duplicate adjacent separators
     382             :      */
     383      273490 :     p = path;
     384             : #ifdef WIN32
     385             :     /* Don't remove leading double-slash on Win32 */
     386             :     if (*p)
     387             :         p++;
     388             : #endif
     389      273490 :     to_p = p;
     390    14331772 :     for (; *p; p++, to_p++)
     391             :     {
     392             :         /* Handle many adjacent slashes, like "/a///b" */
     393    14058282 :         while (*p == '/' && was_sep)
     394           0 :             p++;
     395    14058282 :         if (to_p != p)
     396           0 :             *to_p = *p;
     397    14058282 :         was_sep = (*p == '/');
     398             :     }
     399      273490 :     *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      273490 :     spath = skip_drive(path);
     414      273490 :     if (*spath == '\0')
     415          66 :         return;                 /* empty path is returned as-is */
     416             : 
     417      273424 :     if (*spath == '/')
     418             :     {
     419      214772 :         state = ABSOLUTE_PATH_INIT;
     420             :         /* Skip the leading slash for absolute path */
     421      214772 :         parsed = unparse = (spath + 1);
     422             :     }
     423             :     else
     424             :     {
     425       58652 :         state = RELATIVE_PATH_INIT;
     426       58652 :         parsed = unparse = spath;
     427             :     }
     428             : 
     429     2455590 :     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     2182166 :         unparse_next = unparse;
     436    14116928 :         while (*unparse_next && *unparse_next != '/')
     437    11934762 :             unparse_next++;
     438     2182166 :         if (*unparse_next != '\0')
     439     1908748 :             *unparse_next++ = '\0';
     440             : 
     441             :         /* Identify type of this dir name */
     442     2182166 :         if (strcmp(unparse, ".") == 0)
     443             :         {
     444             :             /* We can ignore "." components in all cases */
     445       51516 :             unparse = unparse_next;
     446       51516 :             continue;
     447             :         }
     448             : 
     449     2130650 :         if (strcmp(unparse, "..") == 0)
     450         234 :             is_double_dot = true;
     451             :         else
     452             :         {
     453             :             /* adjacent separators were eliminated above */
     454             :             Assert(*unparse != '\0');
     455     2130416 :             is_double_dot = false;
     456             :         }
     457             : 
     458     2130650 :         switch (state)
     459             :         {
     460      214820 :             case ABSOLUTE_PATH_INIT:
     461             :                 /* We can ignore ".." immediately after / */
     462      214820 :                 if (!is_double_dot)
     463             :                 {
     464             :                     /* Append first dir name (we already have leading slash) */
     465      214784 :                     parsed = append_subdir_to_path(parsed, unparse);
     466      214784 :                     state = ABSOLUTE_WITH_N_DEPTH;
     467      214784 :                     pathdepth++;
     468             :                 }
     469      214820 :                 break;
     470     1750462 :             case ABSOLUTE_WITH_N_DEPTH:
     471     1750462 :                 if (is_double_dot)
     472             :                 {
     473             :                     /* Remove last parsed dir */
     474             :                     /* (trim_directory won't remove the leading slash) */
     475         102 :                     *parsed = '\0';
     476         102 :                     parsed = trim_directory(path);
     477         102 :                     if (--pathdepth == 0)
     478          30 :                         state = ABSOLUTE_PATH_INIT;
     479             :                 }
     480             :                 else
     481             :                 {
     482             :                     /* Append normal dir */
     483     1750360 :                     *parsed++ = '/';
     484     1750360 :                     parsed = append_subdir_to_path(parsed, unparse);
     485     1750360 :                     pathdepth++;
     486             :                 }
     487     1750462 :                 break;
     488       58616 :             case RELATIVE_PATH_INIT:
     489       58616 :                 if (is_double_dot)
     490             :                 {
     491             :                     /* Append irreducible double-dot (..) */
     492          36 :                     parsed = append_subdir_to_path(parsed, unparse);
     493          36 :                     state = RELATIVE_WITH_PARENT_REF;
     494             :                 }
     495             :                 else
     496             :                 {
     497             :                     /* Append normal dir */
     498       58580 :                     parsed = append_subdir_to_path(parsed, unparse);
     499       58580 :                     state = RELATIVE_WITH_N_DEPTH;
     500       58580 :                     pathdepth++;
     501             :                 }
     502       58616 :                 break;
     503      106704 :             case RELATIVE_WITH_N_DEPTH:
     504      106704 :                 if (is_double_dot)
     505             :                 {
     506             :                     /* Remove last parsed dir */
     507          54 :                     *parsed = '\0';
     508          54 :                     parsed = trim_directory(path);
     509          54 :                     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          42 :                         if (parsed == spath)
     518          24 :                             state = RELATIVE_PATH_INIT;
     519             :                         else
     520          18 :                             state = RELATIVE_WITH_PARENT_REF;
     521             :                     }
     522             :                 }
     523             :                 else
     524             :                 {
     525             :                     /* Append normal dir */
     526      106650 :                     *parsed++ = '/';
     527      106650 :                     parsed = append_subdir_to_path(parsed, unparse);
     528      106650 :                     pathdepth++;
     529             :                 }
     530      106704 :                 break;
     531          48 :             case RELATIVE_WITH_PARENT_REF:
     532          48 :                 if (is_double_dot)
     533             :                 {
     534             :                     /* Append next irreducible double-dot (..) */
     535           6 :                     *parsed++ = '/';
     536           6 :                     parsed = append_subdir_to_path(parsed, unparse);
     537             :                 }
     538             :                 else
     539             :                 {
     540             :                     /* Append normal dir */
     541          42 :                     *parsed++ = '/';
     542          42 :                     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          42 :                     state = RELATIVE_WITH_N_DEPTH;
     550          42 :                     pathdepth = 1;
     551             :                 }
     552          48 :                 break;
     553             :         }
     554             : 
     555     2130650 :         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      273424 :     if (parsed == spath)
     565          60 :         *parsed++ = '.';
     566             : 
     567             :     /* And finally, ensure the output path is nul-terminated. */
     568      273424 :     *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       13814 : 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       13814 :     path = skip_drive(path);    /* C: shouldn't affect our conclusion */
     586             : 
     587       13814 :     if (path[0] == '.' &&
     588           8 :         path[1] == '.' &&
     589           0 :         (path[2] == '\0' || path[2] == '/'))
     590           0 :         return true;
     591             : 
     592       13814 :     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       13814 : path_is_relative_and_below_cwd(const char *path)
     605             : {
     606       13814 :     if (is_absolute_path(path))
     607           0 :         return false;
     608             :     /* don't allow anything above the cwd */
     609       13814 :     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       13814 :         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         112 : path_is_prefix_of_path(const char *path1, const char *path2)
     638             : {
     639         112 :     int         path1_len = strlen(path1);
     640             : 
     641         112 :     if (strncmp(path1, path2, path1_len) == 0 &&
     642           0 :         (IS_DIR_SEP(path2[path1_len]) || path2[path1_len] == '\0'))
     643           0 :         return true;
     644         112 :     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       56232 : get_progname(const char *argv0)
     653             : {
     654             :     const char *nodir_name;
     655             :     char       *progname;
     656             : 
     657       56232 :     nodir_name = last_dir_separator(argv0);
     658       56232 :     if (nodir_name)
     659       44358 :         nodir_name++;
     660             :     else
     661       11874 :         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       56232 :     progname = strdup(nodir_name);
     668       56232 :     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       56232 :     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       87754 : dir_strcmp(const char *s1, const char *s2)
     691             : {
     692      351016 :     while (*s1 && *s2)
     693             :     {
     694      263262 :         if (
     695             : #ifndef WIN32
     696      263262 :             *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      263262 :         s1++, s2++;
     704             :     }
     705       87754 :     if (*s1)
     706           0 :         return 1;               /* s1 longer */
     707       87754 :     if (*s2)
     708           0 :         return -1;              /* s2 longer */
     709       87754 :     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       89654 : 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       89654 :     prefix_len = 0;
     751     1613772 :     for (i = 0; target_path[i] && bin_path[i]; i++)
     752             :     {
     753     1613772 :         if (IS_DIR_SEP(target_path[i]) && IS_DIR_SEP(bin_path[i]))
     754      358616 :             prefix_len = i + 1;
     755     1255156 :         else if (target_path[i] != bin_path[i])
     756       89654 :             break;
     757             :     }
     758       89654 :     if (prefix_len == 0)
     759           0 :         goto no_match;          /* no common prefix? */
     760       89654 :     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       89654 :     strlcpy(ret_path, my_exec_path, MAXPGPATH);
     767       89654 :     trim_directory(ret_path);   /* remove my executable name */
     768       89654 :     canonicalize_path(ret_path);
     769             : 
     770             :     /*
     771             :      * Tail match?
     772             :      */
     773       89654 :     tail_start = (int) strlen(ret_path) - tail_len;
     774       89654 :     if (tail_start > 0 &&
     775      177408 :         IS_DIR_SEP(ret_path[tail_start - 1]) &&
     776       87754 :         dir_strcmp(ret_path + tail_start, bin_path + prefix_len) == 0)
     777             :     {
     778       87754 :         ret_path[tail_start] = '\0';
     779       87754 :         trim_trailing_separator(ret_path);
     780       87754 :         join_path_components(ret_path, ret_path, target_path + prefix_len);
     781       87754 :         canonicalize_path(ret_path);
     782       87754 :         return;
     783             :     }
     784             : 
     785        1900 : no_match:
     786        1900 :     strlcpy(ret_path, target_path, MAXPGPATH);
     787        1900 :     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        4828 : make_absolute_path(const char *path)
     808             : {
     809             :     char       *new;
     810             : 
     811             :     /* Returning null for null input is convenient for some callers */
     812        4828 :     if (path == NULL)
     813           0 :         return NULL;
     814             : 
     815        4828 :     if (!is_absolute_path(path))
     816             :     {
     817             :         char       *buf;
     818             :         size_t      buflen;
     819             : 
     820         718 :         buflen = MAXPGPATH;
     821             :         for (;;)
     822             :         {
     823         718 :             buf = malloc(buflen);
     824         718 :             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         718 :             if (getcwd(buf, buflen))
     837         718 :                 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         718 :         new = malloc(strlen(buf) + strlen(path) + 2);
     860         718 :         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         718 :         sprintf(new, "%s/%s", buf, path);
     873         718 :         free(buf);
     874             :     }
     875             :     else
     876             :     {
     877        4110 :         new = strdup(path);
     878        4110 :         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        4828 :     canonicalize_path(new);
     893             : 
     894        4828 :     return new;
     895             : }
     896             : 
     897             : 
     898             : /*
     899             :  *  get_share_path
     900             :  */
     901             : void
     902       25416 : get_share_path(const char *my_exec_path, char *ret_path)
     903             : {
     904       25416 :     make_relative_path(ret_path, PGSHAREDIR, PGBINDIR, my_exec_path);
     905       25416 : }
     906             : 
     907             : /*
     908             :  *  get_etc_path
     909             :  */
     910             : void
     911       21982 : get_etc_path(const char *my_exec_path, char *ret_path)
     912             : {
     913       21982 :     make_relative_path(ret_path, SYSCONFDIR, PGBINDIR, my_exec_path);
     914       21982 : }
     915             : 
     916             : /*
     917             :  *  get_include_path
     918             :  */
     919             : void
     920         984 : get_include_path(const char *my_exec_path, char *ret_path)
     921             : {
     922         984 :     make_relative_path(ret_path, INCLUDEDIR, PGBINDIR, my_exec_path);
     923         984 : }
     924             : 
     925             : /*
     926             :  *  get_pkginclude_path
     927             :  */
     928             : void
     929         872 : get_pkginclude_path(const char *my_exec_path, char *ret_path)
     930             : {
     931         872 :     make_relative_path(ret_path, PKGINCLUDEDIR, PGBINDIR, my_exec_path);
     932         872 : }
     933             : 
     934             : /*
     935             :  *  get_includeserver_path
     936             :  */
     937             : void
     938         852 : get_includeserver_path(const char *my_exec_path, char *ret_path)
     939             : {
     940         852 :     make_relative_path(ret_path, INCLUDEDIRSERVER, PGBINDIR, my_exec_path);
     941         852 : }
     942             : 
     943             : /*
     944             :  *  get_lib_path
     945             :  */
     946             : void
     947         852 : get_lib_path(const char *my_exec_path, char *ret_path)
     948             : {
     949         852 :     make_relative_path(ret_path, LIBDIR, PGBINDIR, my_exec_path);
     950         852 : }
     951             : 
     952             : /*
     953             :  *  get_pkglib_path
     954             :  */
     955             : void
     956        3708 : get_pkglib_path(const char *my_exec_path, char *ret_path)
     957             : {
     958        3708 :     make_relative_path(ret_path, PKGLIBDIR, PGBINDIR, my_exec_path);
     959        3708 : }
     960             : 
     961             : /*
     962             :  *  get_locale_path
     963             :  */
     964             : void
     965       32432 : get_locale_path(const char *my_exec_path, char *ret_path)
     966             : {
     967       32432 :     make_relative_path(ret_path, LOCALEDIR, PGBINDIR, my_exec_path);
     968       32432 : }
     969             : 
     970             : /*
     971             :  *  get_doc_path
     972             :  */
     973             : void
     974         852 : get_doc_path(const char *my_exec_path, char *ret_path)
     975             : {
     976         852 :     make_relative_path(ret_path, DOCDIR, PGBINDIR, my_exec_path);
     977         852 : }
     978             : 
     979             : /*
     980             :  *  get_html_path
     981             :  */
     982             : void
     983         852 : get_html_path(const char *my_exec_path, char *ret_path)
     984             : {
     985         852 :     make_relative_path(ret_path, HTMLDIR, PGBINDIR, my_exec_path);
     986         852 : }
     987             : 
     988             : /*
     989             :  *  get_man_path
     990             :  */
     991             : void
     992         852 : get_man_path(const char *my_exec_path, char *ret_path)
     993             : {
     994         852 :     make_relative_path(ret_path, MANDIR, PGBINDIR, my_exec_path);
     995         852 : }
     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       12122 : get_parent_directory(char *path)
    1069             : {
    1070       12122 :     trim_directory(path);
    1071       12122 : }
    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      101932 : trim_directory(char *path)
    1086             : {
    1087             :     char       *p;
    1088             : 
    1089      101932 :     path = skip_drive(path);
    1090             : 
    1091      101932 :     if (path[0] == '\0')
    1092           0 :         return path;
    1093             : 
    1094             :     /* back up over trailing slash(es) */
    1095      101932 :     for (p = path + strlen(path) - 1; IS_DIR_SEP(*p) && p > path; p--)
    1096             :         ;
    1097             :     /* back up over directory name */
    1098      966040 :     for (; !IS_DIR_SEP(*p) && p > path; p--)
    1099             :         ;
    1100             :     /* if multiple slashes before directory name, remove 'em all */
    1101      101932 :     for (; p > path && IS_DIR_SEP(*(p - 1)); p--)
    1102             :         ;
    1103             :     /* don't erase a leading slash */
    1104      101932 :     if (p == path && IS_DIR_SEP(*p))
    1105          30 :         p++;
    1106      101932 :     *p = '\0';
    1107      101932 :     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      361244 : trim_trailing_separator(char *path)
    1118             : {
    1119             :     char       *p;
    1120             : 
    1121      361244 :     path = skip_drive(path);
    1122      361244 :     p = path + strlen(path);
    1123      361244 :     if (p > path)
    1124      450674 :         for (p--; p > path && IS_DIR_SEP(*p); p--)
    1125       89496 :             *p = '\0';
    1126      361244 : }
    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     2130458 : append_subdir_to_path(char *path, char *subdir)
    1140             : {
    1141     2130458 :     size_t      len = strlen(subdir);
    1142             : 
    1143             :     /* No need to copy data if path and subdir are the same. */
    1144     2130458 :     if (path != subdir)
    1145      108496 :         memmove(path, subdir, len);
    1146             : 
    1147     2130458 :     return path + len;
    1148             : }

Generated by: LCOV version 1.14