Subject: Re: bug#10472: `realpath --relative-to=<path> /` outputs inconsistent trailing slash
From: Pádraig Brady <[email-masked]>
To: Mike Frysinger <[email-masked]>
Cc: [email-masked]
Date: Wed, 25 Jan 2012 16:55:09 +0000
List-Id: <debbugs-submit.debbugs.gnu.org>

On 01/10/2012 10:30 PM, Pádraig Brady wrote:
> On 01/10/2012 09:53 PM, Mike Frysinger wrote:
>> On Tuesday 10 January 2012 15:15:57 Mike Frysinger wrote:
>>> as does these:
>>> 	realpath --relative-to=/ /usr
>>> 	realpath --relative-to=/ /usr/
>>> which is to say, they show:
>>> 	..
>>
>> sorry, typo here ... these actually output:
>> 	../usr
>>
>> i guess that should be just "usr".
>> -mike
> 
> Agreed. python concurs too:
> 
>>>> os.path.relpath(start='/usr',path='/')
> '..'
>>>> os.path.relpath(start='/',path='/usr')
> 'usr'
> 
> Essentially in these edge cases the relative paths
> printed are valid, but not canonicalised.
> 
> I'll fix it up.

Proposed fix attached.

cheers,
Pádraig.
From 3a8cc1af15f0c9c6a71c5abc9219237f1613e8cf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email-masked]>
Date: Wed, 25 Jan 2012 16:42:42 +0000
Subject: [PATCH] realpath: remove extraneous '/' for --relative-to edge cases

* src/realpath.c (path_common_prefix): Be consistent and
always include a leading '/' in the count returned.
(relpath): Account for the change in path_common_prefix()
and avoid outputting extra '/' chars in relative paths that
span the root dir.
* tests/misc/realpath: Add the two reported cases.
Reported by Mike Frysinger
---
 src/realpath.c      |   16 +++++++++++-----
 tests/misc/realpath |   11 ++++++++---
 2 files changed, 19 insertions(+), 8 deletions(-)

diff --git a/src/realpath.c b/src/realpath.c
index b39f7bf..8a07ab6 100644
--- a/src/realpath.c
+++ b/src/realpath.c
@@ -136,7 +136,7 @@ path_common_prefix (const char *path1, const char *path2)
       if (*path1 != *path2)
         break;
       if (*path1 == '/')
-        ret = i;
+        ret = i + 1;
       path1++;
       path2++;
       i++;
@@ -171,11 +171,16 @@ relpath (const char *can_fname)
       const char *relto_suffix = can_relative_to + common_index;
       const char *fname_suffix = can_fname + common_index;
 
+      /* skip over extraneous '/'.  */
+      if (*relto_suffix == '/')
+        relto_suffix++;
+      if (*fname_suffix == '/')
+        fname_suffix++;
+
       /* Replace remaining components of --relative-to with '..', to get
          to a common directory.  Then output the remainder of fname.  */
       if (*relto_suffix)
         {
-          ++relto_suffix;
           printf ("%s", "..");
           for (; *relto_suffix; ++relto_suffix)
             {
@@ -183,14 +188,15 @@ relpath (const char *can_fname)
                 printf ("%s", "/..");
             }
 
-          printf ("%s", fname_suffix);
+          if (*fname_suffix)
+            printf ("/%s", fname_suffix);
         }
       else
         {
           if (*fname_suffix)
-            printf ("%s", ++fname_suffix);
+            printf ("%s", fname_suffix);
           else
-            printf ("%c", '.');
+            putchar ('.');
         }
 
       putchar (use_nuls ? '\0' : '\n');
diff --git a/tests/misc/realpath b/tests/misc/realpath
index fb01393..8a1f336 100755
--- a/tests/misc/realpath
+++ b/tests/misc/realpath
@@ -41,13 +41,18 @@ realpath -m '' && fail=1
 
 # symlink resolution
 this=$(realpath .)
-test "$(realpath $relative ldir2/..)" = "$this/dir1" || fail=1
-test "$(realpath -L $relative ldir2/..)" = "$this" || fail=1
-test "$(realpath -s $relative ldir2)" = "$this/ldir2" || fail=1
+test "$(realpath ldir2/..)" = "$this/dir1" || fail=1
+test "$(realpath -L ldir2/..)" = "$this" || fail=1
+test "$(realpath -s ldir2)" = "$this/ldir2" || fail=1
 
 # relative string handling
 test $(realpath -m --relative-to=prefix prefixed/1) = '../prefixed/1' || fail=1
 test $(realpath -m --relative-to=prefixed prefix/1) = '../prefix/1' || fail=1
 test $(realpath -m --relative-to=prefixed prefixed/1) = '1' || fail=1
 
+# Ensure no redundant trailing '/' present, as was the case in v8.15
+test $(realpath -sm --relative-to=/usr /) = '..' || fail=1
+# Ensure no redundant leading '../' present, as was the case in v8.15
+test $(realpath -sm --relative-to=/ /usr) = 'usr' || fail=1
+
 Exit $fail
-- 
1.7.6.4