Fix make_relative_path() to support cases where target_path and bin_path

differ by more than the last directory component.  Instead of insisting
that they match up to the last component, accept whatever common prefix
they have, and try to replace the non-matching part of bin_path with
the non-matching part of target_path in the actual executable's path.
In one way this is tighter than the old code, because it insists on
a match to the part of bin_path we want to substitute for, rather than
blindly stripping one directory component from the executable's path.
Per gripe from Martin Pitt and subsequent discussion.
This commit is contained in:
Tom Lane 2005-12-23 22:34:22 +00:00
parent 98b3c3c450
commit 936d43d842
1 changed files with 67 additions and 16 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/port/path.c,v 1.62 2005/11/22 18:17:34 momjian Exp $
* $PostgreSQL: pgsql/src/port/path.c,v 1.63 2005/12/23 22:34:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -418,6 +418,27 @@ get_progname(const char *argv0)
}
/*
* dir_strcmp: strcmp except any two DIR_SEP characters are considered equal
*/
static int
dir_strcmp(const char *s1, const char *s2)
{
while (*s1 && *s2)
{
if (*s1 != *s2 &&
!(IS_DIR_SEP(*s1) && IS_DIR_SEP(*s2)))
return (int) *s1 - (int) *s2;
s1++, s2++;
}
if (*s1)
return 1; /* s1 longer */
if (*s2)
return -1; /* s2 longer */
return 0;
}
/*
* make_relative_path - make a path relative to the actual binary location
*
@ -428,37 +449,67 @@ get_progname(const char *argv0)
* bin_path is the compiled-in path to the directory of executables
* my_exec_path is the actual location of my executable
*
* If target_path matches bin_path up to the last directory component of
* bin_path, then we build the result as my_exec_path (less the executable
* name and last directory) joined to the non-matching part of target_path.
* Otherwise, we return target_path as-is.
* 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 we would return '/opt/pgsql/share/postgresql'
* 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)
{
const char *bin_end;
int prefix_len;
int tail_start;
int tail_len;
int i;
bin_end = last_dir_separator(bin_path);
if (!bin_end)
goto no_match;
prefix_len = bin_end - bin_path + 1;
if (strncmp(target_path, bin_path, prefix_len) != 0)
goto no_match;
/*
* 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.
*/
StrNCpy(ret_path, my_exec_path, MAXPGPATH);
trim_directory(ret_path); /* remove my executable name */
trim_directory(ret_path); /* remove last directory component (/bin) */
join_path_components(ret_path, ret_path, target_path + prefix_len);
canonicalize_path(ret_path);
return;
/*
* 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:
StrNCpy(ret_path, target_path, MAXPGPATH);