/* * make_relative_path - make a path relative to the actual binary location * * This function exists to support relocation of installation trees. * * ret_path is the output area (must be of size MAXPGPATH) * target_path is the compiled-in path to the directory we want to find * bin_path is the compiled-in path to the directory of executables * my_exec_path is the actual location of my executable * * We determine the common prefix of target_path and bin_path, then compare * the remainder of bin_path to the last directory component(s) of * my_exec_path. If they match, build the result as the part of my_exec_path * preceding the match, joined to the remainder of target_path. If no match, * return target_path as-is. * * For example: * target_path = '/usr/local/share/postgresql' * bin_path = '/usr/local/bin' * my_exec_path = '/opt/pgsql/bin/postmaster' * Given these inputs, the common prefix is '/usr/local/', the tail of * bin_path is 'bin' which does match the last directory component of * my_exec_path, so we would return '/opt/pgsql/share/postgresql' */ static void make_relative_path(char *ret_path, const char *target_path, const char *bin_path, const char *my_exec_path) { int prefix_len; int tail_start; int tail_len; int i; /* * Determine the common prefix --- note we require it to end on a * directory separator, consider eg '/usr/lib' and '/usr/libexec'. */ prefix_len = 0; for (i = 0; target_path[i] && bin_path[i]; i++) { if (IS_DIR_SEP(target_path[i]) && IS_DIR_SEP(bin_path[i])) prefix_len = i + 1; else if (target_path[i] != bin_path[i]) break; } if (prefix_len == 0) goto no_match; /* no common prefix? */ tail_len = strlen(bin_path) - prefix_len; /* * Set up my_exec_path without the actual executable name, and * canonicalize to simplify comparison to bin_path. */ strlcpy(ret_path, my_exec_path, MAXPGPATH); trim_directory(ret_path); /* remove my executable name */ canonicalize_path(ret_path); /* * Tail match? */ tail_start = (int) strlen(ret_path) - tail_len; if (tail_start > 0 && IS_DIR_SEP(ret_path[tail_start - 1]) && dir_strcmp(ret_path + tail_start, bin_path + prefix_len) == 0) { ret_path[tail_start] = '\0'; trim_trailing_separator(ret_path); join_path_components(ret_path, ret_path, target_path + prefix_len); canonicalize_path(ret_path); return; } no_match: strlcpy(ret_path, target_path, MAXPGPATH); canonicalize_path(ret_path); }
/* * Clean up path by: * o remove trailing slash * o remove duplicate adjacent separators * o remove trailing '.' * o process trailing '..' ourselves */ void canonicalize_path(char *path) { char *p, *to_p; bool was_sep = false; /* * Removing the trailing slash on a path means we never get ugly * double trailing slashes. */ trim_trailing_separator(path); /* * Remove duplicate adjacent separators */ p = path; to_p = p; for (; *p; p++, to_p++) { /* Handle many adjacent slashes, like "/a///b" */ while (*p == '/' && was_sep) p++; if (to_p != p) *to_p = *p; was_sep = (*p == '/'); } *to_p = '\0'; /* * Remove any trailing uses of "." and process ".." ourselves */ for (;;) { int len = strlen(path); if (len > 2 && strcmp(path + len - 2, "/.") == 0) trim_directory(path); else if (len > 3 && strcmp(path + len - 3, "/..") == 0) { trim_directory(path); trim_directory(path); /* remove directory above */ } else break; } }
/* * Clean up path by: * o make Win32 path use Unix slashes * o remove trailing quote on Win32 * o remove trailing slash * o remove duplicate adjacent separators * o remove trailing '.' * o process trailing '..' ourselves */ void canonicalize_path(char *path) { char *p, *to_p; char *spath; bool was_sep = false; int pending_strips; #ifdef WIN32 /* * The Windows command processor will accept suitably quoted paths with * forward slashes, but barfs badly with mixed forward and back slashes. */ for (p = path; *p; p++) { if (*p == '\\') *p = '/'; } /* * In Win32, if you do: prog.exe "a b" "\c\d\" the system will pass \c\d" * as argv[2], so trim off trailing quote. */ if (p > path && *(p - 1) == '"') *(p - 1) = '/'; #endif /* * Removing the trailing slash on a path means we never get ugly double * trailing slashes. Also, Win32 can't stat() a directory with a trailing * slash. Don't remove a leading slash, though. */ trim_trailing_separator(path); /* * Remove duplicate adjacent separators */ p = path; #ifdef WIN32 /* Don't remove leading double-slash on Win32 */ if (*p) p++; #endif to_p = p; for (; *p; p++, to_p++) { /* Handle many adjacent slashes, like "/a///b" */ while (*p == '/' && was_sep) p++; if (to_p != p) *to_p = *p; was_sep = (*p == '/'); } *to_p = '\0'; /* * Remove any trailing uses of "." and process ".." ourselves * * Note that "/../.." should reduce to just "/", while "../.." has to be * kept as-is. In the latter case we put back mistakenly trimmed ".." * components below. Also note that we want a Windows drive spec to be * visible to trim_directory(), but it's not part of the logic that's * looking at the name components; hence distinction between path and * spath. */ spath = skip_drive(path); pending_strips = 0; for (;;) { int len = strlen(spath); if (len >= 2 && strcmp(spath + len - 2, "/.") == 0) trim_directory(path); else if (strcmp(spath, ".") == 0) { /* Want to leave "." alone, but "./.." has to become ".." */ if (pending_strips > 0) *spath = '\0'; break; } else if ((len >= 3 && strcmp(spath + len - 3, "/..") == 0) || strcmp(spath, "..") == 0) { trim_directory(path); pending_strips++; } else if (pending_strips > 0 && *spath != '\0') { /* trim a regular directory name canceled by ".." */ trim_directory(path); pending_strips--; /* foo/.. should become ".", not empty */ if (*spath == '\0') strcpy(spath, "."); } else break; } if (pending_strips > 0) { /* * We could only get here if path is now totally empty (other than a * possible drive specifier on Windows). We have to put back one or * more ".."'s that we took off. */ while (--pending_strips > 0) strcat(path, "../"); strcat(path, ".."); } }
/* * Clean up path by: * o make Win32 path use Unix slashes * o remove trailing quote on Win32 * o remove trailing slash * o remove duplicate adjacent separators * o remove trailing '.' * o process trailing '..' ourselves */ void canonicalize_path(char *path) { char *p, *to_p; bool was_sep = false; #ifdef WIN32 /* * The Windows command processor will accept suitably quoted paths * with forward slashes, but barfs badly with mixed forward and back * slashes. */ for (p = path; *p; p++) { if (*p == '\\') *p = '/'; } /* * In Win32, if you do: prog.exe "a b" "\c\d\" the system will pass * \c\d" as argv[2], so trim off trailing quote. */ if (p > path && *(p - 1) == '"') *(p - 1) = '/'; #endif /* * Removing the trailing slash on a path means we never get ugly * double trailing slashes. Also, Win32 can't stat() a directory * with a trailing slash. Don't remove a leading slash, though. */ trim_trailing_separator(path); /* * Remove duplicate adjacent separators */ p = path; #ifdef WIN32 /* Don't remove leading double-slash on Win32 */ if (*p) p++; #endif to_p = p; for (; *p; p++, to_p++) { /* Handle many adjacent slashes, like "/a///b" */ while (*p == '/' && was_sep) p++; if (to_p != p) *to_p = *p; was_sep = (*p == '/'); } *to_p = '\0'; /* * Remove any trailing uses of "." and process ".." ourselves */ for (;;) { int len = strlen(path); if (len > 2 && strcmp(path + len - 2, "/.") == 0) trim_directory(path); else if (len > 3 && strcmp(path + len - 3, "/..") == 0) { trim_directory(path); trim_directory(path); /* remove directory above */ } else break; } }