1 /*-------------------------------------------------------------------------
4 * portable path handling routines
6 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
13 *-------------------------------------------------------------------------
24 #define _WIN32_IE 0x0500
34 #include "pg_config_paths.h"
38 #define IS_DIR_SEP(ch) ((ch) == '/')
40 #define IS_DIR_SEP(ch) ((ch) == '/' || (ch) == '\\')
44 #define IS_PATH_SEP(ch) ((ch) == ':')
46 #define IS_PATH_SEP(ch) ((ch) == ';')
49 static void make_relative_path(char *ret_path
, const char *target_path
,
50 const char *bin_path
, const char *my_exec_path
);
51 static void trim_directory(char *path
);
52 static void trim_trailing_separator(char *path
);
58 * On Windows, a path may begin with "C:" or "//network/". Advance over
59 * this and point to the effective start of the path.
64 skip_drive(const char *path
)
66 if (IS_DIR_SEP(path
[0]) && IS_DIR_SEP(path
[1]))
69 while (*path
&& !IS_DIR_SEP(*path
))
72 else if (isalpha((unsigned char) path
[0]) && path
[1] == ':')
80 #define skip_drive(path) (path)
86 * Find the location of the first directory separator, return
90 first_dir_separator(const char *filename
)
94 for (p
= skip_drive(filename
); *p
; p
++)
101 * first_path_separator
103 * Find the location of the first path separator (i.e. ':' on
104 * Unix, ';' on Windows), return NULL if not found.
107 first_path_separator(const char *pathlist
)
111 /* skip_drive is not needed */
112 for (p
= pathlist
; *p
; p
++)
121 * Find the location of the last directory separator, return
125 last_dir_separator(const char *filename
)
130 for (p
= skip_drive(filename
); *p
; p
++)
138 * make_native_path - on WIN32, change / to \ in the path
140 * This effectively undoes canonicalize_path.
142 * This is required because WIN32 COPY is an internal CMD.EXE
143 * command and doesn't process forward slashes in the same way
144 * as external commands. Quoting the first argument to COPY
145 * does not convert forward to backward slashes, but COPY does
146 * properly process quoted forward slashes in the second argument.
148 * COPY works with quoted forward slashes in the first argument
149 * only if the current directory is the same as the directory
150 * of the first argument.
153 make_native_path(char *filename
)
158 for (p
= filename
; *p
; p
++)
166 * join_path_components - join two path components, inserting a slash
168 * ret_path is the output area (must be of size MAXPGPATH)
170 * ret_path can be the same as head, but not the same as tail.
173 join_path_components(char *ret_path
,
174 const char *head
, const char *tail
)
176 if (ret_path
!= head
)
177 strlcpy(ret_path
, head
, MAXPGPATH
);
180 * Remove any leading "." and ".." in the tail component, adjusting head
185 if (tail
[0] == '.' && IS_DIR_SEP(tail
[1]))
189 else if (tail
[0] == '.' && tail
[1] == '\0')
194 else if (tail
[0] == '.' && tail
[1] == '.' && IS_DIR_SEP(tail
[2]))
196 trim_directory(ret_path
);
199 else if (tail
[0] == '.' && tail
[1] == '.' && tail
[2] == '\0')
201 trim_directory(ret_path
);
209 snprintf(ret_path
+ strlen(ret_path
), MAXPGPATH
- strlen(ret_path
),
216 * o make Win32 path use Unix slashes
217 * o remove trailing quote on Win32
218 * o remove trailing slash
219 * o remove duplicate adjacent separators
220 * o remove trailing '.'
221 * o process trailing '..' ourselves
224 canonicalize_path(char *path
)
229 bool was_sep
= false;
235 * The Windows command processor will accept suitably quoted paths with
236 * forward slashes, but barfs badly with mixed forward and back slashes.
238 for (p
= path
; *p
; p
++)
245 * In Win32, if you do: prog.exe "a b" "\c\d\" the system will pass \c\d"
246 * as argv[2], so trim off trailing quote.
248 if (p
> path
&& *(p
- 1) == '"')
253 * Removing the trailing slash on a path means we never get ugly double
254 * trailing slashes. Also, Win32 can't stat() a directory with a trailing
255 * slash. Don't remove a leading slash, though.
257 trim_trailing_separator(path
);
260 * Remove duplicate adjacent separators
264 /* Don't remove leading double-slash on Win32 */
269 for (; *p
; p
++, to_p
++)
271 /* Handle many adjacent slashes, like "/a///b" */
272 while (*p
== '/' && was_sep
)
276 was_sep
= (*p
== '/');
281 * Remove any trailing uses of "." and process ".." ourselves
283 * Note that "/../.." should reduce to just "/", while "../.." has to be
284 * kept as-is. In the latter case we put back mistakenly trimmed ".."
285 * components below. Also note that we want a Windows drive spec to be
286 * visible to trim_directory(), but it's not part of the logic that's
287 * looking at the name components; hence distinction between path and
290 spath
= skip_drive(path
);
294 int len
= strlen(spath
);
296 if (len
>= 2 && strcmp(spath
+ len
- 2, "/.") == 0)
297 trim_directory(path
);
298 else if (strcmp(spath
, ".") == 0)
300 /* Want to leave "." alone, but "./.." has to become ".." */
301 if (pending_strips
> 0)
305 else if ((len
>= 3 && strcmp(spath
+ len
- 3, "/..") == 0) ||
306 strcmp(spath
, "..") == 0)
308 trim_directory(path
);
311 else if (pending_strips
> 0 && *spath
!= '\0')
313 /* trim a regular directory name cancelled by ".." */
314 trim_directory(path
);
316 /* foo/.. should become ".", not empty */
324 if (pending_strips
> 0)
327 * We could only get here if path is now totally empty (other than a
328 * possible drive specifier on Windows). We have to put back one or
329 * more ".."'s that we took off.
331 while (--pending_strips
> 0)
338 * Detect whether a path contains any parent-directory references ("..")
340 * The input *must* have been put through canonicalize_path previously.
342 * This is a bit tricky because we mustn't be fooled by "..a.." (legal)
343 * nor "C:.." (legal on Unix but not Windows).
346 path_contains_parent_reference(const char *path
)
350 path
= skip_drive(path
); /* C: shouldn't affect our conclusion */
352 path_len
= strlen(path
);
355 * ".." could be the whole path; otherwise, if it's present it must be at
356 * the beginning, in the middle, or at the end.
358 if (strcmp(path
, "..") == 0 ||
359 strncmp(path
, "../", 3) == 0 ||
360 strstr(path
, "/../") != NULL
||
361 (path_len
>= 3 && strcmp(path
+ path_len
- 3, "/..") == 0))
368 * Detect whether path1 is a prefix of path2 (including equality).
370 * This is pretty trivial, but it seems better to export a function than
371 * to export IS_DIR_SEP.
374 path_is_prefix_of_path(const char *path1
, const char *path2
)
376 int path1_len
= strlen(path1
);
378 if (strncmp(path1
, path2
, path1_len
) == 0 &&
379 (IS_DIR_SEP(path2
[path1_len
]) || path2
[path1_len
] == '\0'))
385 * Extracts the actual name of the program as called -
386 * stripped of .exe suffix if any
389 get_progname(const char *argv0
)
391 const char *nodir_name
;
394 nodir_name
= last_dir_separator(argv0
);
398 nodir_name
= skip_drive(argv0
);
401 * Make a copy in case argv[0] is modified by ps_status. Leaks memory, but
404 progname
= strdup(nodir_name
);
405 if (progname
== NULL
)
407 fprintf(stderr
, "%s: out of memory\n", nodir_name
);
408 exit(1); /* This could exit the postmaster */
411 #if defined(__CYGWIN__) || defined(WIN32)
412 /* strip ".exe" suffix, regardless of case */
413 if (strlen(progname
) > sizeof(EXE
) - 1 &&
414 pg_strcasecmp(progname
+ strlen(progname
) - (sizeof(EXE
) - 1), EXE
) == 0)
415 progname
[strlen(progname
) - (sizeof(EXE
) - 1)] = '\0';
423 * dir_strcmp: strcmp except any two DIR_SEP characters are considered equal
426 dir_strcmp(const char *s1
, const char *s2
)
431 !(IS_DIR_SEP(*s1
) && IS_DIR_SEP(*s2
)))
432 return (int) *s1
- (int) *s2
;
436 return 1; /* s1 longer */
438 return -1; /* s2 longer */
444 * make_relative_path - make a path relative to the actual binary location
446 * This function exists to support relocation of installation trees.
448 * ret_path is the output area (must be of size MAXPGPATH)
449 * target_path is the compiled-in path to the directory we want to find
450 * bin_path is the compiled-in path to the directory of executables
451 * my_exec_path is the actual location of my executable
453 * We determine the common prefix of target_path and bin_path, then compare
454 * the remainder of bin_path to the last directory component(s) of
455 * my_exec_path. If they match, build the result as the part of my_exec_path
456 * preceding the match, joined to the remainder of target_path. If no match,
457 * return target_path as-is.
460 * target_path = '/usr/local/share/postgresql'
461 * bin_path = '/usr/local/bin'
462 * my_exec_path = '/opt/pgsql/bin/postmaster'
463 * Given these inputs, the common prefix is '/usr/local/', the tail of
464 * bin_path is 'bin' which does match the last directory component of
465 * my_exec_path, so we would return '/opt/pgsql/share/postgresql'
468 make_relative_path(char *ret_path
, const char *target_path
,
469 const char *bin_path
, const char *my_exec_path
)
477 * Determine the common prefix --- note we require it to end on a
478 * directory separator, consider eg '/usr/lib' and '/usr/libexec'.
481 for (i
= 0; target_path
[i
] && bin_path
[i
]; i
++)
483 if (IS_DIR_SEP(target_path
[i
]) && IS_DIR_SEP(bin_path
[i
]))
485 else if (target_path
[i
] != bin_path
[i
])
489 goto no_match
; /* no common prefix? */
490 tail_len
= strlen(bin_path
) - prefix_len
;
493 * Set up my_exec_path without the actual executable name, and
494 * canonicalize to simplify comparison to bin_path.
496 strlcpy(ret_path
, my_exec_path
, MAXPGPATH
);
497 trim_directory(ret_path
); /* remove my executable name */
498 canonicalize_path(ret_path
);
503 tail_start
= (int) strlen(ret_path
) - tail_len
;
504 if (tail_start
> 0 &&
505 IS_DIR_SEP(ret_path
[tail_start
- 1]) &&
506 dir_strcmp(ret_path
+ tail_start
, bin_path
+ prefix_len
) == 0)
508 ret_path
[tail_start
] = '\0';
509 trim_trailing_separator(ret_path
);
510 join_path_components(ret_path
, ret_path
, target_path
+ prefix_len
);
511 canonicalize_path(ret_path
);
516 strlcpy(ret_path
, target_path
, MAXPGPATH
);
517 canonicalize_path(ret_path
);
525 get_share_path(const char *my_exec_path
, char *ret_path
)
527 make_relative_path(ret_path
, PGSHAREDIR
, PGBINDIR
, my_exec_path
);
534 get_etc_path(const char *my_exec_path
, char *ret_path
)
536 make_relative_path(ret_path
, SYSCONFDIR
, PGBINDIR
, my_exec_path
);
543 get_include_path(const char *my_exec_path
, char *ret_path
)
545 make_relative_path(ret_path
, INCLUDEDIR
, PGBINDIR
, my_exec_path
);
549 * get_pkginclude_path
552 get_pkginclude_path(const char *my_exec_path
, char *ret_path
)
554 make_relative_path(ret_path
, PKGINCLUDEDIR
, PGBINDIR
, my_exec_path
);
558 * get_includeserver_path
561 get_includeserver_path(const char *my_exec_path
, char *ret_path
)
563 make_relative_path(ret_path
, INCLUDEDIRSERVER
, PGBINDIR
, my_exec_path
);
570 get_lib_path(const char *my_exec_path
, char *ret_path
)
572 make_relative_path(ret_path
, LIBDIR
, PGBINDIR
, my_exec_path
);
579 get_pkglib_path(const char *my_exec_path
, char *ret_path
)
581 make_relative_path(ret_path
, PKGLIBDIR
, PGBINDIR
, my_exec_path
);
588 get_locale_path(const char *my_exec_path
, char *ret_path
)
590 make_relative_path(ret_path
, LOCALEDIR
, PGBINDIR
, my_exec_path
);
597 get_doc_path(const char *my_exec_path
, char *ret_path
)
599 make_relative_path(ret_path
, DOCDIR
, PGBINDIR
, my_exec_path
);
606 get_html_path(const char *my_exec_path
, char *ret_path
)
608 make_relative_path(ret_path
, HTMLDIR
, PGBINDIR
, my_exec_path
);
615 get_man_path(const char *my_exec_path
, char *ret_path
)
617 make_relative_path(ret_path
, MANDIR
, PGBINDIR
, my_exec_path
);
624 * On Unix, this actually returns the user's home directory. On Windows
625 * it returns the PostgreSQL-specific application data folder.
628 get_home_path(char *ret_path
)
632 struct passwd pwdstr
;
633 struct passwd
*pwd
= NULL
;
635 if (pqGetpwuid(geteuid(), &pwdstr
, pwdbuf
, sizeof(pwdbuf
), &pwd
) != 0)
637 strlcpy(ret_path
, pwd
->pw_dir
, MAXPGPATH
);
643 * Note: We use getenv here because the more modern
644 * SHGetSpecialFolderPath() will force us to link with shell32.lib which
645 * eats valuable desktop heap.
647 tmppath
= getenv("APPDATA");
650 snprintf(ret_path
, MAXPGPATH
, "%s/postgresql", tmppath
);
657 * get_parent_directory
659 * Modify the given string in-place to name the parent directory of the
663 get_parent_directory(char *path
)
665 trim_directory(path
);
672 * Trim trailing directory from path, that is, remove any trailing slashes,
673 * the last pathname component, and the slash just ahead of it --- but never
674 * remove a leading slash.
677 trim_directory(char *path
)
681 path
= skip_drive(path
);
686 /* back up over trailing slash(es) */
687 for (p
= path
+ strlen(path
) - 1; IS_DIR_SEP(*p
) && p
> path
; p
--)
689 /* back up over directory name */
690 for (; !IS_DIR_SEP(*p
) && p
> path
; p
--)
692 /* if multiple slashes before directory name, remove 'em all */
693 for (; p
> path
&& IS_DIR_SEP(*(p
- 1)); p
--)
695 /* don't erase a leading slash */
696 if (p
== path
&& IS_DIR_SEP(*p
))
703 * trim_trailing_separator
705 * trim off trailing slashes, but not a leading slash
708 trim_trailing_separator(char *path
)
712 path
= skip_drive(path
);
713 p
= path
+ strlen(path
);
715 for (p
--; p
> path
&& IS_DIR_SEP(*p
); p
--)