8
8
*
9
9
*
10
10
* 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 $
12
12
*
13
13
*-------------------------------------------------------------------------
14
14
*/
@@ -418,6 +418,27 @@ get_progname(const char *argv0)
418
418
}
419
419
420
420
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
+
421
442
/*
422
443
* make_relative_path - make a path relative to the actual binary location
423
444
*
@@ -428,37 +449,67 @@ get_progname(const char *argv0)
428
449
* bin_path is the compiled-in path to the directory of executables
429
450
* my_exec_path is the actual location of my executable
430
451
*
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.
435
457
*
436
458
* For example:
437
459
* target_path = '/usr/local/share/postgresql'
438
460
* bin_path = '/usr/local/bin'
439
461
* 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'
441
465
*/
442
466
static void
443
467
make_relative_path (char * ret_path , const char * target_path ,
444
468
const char * bin_path , const char * my_exec_path )
445
469
{
446
- const char * bin_end ;
447
470
int prefix_len ;
471
+ int tail_start ;
472
+ int tail_len ;
473
+ int i ;
448
474
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 ;
455
490
491
+ /*
492
+ * Set up my_exec_path without the actual executable name, and
493
+ * canonicalize to simplify comparison to bin_path.
494
+ */
456
495
StrNCpy (ret_path , my_exec_path , MAXPGPATH );
457
496
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 );
460
497
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
+ }
462
513
463
514
no_match :
464
515
StrNCpy (ret_path , target_path , MAXPGPATH );
0 commit comments