Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 936d43d

Browse files
committed
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.
1 parent 98b3c3c commit 936d43d

File tree

1 file changed

+67
-16
lines changed

1 file changed

+67
-16
lines changed

src/port/path.c

Lines changed: 67 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/port/path.c,v 1.62 2005/11/22 18:17:34 momjian Exp $
11+
* $PostgreSQL: pgsql/src/port/path.c,v 1.63 2005/12/23 22:34:22 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -418,6 +418,27 @@ get_progname(const char *argv0)
418418
}
419419

420420

421+
/*
422+
* dir_strcmp: strcmp except any two DIR_SEP characters are considered equal
423+
*/
424+
static int
425+
dir_strcmp(const char *s1, const char *s2)
426+
{
427+
while (*s1 && *s2)
428+
{
429+
if (*s1 != *s2 &&
430+
!(IS_DIR_SEP(*s1) && IS_DIR_SEP(*s2)))
431+
return (int) *s1 - (int) *s2;
432+
s1++, s2++;
433+
}
434+
if (*s1)
435+
return 1; /* s1 longer */
436+
if (*s2)
437+
return -1; /* s2 longer */
438+
return 0;
439+
}
440+
441+
421442
/*
422443
* make_relative_path - make a path relative to the actual binary location
423444
*
@@ -428,37 +449,67 @@ get_progname(const char *argv0)
428449
* bin_path is the compiled-in path to the directory of executables
429450
* my_exec_path is the actual location of my executable
430451
*
431-
* If target_path matches bin_path up to the last directory component of
432-
* bin_path, then we build the result as my_exec_path (less the executable
433-
* name and last directory) joined to the non-matching part of target_path.
434-
* Otherwise, we return target_path as-is.
452+
* We determine the common prefix of target_path and bin_path, then compare
453+
* the remainder of bin_path to the last directory component(s) of
454+
* my_exec_path. If they match, build the result as the part of my_exec_path
455+
* preceding the match, joined to the remainder of target_path. If no match,
456+
* return target_path as-is.
435457
*
436458
* For example:
437459
* target_path = '/usr/local/share/postgresql'
438460
* bin_path = '/usr/local/bin'
439461
* my_exec_path = '/opt/pgsql/bin/postmaster'
440-
* Given these inputs we would return '/opt/pgsql/share/postgresql'
462+
* Given these inputs, the common prefix is '/usr/local/', the tail of
463+
* bin_path is 'bin' which does match the last directory component of
464+
* my_exec_path, so we would return '/opt/pgsql/share/postgresql'
441465
*/
442466
static void
443467
make_relative_path(char *ret_path, const char *target_path,
444468
const char *bin_path, const char *my_exec_path)
445469
{
446-
const char *bin_end;
447470
int prefix_len;
471+
int tail_start;
472+
int tail_len;
473+
int i;
448474

449-
bin_end = last_dir_separator(bin_path);
450-
if (!bin_end)
451-
goto no_match;
452-
prefix_len = bin_end - bin_path + 1;
453-
if (strncmp(target_path, bin_path, prefix_len) != 0)
454-
goto no_match;
475+
/*
476+
* Determine the common prefix --- note we require it to end on a
477+
* directory separator, consider eg '/usr/lib' and '/usr/libexec'.
478+
*/
479+
prefix_len = 0;
480+
for (i = 0; target_path[i] && bin_path[i]; i++)
481+
{
482+
if (IS_DIR_SEP(target_path[i]) && IS_DIR_SEP(bin_path[i]))
483+
prefix_len = i + 1;
484+
else if (target_path[i] != bin_path[i])
485+
break;
486+
}
487+
if (prefix_len == 0)
488+
goto no_match; /* no common prefix? */
489+
tail_len = strlen(bin_path) - prefix_len;
455490

491+
/*
492+
* Set up my_exec_path without the actual executable name, and
493+
* canonicalize to simplify comparison to bin_path.
494+
*/
456495
StrNCpy(ret_path, my_exec_path, MAXPGPATH);
457496
trim_directory(ret_path); /* remove my executable name */
458-
trim_directory(ret_path); /* remove last directory component (/bin) */
459-
join_path_components(ret_path, ret_path, target_path + prefix_len);
460497
canonicalize_path(ret_path);
461-
return;
498+
499+
/*
500+
* Tail match?
501+
*/
502+
tail_start = (int) strlen(ret_path) - tail_len;
503+
if (tail_start > 0 &&
504+
IS_DIR_SEP(ret_path[tail_start-1]) &&
505+
dir_strcmp(ret_path + tail_start, bin_path + prefix_len) == 0)
506+
{
507+
ret_path[tail_start] = '\0';
508+
trim_trailing_separator(ret_path);
509+
join_path_components(ret_path, ret_path, target_path + prefix_len);
510+
canonicalize_path(ret_path);
511+
return;
512+
}
462513

463514
no_match:
464515
StrNCpy(ret_path, target_path, MAXPGPATH);

0 commit comments

Comments
 (0)