Fix pg_dump bug in the database-level collation patch. "datcollate" and
[PostgreSQL.git] / src / port / path.c
blobdc22c770d3635edf89bb7f397f1b29855aee4676
1 /*-------------------------------------------------------------------------
3 * path.c
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
10 * IDENTIFICATION
11 * $PostgreSQL$
13 *-------------------------------------------------------------------------
16 #include "c.h"
18 #include <ctype.h>
19 #include <sys/stat.h>
20 #ifdef WIN32
21 #ifdef _WIN32_IE
22 #undef _WIN32_IE
23 #endif
24 #define _WIN32_IE 0x0500
25 #ifdef near
26 #undef near
27 #endif
28 #define near
29 #include <shlobj.h>
30 #else
31 #include <unistd.h>
32 #endif
34 #include "pg_config_paths.h"
37 #ifndef WIN32
38 #define IS_DIR_SEP(ch) ((ch) == '/')
39 #else
40 #define IS_DIR_SEP(ch) ((ch) == '/' || (ch) == '\\')
41 #endif
43 #ifndef WIN32
44 #define IS_PATH_SEP(ch) ((ch) == ':')
45 #else
46 #define IS_PATH_SEP(ch) ((ch) == ';')
47 #endif
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);
56 * skip_drive
58 * On Windows, a path may begin with "C:" or "//network/". Advance over
59 * this and point to the effective start of the path.
61 #ifdef WIN32
63 static char *
64 skip_drive(const char *path)
66 if (IS_DIR_SEP(path[0]) && IS_DIR_SEP(path[1]))
68 path += 2;
69 while (*path && !IS_DIR_SEP(*path))
70 path++;
72 else if (isalpha((unsigned char) path[0]) && path[1] == ':')
74 path += 2;
76 return (char *) path;
78 #else
80 #define skip_drive(path) (path)
81 #endif
84 * first_dir_separator
86 * Find the location of the first directory separator, return
87 * NULL if not found.
89 char *
90 first_dir_separator(const char *filename)
92 const char *p;
94 for (p = skip_drive(filename); *p; p++)
95 if (IS_DIR_SEP(*p))
96 return (char *) p;
97 return NULL;
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.
106 char *
107 first_path_separator(const char *pathlist)
109 const char *p;
111 /* skip_drive is not needed */
112 for (p = pathlist; *p; p++)
113 if (IS_PATH_SEP(*p))
114 return (char *) p;
115 return NULL;
119 * last_dir_separator
121 * Find the location of the last directory separator, return
122 * NULL if not found.
124 char *
125 last_dir_separator(const char *filename)
127 const char *p,
128 *ret = NULL;
130 for (p = skip_drive(filename); *p; p++)
131 if (IS_DIR_SEP(*p))
132 ret = p;
133 return (char *) ret;
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.
152 void
153 make_native_path(char *filename)
155 #ifdef WIN32
156 char *p;
158 for (p = filename; *p; p++)
159 if (*p == '/')
160 *p = '\\';
161 #endif
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.
172 void
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
181 * as needed.
183 for (;;)
185 if (tail[0] == '.' && IS_DIR_SEP(tail[1]))
187 tail += 2;
189 else if (tail[0] == '.' && tail[1] == '\0')
191 tail += 1;
192 break;
194 else if (tail[0] == '.' && tail[1] == '.' && IS_DIR_SEP(tail[2]))
196 trim_directory(ret_path);
197 tail += 3;
199 else if (tail[0] == '.' && tail[1] == '.' && tail[2] == '\0')
201 trim_directory(ret_path);
202 tail += 2;
203 break;
205 else
206 break;
208 if (*tail)
209 snprintf(ret_path + strlen(ret_path), MAXPGPATH - strlen(ret_path),
210 "/%s", tail);
215 * Clean up path by:
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
223 void
224 canonicalize_path(char *path)
226 char *p,
227 *to_p;
228 char *spath;
229 bool was_sep = false;
230 int pending_strips;
232 #ifdef WIN32
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++)
240 if (*p == '\\')
241 *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) == '"')
249 *(p - 1) = '/';
250 #endif
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
262 p = path;
263 #ifdef WIN32
264 /* Don't remove leading double-slash on Win32 */
265 if (*p)
266 p++;
267 #endif
268 to_p = p;
269 for (; *p; p++, to_p++)
271 /* Handle many adjacent slashes, like "/a///b" */
272 while (*p == '/' && was_sep)
273 p++;
274 if (to_p != p)
275 *to_p = *p;
276 was_sep = (*p == '/');
278 *to_p = '\0';
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
288 * spath.
290 spath = skip_drive(path);
291 pending_strips = 0;
292 for (;;)
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)
302 *spath = '\0';
303 break;
305 else if ((len >= 3 && strcmp(spath + len - 3, "/..") == 0) ||
306 strcmp(spath, "..") == 0)
308 trim_directory(path);
309 pending_strips++;
311 else if (pending_strips > 0 && *spath != '\0')
313 /* trim a regular directory name cancelled by ".." */
314 trim_directory(path);
315 pending_strips--;
316 /* foo/.. should become ".", not empty */
317 if (*spath == '\0')
318 strcpy(spath, ".");
320 else
321 break;
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)
332 strcat(path, "../");
333 strcat(path, "..");
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).
345 bool
346 path_contains_parent_reference(const char *path)
348 int path_len;
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))
362 return true;
364 return false;
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.
373 bool
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'))
380 return true;
381 return false;
385 * Extracts the actual name of the program as called -
386 * stripped of .exe suffix if any
388 const char *
389 get_progname(const char *argv0)
391 const char *nodir_name;
392 char *progname;
394 nodir_name = last_dir_separator(argv0);
395 if (nodir_name)
396 nodir_name++;
397 else
398 nodir_name = skip_drive(argv0);
401 * Make a copy in case argv[0] is modified by ps_status. Leaks memory, but
402 * called only once.
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';
416 #endif
418 return progname;
423 * dir_strcmp: strcmp except any two DIR_SEP characters are considered equal
425 static int
426 dir_strcmp(const char *s1, const char *s2)
428 while (*s1 && *s2)
430 if (*s1 != *s2 &&
431 !(IS_DIR_SEP(*s1) && IS_DIR_SEP(*s2)))
432 return (int) *s1 - (int) *s2;
433 s1++, s2++;
435 if (*s1)
436 return 1; /* s1 longer */
437 if (*s2)
438 return -1; /* s2 longer */
439 return 0;
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.
459 * For example:
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'
467 static void
468 make_relative_path(char *ret_path, const char *target_path,
469 const char *bin_path, const char *my_exec_path)
471 int prefix_len;
472 int tail_start;
473 int tail_len;
474 int i;
477 * Determine the common prefix --- note we require it to end on a
478 * directory separator, consider eg '/usr/lib' and '/usr/libexec'.
480 prefix_len = 0;
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]))
484 prefix_len = i + 1;
485 else if (target_path[i] != bin_path[i])
486 break;
488 if (prefix_len == 0)
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);
501 * Tail match?
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);
512 return;
515 no_match:
516 strlcpy(ret_path, target_path, MAXPGPATH);
517 canonicalize_path(ret_path);
522 * get_share_path
524 void
525 get_share_path(const char *my_exec_path, char *ret_path)
527 make_relative_path(ret_path, PGSHAREDIR, PGBINDIR, my_exec_path);
531 * get_etc_path
533 void
534 get_etc_path(const char *my_exec_path, char *ret_path)
536 make_relative_path(ret_path, SYSCONFDIR, PGBINDIR, my_exec_path);
540 * get_include_path
542 void
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
551 void
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
560 void
561 get_includeserver_path(const char *my_exec_path, char *ret_path)
563 make_relative_path(ret_path, INCLUDEDIRSERVER, PGBINDIR, my_exec_path);
567 * get_lib_path
569 void
570 get_lib_path(const char *my_exec_path, char *ret_path)
572 make_relative_path(ret_path, LIBDIR, PGBINDIR, my_exec_path);
576 * get_pkglib_path
578 void
579 get_pkglib_path(const char *my_exec_path, char *ret_path)
581 make_relative_path(ret_path, PKGLIBDIR, PGBINDIR, my_exec_path);
585 * get_locale_path
587 void
588 get_locale_path(const char *my_exec_path, char *ret_path)
590 make_relative_path(ret_path, LOCALEDIR, PGBINDIR, my_exec_path);
594 * get_doc_path
596 void
597 get_doc_path(const char *my_exec_path, char *ret_path)
599 make_relative_path(ret_path, DOCDIR, PGBINDIR, my_exec_path);
603 * get_html_path
605 void
606 get_html_path(const char *my_exec_path, char *ret_path)
608 make_relative_path(ret_path, HTMLDIR, PGBINDIR, my_exec_path);
612 * get_man_path
614 void
615 get_man_path(const char *my_exec_path, char *ret_path)
617 make_relative_path(ret_path, MANDIR, PGBINDIR, my_exec_path);
622 * get_home_path
624 * On Unix, this actually returns the user's home directory. On Windows
625 * it returns the PostgreSQL-specific application data folder.
627 bool
628 get_home_path(char *ret_path)
630 #ifndef WIN32
631 char pwdbuf[BUFSIZ];
632 struct passwd pwdstr;
633 struct passwd *pwd = NULL;
635 if (pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) != 0)
636 return false;
637 strlcpy(ret_path, pwd->pw_dir, MAXPGPATH);
638 return true;
639 #else
640 char *tmppath;
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");
648 if (!tmppath)
649 return false;
650 snprintf(ret_path, MAXPGPATH, "%s/postgresql", tmppath);
651 return true;
652 #endif
657 * get_parent_directory
659 * Modify the given string in-place to name the parent directory of the
660 * named file.
662 void
663 get_parent_directory(char *path)
665 trim_directory(path);
670 * trim_directory
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.
676 static void
677 trim_directory(char *path)
679 char *p;
681 path = skip_drive(path);
683 if (path[0] == '\0')
684 return;
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))
697 p++;
698 *p = '\0';
703 * trim_trailing_separator
705 * trim off trailing slashes, but not a leading slash
707 static void
708 trim_trailing_separator(char *path)
710 char *p;
712 path = skip_drive(path);
713 p = path + strlen(path);
714 if (p > path)
715 for (p--; p > path && IS_DIR_SEP(*p); p--)
716 *p = '\0';