LCOV - code coverage report
Current view: top level - src/port - path.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 159 233 68.2 %
Date: 2017-09-29 13:40:31 Functions: 25 30 83.3 %
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-2017, 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 <unistd.h>
      36             : #endif
      37             : 
      38             : #include "pg_config_paths.h"
      39             : 
      40             : 
      41             : #ifndef WIN32
      42             : #define IS_PATH_VAR_SEP(ch) ((ch) == ':')
      43             : #else
      44             : #define IS_PATH_VAR_SEP(ch) ((ch) == ';')
      45             : #endif
      46             : 
      47             : static void make_relative_path(char *ret_path, const char *target_path,
      48             :                    const char *bin_path, const char *my_exec_path);
      49             : static void trim_directory(char *path);
      50             : static void trim_trailing_separator(char *path);
      51             : 
      52             : 
      53             : /*
      54             :  * skip_drive
      55             :  *
      56             :  * On Windows, a path may begin with "C:" or "//network/".  Advance over
      57             :  * this and point to the effective start of the path.
      58             :  */
      59             : #ifdef WIN32
      60             : 
      61             : static char *
      62             : skip_drive(const char *path)
      63             : {
      64             :     if (IS_DIR_SEP(path[0]) && IS_DIR_SEP(path[1]))
      65             :     {
      66             :         path += 2;
      67             :         while (*path && !IS_DIR_SEP(*path))
      68             :             path++;
      69             :     }
      70             :     else if (isalpha((unsigned char) path[0]) && path[1] == ':')
      71             :     {
      72             :         path += 2;
      73             :     }
      74             :     return (char *) path;
      75             : }
      76             : #else
      77             : 
      78             : #define skip_drive(path)    (path)
      79             : #endif
      80             : 
      81             : /*
      82             :  *  has_drive_prefix
      83             :  *
      84             :  * Return true if the given pathname has a drive prefix.
      85             :  */
      86             : bool
      87           0 : has_drive_prefix(const char *path)
      88             : {
      89             : #ifdef WIN32
      90             :     return skip_drive(path) != path;
      91             : #else
      92           0 :     return false;
      93             : #endif
      94             : }
      95             : 
      96             : /*
      97             :  *  first_dir_separator
      98             :  *
      99             :  * Find the location of the first directory separator, return
     100             :  * NULL if not found.
     101             :  */
     102             : char *
     103        1608 : first_dir_separator(const char *filename)
     104             : {
     105             :     const char *p;
     106             : 
     107       12258 :     for (p = skip_drive(filename); *p; p++)
     108       12027 :         if (IS_DIR_SEP(*p))
     109        1377 :             return (char *) p;
     110         231 :     return NULL;
     111             : }
     112             : 
     113             : /*
     114             :  *  first_path_var_separator
     115             :  *
     116             :  * Find the location of the first path separator (i.e. ':' on
     117             :  * Unix, ';' on Windows), return NULL if not found.
     118             :  */
     119             : char *
     120         205 : first_path_var_separator(const char *pathlist)
     121             : {
     122             :     const char *p;
     123             : 
     124             :     /* skip_drive is not needed */
     125       11657 :     for (p = pathlist; *p; p++)
     126       11641 :         if (IS_PATH_VAR_SEP(*p))
     127         189 :             return (char *) p;
     128          16 :     return NULL;
     129             : }
     130             : 
     131             : /*
     132             :  *  last_dir_separator
     133             :  *
     134             :  * Find the location of the last directory separator, return
     135             :  * NULL if not found.
     136             :  */
     137             : char *
     138         394 : last_dir_separator(const char *filename)
     139             : {
     140             :     const char *p,
     141         394 :                *ret = NULL;
     142             : 
     143       14669 :     for (p = skip_drive(filename); *p; p++)
     144       14275 :         if (IS_DIR_SEP(*p))
     145        2070 :             ret = p;
     146         394 :     return (char *) ret;
     147             : }
     148             : 
     149             : 
     150             : /*
     151             :  *  make_native_path - on WIN32, change / to \ in the path
     152             :  *
     153             :  *  This effectively undoes canonicalize_path.
     154             :  *
     155             :  *  This is required because WIN32 COPY is an internal CMD.EXE
     156             :  *  command and doesn't process forward slashes in the same way
     157             :  *  as external commands.  Quoting the first argument to COPY
     158             :  *  does not convert forward to backward slashes, but COPY does
     159             :  *  properly process quoted forward slashes in the second argument.
     160             :  *
     161             :  *  COPY works with quoted forward slashes in the first argument
     162             :  *  only if the current directory is the same as the directory
     163             :  *  of the first argument.
     164             :  */
     165             : void
     166           0 : make_native_path(char *filename)
     167             : {
     168             : #ifdef WIN32
     169             :     char       *p;
     170             : 
     171             :     for (p = filename; *p; p++)
     172             :         if (*p == '/')
     173             :             *p = '\\';
     174             : #endif
     175           0 : }
     176             : 
     177             : 
     178             : /*
     179             :  * This function cleans up the paths for use with either cmd.exe or Msys
     180             :  * on Windows. We need them to use filenames without spaces, for which a
     181             :  * short filename is the safest equivalent, eg:
     182             :  *      C:/Progra~1/
     183             :  */
     184             : void
     185          13 : cleanup_path(char *path)
     186             : {
     187             : #ifdef WIN32
     188             :     char       *ptr;
     189             : 
     190             :     /*
     191             :      * GetShortPathName() will fail if the path does not exist, or short names
     192             :      * are disabled on this file system.  In both cases, we just return the
     193             :      * original path.  This is particularly useful for --sysconfdir, which
     194             :      * might not exist.
     195             :      */
     196             :     GetShortPathName(path, path, MAXPGPATH - 1);
     197             : 
     198             :     /* Replace '\' with '/' */
     199             :     for (ptr = path; *ptr; ptr++)
     200             :     {
     201             :         if (*ptr == '\\')
     202             :             *ptr = '/';
     203             :     }
     204             : #endif
     205          13 : }
     206             : 
     207             : 
     208             : /*
     209             :  * join_path_components - join two path components, inserting a slash
     210             :  *
     211             :  * We omit the slash if either given component is empty.
     212             :  *
     213             :  * ret_path is the output area (must be of size MAXPGPATH)
     214             :  *
     215             :  * ret_path can be the same as head, but not the same as tail.
     216             :  */
     217             : void
     218         918 : join_path_components(char *ret_path,
     219             :                      const char *head, const char *tail)
     220             : {
     221         918 :     if (ret_path != head)
     222         196 :         strlcpy(ret_path, head, MAXPGPATH);
     223             : 
     224             :     /*
     225             :      * Remove any leading "." in the tail component.
     226             :      *
     227             :      * Note: we used to try to remove ".." as well, but that's tricky to get
     228             :      * right; now we just leave it to be done by canonicalize_path() later.
     229             :      */
     230        1836 :     while (tail[0] == '.' && IS_DIR_SEP(tail[1]))
     231           0 :         tail += 2;
     232             : 
     233         918 :     if (*tail)
     234             :     {
     235             :         /* only separate with slash if head wasn't empty */
     236         918 :         snprintf(ret_path + strlen(ret_path), MAXPGPATH - strlen(ret_path),
     237             :                  "%s%s",
     238         918 :                  (*(skip_drive(head)) != '\0') ? "/" : "",
     239             :                  tail);
     240             :     }
     241         918 : }
     242             : 
     243             : 
     244             : /*
     245             :  *  Clean up path by:
     246             :  *      o  make Win32 path use Unix slashes
     247             :  *      o  remove trailing quote on Win32
     248             :  *      o  remove trailing slash
     249             :  *      o  remove duplicate adjacent separators
     250             :  *      o  remove trailing '.'
     251             :  *      o  process trailing '..' ourselves
     252             :  */
     253             : void
     254        1503 : canonicalize_path(char *path)
     255             : {
     256             :     char       *p,
     257             :                *to_p;
     258             :     char       *spath;
     259        1503 :     bool        was_sep = false;
     260             :     int         pending_strips;
     261             : 
     262             : #ifdef WIN32
     263             : 
     264             :     /*
     265             :      * The Windows command processor will accept suitably quoted paths with
     266             :      * forward slashes, but barfs badly with mixed forward and back slashes.
     267             :      */
     268             :     for (p = path; *p; p++)
     269             :     {
     270             :         if (*p == '\\')
     271             :             *p = '/';
     272             :     }
     273             : 
     274             :     /*
     275             :      * In Win32, if you do: prog.exe "a b" "\c\d\" the system will pass \c\d"
     276             :      * as argv[2], so trim off trailing quote.
     277             :      */
     278             :     if (p > path && *(p - 1) == '"')
     279             :         *(p - 1) = '/';
     280             : #endif
     281             : 
     282             :     /*
     283             :      * Removing the trailing slash on a path means we never get ugly double
     284             :      * trailing slashes. Also, Win32 can't stat() a directory with a trailing
     285             :      * slash. Don't remove a leading slash, though.
     286             :      */
     287        1503 :     trim_trailing_separator(path);
     288             : 
     289             :     /*
     290             :      * Remove duplicate adjacent separators
     291             :      */
     292        1503 :     p = path;
     293             : #ifdef WIN32
     294             :     /* Don't remove leading double-slash on Win32 */
     295             :     if (*p)
     296             :         p++;
     297             : #endif
     298        1503 :     to_p = p;
     299       94203 :     for (; *p; p++, to_p++)
     300             :     {
     301             :         /* Handle many adjacent slashes, like "/a///b" */
     302      185400 :         while (*p == '/' && was_sep)
     303           0 :             p++;
     304       92700 :         if (to_p != p)
     305           0 :             *to_p = *p;
     306       92700 :         was_sep = (*p == '/');
     307             :     }
     308        1503 :     *to_p = '\0';
     309             : 
     310             :     /*
     311             :      * Remove any trailing uses of "." and process ".." ourselves
     312             :      *
     313             :      * Note that "/../.." should reduce to just "/", while "../.." has to be
     314             :      * kept as-is.  In the latter case we put back mistakenly trimmed ".."
     315             :      * components below.  Also note that we want a Windows drive spec to be
     316             :      * visible to trim_directory(), but it's not part of the logic that's
     317             :      * looking at the name components; hence distinction between path and
     318             :      * spath.
     319             :      */
     320        1503 :     spath = skip_drive(path);
     321        1503 :     pending_strips = 0;
     322             :     for (;;)
     323             :     {
     324        1506 :         int         len = strlen(spath);
     325             : 
     326        1506 :         if (len >= 2 && strcmp(spath + len - 2, "/.") == 0)
     327           3 :             trim_directory(path);
     328        1503 :         else if (strcmp(spath, ".") == 0)
     329             :         {
     330             :             /* Want to leave "." alone, but "./.." has to become ".." */
     331           0 :             if (pending_strips > 0)
     332           0 :                 *spath = '\0';
     333           0 :             break;
     334             :         }
     335        3006 :         else if ((len >= 3 && strcmp(spath + len - 3, "/..") == 0) ||
     336        1503 :                  strcmp(spath, "..") == 0)
     337             :         {
     338           0 :             trim_directory(path);
     339           0 :             pending_strips++;
     340             :         }
     341        1503 :         else if (pending_strips > 0 && *spath != '\0')
     342             :         {
     343             :             /* trim a regular directory name canceled by ".." */
     344           0 :             trim_directory(path);
     345           0 :             pending_strips--;
     346             :             /* foo/.. should become ".", not empty */
     347           0 :             if (*spath == '\0')
     348           0 :                 strcpy(spath, ".");
     349             :         }
     350             :         else
     351             :             break;
     352           3 :     }
     353             : 
     354        1503 :     if (pending_strips > 0)
     355             :     {
     356             :         /*
     357             :          * We could only get here if path is now totally empty (other than a
     358             :          * possible drive specifier on Windows). We have to put back one or
     359             :          * more ".."'s that we took off.
     360             :          */
     361           0 :         while (--pending_strips > 0)
     362           0 :             strcat(path, "../");
     363           0 :         strcat(path, "..");
     364             :     }
     365        1503 : }
     366             : 
     367             : /*
     368             :  * Detect whether a path contains any parent-directory references ("..")
     369             :  *
     370             :  * The input *must* have been put through canonicalize_path previously.
     371             :  *
     372             :  * This is a bit tricky because we mustn't be fooled by "..a.." (legal)
     373             :  * nor "C:.." (legal on Unix but not Windows).
     374             :  */
     375             : bool
     376           0 : path_contains_parent_reference(const char *path)
     377             : {
     378             :     int         path_len;
     379             : 
     380           0 :     path = skip_drive(path);    /* C: shouldn't affect our conclusion */
     381             : 
     382           0 :     path_len = strlen(path);
     383             : 
     384             :     /*
     385             :      * ".." could be the whole path; otherwise, if it's present it must be at
     386             :      * the beginning, in the middle, or at the end.
     387             :      */
     388           0 :     if (strcmp(path, "..") == 0 ||
     389           0 :         strncmp(path, "../", 3) == 0 ||
     390           0 :         strstr(path, "/../") != NULL ||
     391           0 :         (path_len >= 3 && strcmp(path + path_len - 3, "/..") == 0))
     392           0 :         return true;
     393             : 
     394           0 :     return false;
     395             : }
     396             : 
     397             : /*
     398             :  * Detect whether a path is only in or below the current working directory.
     399             :  * An absolute path that matches the current working directory should
     400             :  * return false (we only want relative to the cwd).  We don't allow
     401             :  * "/../" even if that would keep us under the cwd (it is too hard to
     402             :  * track that).
     403             :  */
     404             : bool
     405           0 : path_is_relative_and_below_cwd(const char *path)
     406             : {
     407           0 :     if (is_absolute_path(path))
     408           0 :         return false;
     409             :     /* don't allow anything above the cwd */
     410           0 :     else if (path_contains_parent_reference(path))
     411           0 :         return false;
     412             : #ifdef WIN32
     413             : 
     414             :     /*
     415             :      * On Win32, a drive letter _not_ followed by a slash, e.g. 'E:abc', is
     416             :      * relative to the cwd on that drive, or the drive's root directory if
     417             :      * that drive has no cwd.  Because the path itself cannot tell us which is
     418             :      * the case, we have to assume the worst, i.e. that it is not below the
     419             :      * cwd.  We could use GetFullPathName() to find the full path but that
     420             :      * could change if the current directory for the drive changes underneath
     421             :      * us, so we just disallow it.
     422             :      */
     423             :     else if (isalpha((unsigned char) path[0]) && path[1] == ':' &&
     424             :              !IS_DIR_SEP(path[2]))
     425             :         return false;
     426             : #endif
     427             :     else
     428           0 :         return true;
     429             : }
     430             : 
     431             : /*
     432             :  * Detect whether path1 is a prefix of path2 (including equality).
     433             :  *
     434             :  * This is pretty trivial, but it seems better to export a function than
     435             :  * to export IS_DIR_SEP.
     436             :  */
     437             : bool
     438           4 : path_is_prefix_of_path(const char *path1, const char *path2)
     439             : {
     440           4 :     int         path1_len = strlen(path1);
     441             : 
     442           4 :     if (strncmp(path1, path2, path1_len) == 0 &&
     443           0 :         (IS_DIR_SEP(path2[path1_len]) || path2[path1_len] == '\0'))
     444           0 :         return true;
     445           4 :     return false;
     446             : }
     447             : 
     448             : /*
     449             :  * Extracts the actual name of the program as called -
     450             :  * stripped of .exe suffix if any
     451             :  */
     452             : const char *
     453         193 : get_progname(const char *argv0)
     454             : {
     455             :     const char *nodir_name;
     456             :     char       *progname;
     457             : 
     458         193 :     nodir_name = last_dir_separator(argv0);
     459         193 :     if (nodir_name)
     460           6 :         nodir_name++;
     461             :     else
     462         187 :         nodir_name = skip_drive(argv0);
     463             : 
     464             :     /*
     465             :      * Make a copy in case argv[0] is modified by ps_status. Leaks memory, but
     466             :      * called only once.
     467             :      */
     468         193 :     progname = strdup(nodir_name);
     469         193 :     if (progname == NULL)
     470             :     {
     471           0 :         fprintf(stderr, "%s: out of memory\n", nodir_name);
     472           0 :         abort();                /* This could exit the postmaster */
     473             :     }
     474             : 
     475             : #if defined(__CYGWIN__) || defined(WIN32)
     476             :     /* strip ".exe" suffix, regardless of case */
     477             :     if (strlen(progname) > sizeof(EXE) - 1 &&
     478             :         pg_strcasecmp(progname + strlen(progname) - (sizeof(EXE) - 1), EXE) == 0)
     479             :         progname[strlen(progname) - (sizeof(EXE) - 1)] = '\0';
     480             : #endif
     481             : 
     482         193 :     return progname;
     483             : }
     484             : 
     485             : 
     486             : /*
     487             :  * dir_strcmp: strcmp except any two DIR_SEP characters are considered equal,
     488             :  * and we honor filesystem case insensitivity if known
     489             :  */
     490             : static int
     491         522 : dir_strcmp(const char *s1, const char *s2)
     492             : {
     493        2610 :     while (*s1 && *s2)
     494             :     {
     495        1566 :         if (
     496             : #ifndef WIN32
     497        1566 :             *s1 != *s2
     498             : #else
     499             :         /* On windows, paths are case-insensitive */
     500             :             pg_tolower((unsigned char) *s1) != pg_tolower((unsigned char) *s2)
     501             : #endif
     502           0 :             && !(IS_DIR_SEP(*s1) && IS_DIR_SEP(*s2)))
     503           0 :             return (int) *s1 - (int) *s2;
     504        1566 :         s1++, s2++;
     505             :     }
     506         522 :     if (*s1)
     507           0 :         return 1;               /* s1 longer */
     508         522 :     if (*s2)
     509           0 :         return -1;              /* s2 longer */
     510         522 :     return 0;
     511             : }
     512             : 
     513             : 
     514             : /*
     515             :  * make_relative_path - make a path relative to the actual binary location
     516             :  *
     517             :  * This function exists to support relocation of installation trees.
     518             :  *
     519             :  *  ret_path is the output area (must be of size MAXPGPATH)
     520             :  *  target_path is the compiled-in path to the directory we want to find
     521             :  *  bin_path is the compiled-in path to the directory of executables
     522             :  *  my_exec_path is the actual location of my executable
     523             :  *
     524             :  * We determine the common prefix of target_path and bin_path, then compare
     525             :  * the remainder of bin_path to the last directory component(s) of
     526             :  * my_exec_path.  If they match, build the result as the part of my_exec_path
     527             :  * preceding the match, joined to the remainder of target_path.  If no match,
     528             :  * return target_path as-is.
     529             :  *
     530             :  * For example:
     531             :  *      target_path  = '/usr/local/share/postgresql'
     532             :  *      bin_path     = '/usr/local/bin'
     533             :  *      my_exec_path = '/opt/pgsql/bin/postmaster'
     534             :  * Given these inputs, the common prefix is '/usr/local/', the tail of
     535             :  * bin_path is 'bin' which does match the last directory component of
     536             :  * my_exec_path, so we would return '/opt/pgsql/share/postgresql'
     537             :  */
     538             : static void
     539         523 : make_relative_path(char *ret_path, const char *target_path,
     540             :                    const char *bin_path, const char *my_exec_path)
     541             : {
     542             :     int         prefix_len;
     543             :     int         tail_start;
     544             :     int         tail_len;
     545             :     int         i;
     546             : 
     547             :     /*
     548             :      * Determine the common prefix --- note we require it to end on a
     549             :      * directory separator, consider eg '/usr/lib' and '/usr/libexec'.
     550             :      */
     551         523 :     prefix_len = 0;
     552        9414 :     for (i = 0; target_path[i] && bin_path[i]; i++)
     553             :     {
     554        9414 :         if (IS_DIR_SEP(target_path[i]) && IS_DIR_SEP(bin_path[i]))
     555        2092 :             prefix_len = i + 1;
     556        7322 :         else if (target_path[i] != bin_path[i])
     557         523 :             break;
     558             :     }
     559         523 :     if (prefix_len == 0)
     560           0 :         goto no_match;          /* no common prefix? */
     561         523 :     tail_len = strlen(bin_path) - prefix_len;
     562             : 
     563             :     /*
     564             :      * Set up my_exec_path without the actual executable name, and
     565             :      * canonicalize to simplify comparison to bin_path.
     566             :      */
     567         523 :     strlcpy(ret_path, my_exec_path, MAXPGPATH);
     568         523 :     trim_directory(ret_path);   /* remove my executable name */
     569         523 :     canonicalize_path(ret_path);
     570             : 
     571             :     /*
     572             :      * Tail match?
     573             :      */
     574         523 :     tail_start = (int) strlen(ret_path) - tail_len;
     575        1046 :     if (tail_start > 0 &&
     576        1045 :         IS_DIR_SEP(ret_path[tail_start - 1]) &&
     577         522 :         dir_strcmp(ret_path + tail_start, bin_path + prefix_len) == 0)
     578             :     {
     579         522 :         ret_path[tail_start] = '\0';
     580         522 :         trim_trailing_separator(ret_path);
     581         522 :         join_path_components(ret_path, ret_path, target_path + prefix_len);
     582         522 :         canonicalize_path(ret_path);
     583        1045 :         return;
     584             :     }
     585             : 
     586             : no_match:
     587           1 :     strlcpy(ret_path, target_path, MAXPGPATH);
     588           1 :     canonicalize_path(ret_path);
     589             : }
     590             : 
     591             : 
     592             : /*
     593             :  * make_absolute_path
     594             :  *
     595             :  * If the given pathname isn't already absolute, make it so, interpreting
     596             :  * it relative to the current working directory.
     597             :  *
     598             :  * Also canonicalizes the path.  The result is always a malloc'd copy.
     599             :  *
     600             :  * In backend, failure cases result in ereport(ERROR); in frontend,
     601             :  * we write a complaint on stderr and return NULL.
     602             :  *
     603             :  * Note: interpretation of relative-path arguments during postmaster startup
     604             :  * should happen before doing ChangeToDataDir(), else the user will probably
     605             :  * not like the results.
     606             :  */
     607             : char *
     608          14 : make_absolute_path(const char *path)
     609             : {
     610             :     char       *new;
     611             : 
     612             :     /* Returning null for null input is convenient for some callers */
     613          14 :     if (path == NULL)
     614           0 :         return NULL;
     615             : 
     616          14 :     if (!is_absolute_path(path))
     617             :     {
     618             :         char       *buf;
     619             :         size_t      buflen;
     620             : 
     621           4 :         buflen = MAXPGPATH;
     622             :         for (;;)
     623             :         {
     624           4 :             buf = malloc(buflen);
     625           4 :             if (!buf)
     626             :             {
     627             : #ifndef FRONTEND
     628           0 :                 ereport(ERROR,
     629             :                         (errcode(ERRCODE_OUT_OF_MEMORY),
     630             :                          errmsg("out of memory")));
     631             : #else
     632           0 :                 fprintf(stderr, _("out of memory\n"));
     633           0 :                 return NULL;
     634             : #endif
     635             :             }
     636             : 
     637           4 :             if (getcwd(buf, buflen))
     638           4 :                 break;
     639           0 :             else if (errno == ERANGE)
     640             :             {
     641           0 :                 free(buf);
     642           0 :                 buflen *= 2;
     643           0 :                 continue;
     644             :             }
     645             :             else
     646             :             {
     647           0 :                 int         save_errno = errno;
     648             : 
     649           0 :                 free(buf);
     650           0 :                 errno = save_errno;
     651             : #ifndef FRONTEND
     652           0 :                 elog(ERROR, "could not get current working directory: %m");
     653             : #else
     654           0 :                 fprintf(stderr, _("could not get current working directory: %s\n"),
     655           0 :                         strerror(errno));
     656           0 :                 return NULL;
     657             : #endif
     658             :             }
     659           0 :         }
     660             : 
     661           4 :         new = malloc(strlen(buf) + strlen(path) + 2);
     662           4 :         if (!new)
     663             :         {
     664           0 :             free(buf);
     665             : #ifndef FRONTEND
     666           0 :             ereport(ERROR,
     667             :                     (errcode(ERRCODE_OUT_OF_MEMORY),
     668             :                      errmsg("out of memory")));
     669             : #else
     670           0 :             fprintf(stderr, _("out of memory\n"));
     671           0 :             return NULL;
     672             : #endif
     673             :         }
     674           4 :         sprintf(new, "%s/%s", buf, path);
     675           4 :         free(buf);
     676             :     }
     677             :     else
     678             :     {
     679          10 :         new = strdup(path);
     680          10 :         if (!new)
     681             :         {
     682             : #ifndef FRONTEND
     683           0 :             ereport(ERROR,
     684             :                     (errcode(ERRCODE_OUT_OF_MEMORY),
     685             :                      errmsg("out of memory")));
     686             : #else
     687           0 :             fprintf(stderr, _("out of memory\n"));
     688           0 :             return NULL;
     689             : #endif
     690             :         }
     691             :     }
     692             : 
     693             :     /* Make sure punctuation is canonical, too */
     694          14 :     canonicalize_path(new);
     695             : 
     696          14 :     return new;
     697             : }
     698             : 
     699             : 
     700             : /*
     701             :  *  get_share_path
     702             :  */
     703             : void
     704         506 : get_share_path(const char *my_exec_path, char *ret_path)
     705             : {
     706         506 :     make_relative_path(ret_path, PGSHAREDIR, PGBINDIR, my_exec_path);
     707         506 : }
     708             : 
     709             : /*
     710             :  *  get_etc_path
     711             :  */
     712             : void
     713           2 : get_etc_path(const char *my_exec_path, char *ret_path)
     714             : {
     715           2 :     make_relative_path(ret_path, SYSCONFDIR, PGBINDIR, my_exec_path);
     716           2 : }
     717             : 
     718             : /*
     719             :  *  get_include_path
     720             :  */
     721             : void
     722           1 : get_include_path(const char *my_exec_path, char *ret_path)
     723             : {
     724           1 :     make_relative_path(ret_path, INCLUDEDIR, PGBINDIR, my_exec_path);
     725           1 : }
     726             : 
     727             : /*
     728             :  *  get_pkginclude_path
     729             :  */
     730             : void
     731           1 : get_pkginclude_path(const char *my_exec_path, char *ret_path)
     732             : {
     733           1 :     make_relative_path(ret_path, PKGINCLUDEDIR, PGBINDIR, my_exec_path);
     734           1 : }
     735             : 
     736             : /*
     737             :  *  get_includeserver_path
     738             :  */
     739             : void
     740           1 : get_includeserver_path(const char *my_exec_path, char *ret_path)
     741             : {
     742           1 :     make_relative_path(ret_path, INCLUDEDIRSERVER, PGBINDIR, my_exec_path);
     743           1 : }
     744             : 
     745             : /*
     746             :  *  get_lib_path
     747             :  */
     748             : void
     749           1 : get_lib_path(const char *my_exec_path, char *ret_path)
     750             : {
     751           1 :     make_relative_path(ret_path, LIBDIR, PGBINDIR, my_exec_path);
     752           1 : }
     753             : 
     754             : /*
     755             :  *  get_pkglib_path
     756             :  */
     757             : void
     758           7 : get_pkglib_path(const char *my_exec_path, char *ret_path)
     759             : {
     760           7 :     make_relative_path(ret_path, PKGLIBDIR, PGBINDIR, my_exec_path);
     761           7 : }
     762             : 
     763             : /*
     764             :  *  get_locale_path
     765             :  */
     766             : void
     767           1 : get_locale_path(const char *my_exec_path, char *ret_path)
     768             : {
     769           1 :     make_relative_path(ret_path, LOCALEDIR, PGBINDIR, my_exec_path);
     770           1 : }
     771             : 
     772             : /*
     773             :  *  get_doc_path
     774             :  */
     775             : void
     776           1 : get_doc_path(const char *my_exec_path, char *ret_path)
     777             : {
     778           1 :     make_relative_path(ret_path, DOCDIR, PGBINDIR, my_exec_path);
     779           1 : }
     780             : 
     781             : /*
     782             :  *  get_html_path
     783             :  */
     784             : void
     785           1 : get_html_path(const char *my_exec_path, char *ret_path)
     786             : {
     787           1 :     make_relative_path(ret_path, HTMLDIR, PGBINDIR, my_exec_path);
     788           1 : }
     789             : 
     790             : /*
     791             :  *  get_man_path
     792             :  */
     793             : void
     794           1 : get_man_path(const char *my_exec_path, char *ret_path)
     795             : {
     796           1 :     make_relative_path(ret_path, MANDIR, PGBINDIR, my_exec_path);
     797           1 : }
     798             : 
     799             : 
     800             : /*
     801             :  *  get_home_path
     802             :  *
     803             :  * On Unix, this actually returns the user's home directory.  On Windows
     804             :  * it returns the PostgreSQL-specific application data folder.
     805             :  */
     806             : bool
     807           0 : get_home_path(char *ret_path)
     808             : {
     809             : #ifndef WIN32
     810             :     char        pwdbuf[BUFSIZ];
     811             :     struct passwd pwdstr;
     812           0 :     struct passwd *pwd = NULL;
     813             : 
     814           0 :     (void) pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd);
     815           0 :     if (pwd == NULL)
     816           0 :         return false;
     817           0 :     strlcpy(ret_path, pwd->pw_dir, MAXPGPATH);
     818           0 :     return true;
     819             : #else
     820             :     char       *tmppath;
     821             : 
     822             :     /*
     823             :      * Note: We use getenv() here because the more modern SHGetFolderPath()
     824             :      * would force the backend to link with shell32.lib, which eats valuable
     825             :      * desktop heap.  XXX This function is used only in psql, which already
     826             :      * brings in shell32 via libpq.  Moving this function to its own file
     827             :      * would keep it out of the backend, freeing it from this concern.
     828             :      */
     829             :     tmppath = getenv("APPDATA");
     830             :     if (!tmppath)
     831             :         return false;
     832             :     snprintf(ret_path, MAXPGPATH, "%s/postgresql", tmppath);
     833             :     return true;
     834             : #endif
     835             : }
     836             : 
     837             : 
     838             : /*
     839             :  * get_parent_directory
     840             :  *
     841             :  * Modify the given string in-place to name the parent directory of the
     842             :  * named file.
     843             :  *
     844             :  * If the input is just a file name with no directory part, the result is
     845             :  * an empty string, not ".".  This is appropriate when the next step is
     846             :  * join_path_components(), but might need special handling otherwise.
     847             :  *
     848             :  * Caution: this will not produce desirable results if the string ends
     849             :  * with "..".  For most callers this is not a problem since the string
     850             :  * is already known to name a regular file.  If in doubt, apply
     851             :  * canonicalize_path() first.
     852             :  */
     853             : void
     854          25 : get_parent_directory(char *path)
     855             : {
     856          25 :     trim_directory(path);
     857          25 : }
     858             : 
     859             : 
     860             : /*
     861             :  *  trim_directory
     862             :  *
     863             :  *  Trim trailing directory from path, that is, remove any trailing slashes,
     864             :  *  the last pathname component, and the slash just ahead of it --- but never
     865             :  *  remove a leading slash.
     866             :  */
     867             : static void
     868         551 : trim_directory(char *path)
     869             : {
     870             :     char       *p;
     871             : 
     872         551 :     path = skip_drive(path);
     873             : 
     874         551 :     if (path[0] == '\0')
     875         551 :         return;
     876             : 
     877             :     /* back up over trailing slash(es) */
     878         551 :     for (p = path + strlen(path) - 1; IS_DIR_SEP(*p) && p > path; p--)
     879             :         ;
     880             :     /* back up over directory name */
     881         551 :     for (; !IS_DIR_SEP(*p) && p > path; p--)
     882             :         ;
     883             :     /* if multiple slashes before directory name, remove 'em all */
     884         551 :     for (; p > path && IS_DIR_SEP(*(p - 1)); p--)
     885             :         ;
     886             :     /* don't erase a leading slash */
     887         551 :     if (p == path && IS_DIR_SEP(*p))
     888           0 :         p++;
     889         551 :     *p = '\0';
     890             : }
     891             : 
     892             : 
     893             : /*
     894             :  *  trim_trailing_separator
     895             :  *
     896             :  * trim off trailing slashes, but not a leading slash
     897             :  */
     898             : static void
     899        2025 : trim_trailing_separator(char *path)
     900             : {
     901             :     char       *p;
     902             : 
     903        2025 :     path = skip_drive(path);
     904        2025 :     p = path + strlen(path);
     905        2025 :     if (p > path)
     906        2549 :         for (p--; p > path && IS_DIR_SEP(*p); p--)
     907         524 :             *p = '\0';
     908        2025 : }

Generated by: LCOV version 1.11