Subject: bug#10472: closed (Re: bug#10472: `realpath --relative-to=<path> /` outputs inconsistent trailing slash)

From: [email-masked] (GNU bug Tracking System)
Date: Wed, 25 Jan 2012 16:56:02 +0000

Your bug report

#10472: `realpath --relative-to=<path> /` outputs inconsistent trailing slash

which was filed against the coreutils package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
10472: http://debbugs.gnu.org/cgi/bugreport.cgi?bug=10472
GNU Bug Tracking System
Contact [email-masked] with problems

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

Subject: `realpath --relative-to=<path> /` outputs inconsistent trailing slash
From: Mike Frysinger <[email-masked]>
To: [email-masked]
Date: Tue, 10 Jan 2012 15:15:57 -0500
List-Id: <debbugs-submit.debbugs.gnu.org>

first some examples that look fine ...

these all output the same thing:
	realpath --relative-to=/usr /usr/bin
	realpath --relative-to=/usr/ /usr/bin
	realpath --relative-to=/usr /usr/bin/
	realpath --relative-to=/usr/ /usr/bin/
which is to say, they show:
	bin

as does these:
	realpath --relative-to=/usr/bin /usr
	realpath --relative-to=/usr/bin/ /usr
	realpath --relative-to=/usr/bin /usr/
	realpath --relative-to=/usr/bin/ /usr/
which is to say, they show:
	..

as does these:
	realpath --relative-to=/ /usr
	realpath --relative-to=/ /usr/
which is to say, they show:
	..

however, if the last argument is just the root path:
	realpath --relative-to=/usr /
	realpath --relative-to=/usr/ /
we end up with a trailing slash:
	../

for consistency, i don't think that should be the case

(reported by Ulrich Müller via https://bugs.gentoo.org/398339)
-mike