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 */
442466static void
443467make_relative_path (char * ret_path ,const char * target_path ,
444468const char * bin_path ,const char * my_exec_path )
445469{
446- const char * bin_end ;
447470int 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- gotono_match ;
452- prefix_len = bin_end - bin_path + 1 ;
453- if (strncmp (target_path ,bin_path ,prefix_len )!= 0 )
454- gotono_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+ gotono_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+ */
456495StrNCpy (ret_path ,my_exec_path ,MAXPGPATH );
457496trim_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 );
460497canonicalize_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
463514no_match :
464515StrNCpy (ret_path ,target_path ,MAXPGPATH );