Fix xslt_process() to ensure that it inserts a NULL terminator after the
[PostgreSQL.git] / src / port / path.c
blob4611b58edbec8570d8a1c177deabd6290b95bdef
1 /*-------------------------------------------------------------------------
3 * path.c
4 * portable path handling routines
6 * Portions Copyright (c) 1996-2009, 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,
424 * and we honor filesystem case insensitivity if known
426 static int
427 dir_strcmp(const char *s1, const char *s2)
429 while (*s1 && *s2)
431 if (
432 #ifndef WIN32
433 *s1 != *s2
434 #else
435 /* On windows, paths are case-insensitive */
436 pg_tolower((unsigned char) *s1) != pg_tolower((unsigned char) *s2)
437 #endif
438 && !(IS_DIR_SEP(*s1) && IS_DIR_SEP(*s2)))
439 return (int) *s1 - (int) *s2;
440 s1++, s2++;
442 if (*s1)
443 return 1; /* s1 longer */
444 if (*s2)
445 return -1; /* s2 longer */
446 return 0;
451 * make_relative_path - make a path relative to the actual binary location
453 * This function exists to support relocation of installation trees.
455 * ret_path is the output area (must be of size MAXPGPATH)
456 * target_path is the compiled-in path to the directory we want to find
457 * bin_path is the compiled-in path to the directory of executables
458 * my_exec_path is the actual location of my executable
460 * We determine the common prefix of target_path and bin_path, then compare
461 * the remainder of bin_path to the last directory component(s) of
462 * my_exec_path. If they match, build the result as the part of my_exec_path
463 * preceding the match, joined to the remainder of target_path. If no match,
464 * return target_path as-is.
466 * For example:
467 * target_path = '/usr/local/share/postgresql'
468 * bin_path = '/usr/local/bin'
469 * my_exec_path = '/opt/pgsql/bin/postmaster'
470 * Given these inputs, the common prefix is '/usr/local/', the tail of
471 * bin_path is 'bin' which does match the last directory component of
472 * my_exec_path, so we would return '/opt/pgsql/share/postgresql'
474 static void
475 make_relative_path(char *ret_path, const char *target_path,
476 const char *bin_path, const char *my_exec_path)
478 int prefix_len;
479 int tail_start;
480 int tail_len;
481 int i;
484 * Determine the common prefix --- note we require it to end on a
485 * directory separator, consider eg '/usr/lib' and '/usr/libexec'.
487 prefix_len = 0;
488 for (i = 0; target_path[i] && bin_path[i]; i++)
490 if (IS_DIR_SEP(target_path[i]) && IS_DIR_SEP(bin_path[i]))
491 prefix_len = i + 1;
492 else if (target_path[i] != bin_path[i])
493 break;
495 if (prefix_len == 0)
496 goto no_match; /* no common prefix? */
497 tail_len = strlen(bin_path) - prefix_len;
500 * Set up my_exec_path without the actual executable name, and
501 * canonicalize to simplify comparison to bin_path.
503 strlcpy(ret_path, my_exec_path, MAXPGPATH);
504 trim_directory(ret_path); /* remove my executable name */
505 canonicalize_path(ret_path);
508 * Tail match?
510 tail_start = (int) strlen(ret_path) - tail_len;
511 if (tail_start > 0 &&
512 IS_DIR_SEP(ret_path[tail_start - 1]) &&
513 dir_strcmp(ret_path + tail_start, bin_path + prefix_len) == 0)
515 ret_path[tail_start] = '\0';
516 trim_trailing_separator(ret_path);
517 join_path_components(ret_path, ret_path, target_path + prefix_len);
518 canonicalize_path(ret_path);
519 return;
522 no_match:
523 strlcpy(ret_path, target_path, MAXPGPATH);
524 canonicalize_path(ret_path);
529 * get_share_path
531 void
532 get_share_path(const char *my_exec_path, char *ret_path)
534 make_relative_path(ret_path, PGSHAREDIR, PGBINDIR, my_exec_path);
538 * get_etc_path
540 void
541 get_etc_path(const char *my_exec_path, char *ret_path)
543 make_relative_path(ret_path, SYSCONFDIR, PGBINDIR, my_exec_path);
547 * get_include_path
549 void
550 get_include_path(const char *my_exec_path, char *ret_path)
552 make_relative_path(ret_path, INCLUDEDIR, PGBINDIR, my_exec_path);
556 * get_pkginclude_path
558 void
559 get_pkginclude_path(const char *my_exec_path, char *ret_path)
561 make_relative_path(ret_path, PKGINCLUDEDIR, PGBINDIR, my_exec_path);
565 * get_includeserver_path
567 void
568 get_includeserver_path(const char *my_exec_path, char *ret_path)
570 make_relative_path(ret_path, INCLUDEDIRSERVER, PGBINDIR, my_exec_path);
574 * get_lib_path
576 void
577 get_lib_path(const char *my_exec_path, char *ret_path)
579 make_relative_path(ret_path, LIBDIR, PGBINDIR, my_exec_path);
583 * get_pkglib_path
585 void
586 get_pkglib_path(const char *my_exec_path, char *ret_path)
588 make_relative_path(ret_path, PKGLIBDIR, PGBINDIR, my_exec_path);
592 * get_locale_path
594 void
595 get_locale_path(const char *my_exec_path, char *ret_path)
597 make_relative_path(ret_path, LOCALEDIR, PGBINDIR, my_exec_path);
601 * get_doc_path
603 void
604 get_doc_path(const char *my_exec_path, char *ret_path)
606 make_relative_path(ret_path, DOCDIR, PGBINDIR, my_exec_path);
610 * get_html_path
612 void
613 get_html_path(const char *my_exec_path, char *ret_path)
615 make_relative_path(ret_path, HTMLDIR, PGBINDIR, my_exec_path);
619 * get_man_path
621 void
622 get_man_path(const char *my_exec_path, char *ret_path)
624 make_relative_path(ret_path, MANDIR, PGBINDIR, my_exec_path);
629 * get_home_path
631 * On Unix, this actually returns the user's home directory. On Windows
632 * it returns the PostgreSQL-specific application data folder.
634 bool
635 get_home_path(char *ret_path)
637 #ifndef WIN32
638 char pwdbuf[BUFSIZ];
639 struct passwd pwdstr;
640 struct passwd *pwd = NULL;
642 if (pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) != 0)
643 return false;
644 strlcpy(ret_path, pwd->pw_dir, MAXPGPATH);
645 return true;
646 #else
647 char *tmppath;
650 * Note: We use getenv here because the more modern
651 * SHGetSpecialFolderPath() will force us to link with shell32.lib which
652 * eats valuable desktop heap.
654 tmppath = getenv("APPDATA");
655 if (!tmppath)
656 return false;
657 snprintf(ret_path, MAXPGPATH, "%s/postgresql", tmppath);
658 return true;
659 #endif
664 * get_parent_directory
666 * Modify the given string in-place to name the parent directory of the
667 * named file.
669 void
670 get_parent_directory(char *path)
672 trim_directory(path);
677 * trim_directory
679 * Trim trailing directory from path, that is, remove any trailing slashes,
680 * the last pathname component, and the slash just ahead of it --- but never
681 * remove a leading slash.
683 static void
684 trim_directory(char *path)
686 char *p;
688 path = skip_drive(path);
690 if (path[0] == '\0')
691 return;
693 /* back up over trailing slash(es) */
694 for (p = path + strlen(path) - 1; IS_DIR_SEP(*p) && p > path; p--)
696 /* back up over directory name */
697 for (; !IS_DIR_SEP(*p) && p > path; p--)
699 /* if multiple slashes before directory name, remove 'em all */
700 for (; p > path && IS_DIR_SEP(*(p - 1)); p--)
702 /* don't erase a leading slash */
703 if (p == path && IS_DIR_SEP(*p))
704 p++;
705 *p = '\0';
710 * trim_trailing_separator
712 * trim off trailing slashes, but not a leading slash
714 static void
715 trim_trailing_separator(char *path)
717 char *p;
719 path = skip_drive(path);
720 p = path + strlen(path);
721 if (p > path)
722 for (p--; p > path && IS_DIR_SEP(*p); p--)
723 *p = '\0';