summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2020-03-05 15:31:22 +0100
committerGitHub <noreply@github.com>2020-03-05 15:31:22 +0100
commit62c113ebc7ba6fa1594b24158a6cc3e1170f4030 (patch)
tree37ef86b89836406c30799d91534c1bba9afc20e2
parent357edd86b429dfaa3c24dedabdad0eb68915616f (diff)
downloadNim-62c113ebc7ba6fa1594b24158a6cc3e1170f4030.tar.gz
fix #13579 joinPath("/foo/", "../a") is now /a (#13586)
-rw-r--r--compiler/pathutils.nim2
-rw-r--r--lib/pure/os.nim1
-rw-r--r--lib/pure/pathnorm.nim5
-rw-r--r--tests/stdlib/tos.nim13
4 files changed, 21 insertions, 0 deletions
diff --git a/compiler/pathutils.nim b/compiler/pathutils.nim
index 1c35eb0b2..888e0a572 100644
--- a/compiler/pathutils.nim
+++ b/compiler/pathutils.nim
@@ -102,6 +102,8 @@ when true:
 
 when isMainModule:
   doAssert AbsoluteDir"/Users/me///" / RelativeFile"z.nim" == AbsoluteFile"/Users/me/z.nim"
+  doAssert AbsoluteDir"/Users/me" / RelativeFile"../z.nim" == AbsoluteFile"/Users/z.nim"
+  doAssert AbsoluteDir"/Users/me/" / RelativeFile"../z.nim" == AbsoluteFile"/Users/z.nim"
   doAssert relativePath("/foo/bar.nim", "/foo/", '/') == "bar.nim"
   doAssert $RelativeDir"foo/bar" == "foo/bar"
   doAssert RelativeDir"foo/bar" == RelativeDir"foo/bar"
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 3db518273..ba092da05 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -127,6 +127,7 @@ template endsWith(a: string, b: set[char]): bool =
 
 proc joinPathImpl(result: var string, state: var int, tail: string) =
   let trailingSep = tail.endsWith({DirSep, AltSep}) or tail.len == 0 and result.endsWith({DirSep, AltSep})
+  normalizePathEnd(result, trailingSep=false)
   addNormalizePath(tail, result, state, DirSep)
   normalizePathEnd(result, trailingSep=trailingSep)
 
diff --git a/lib/pure/pathnorm.nim b/lib/pure/pathnorm.nim
index 3659e85e6..8763ba878 100644
--- a/lib/pure/pathnorm.nim
+++ b/lib/pure/pathnorm.nim
@@ -76,6 +76,11 @@ proc addNormalizePath*(x: string; result: var string; state: var int;
       if (state shr 1) >= 1:
         var d = result.len
         # f/..
+        # We could handle stripping trailing sep here: foo// => foo like this:
+        # while (d-1) > (state and 1) and result[d-1] in {DirSep, AltSep}: dec d
+        # but right now we instead handle it inside os.joinPath
+
+        # strip path component: foo/bar => foo
         while (d-1) > (state and 1) and result[d-1] notin {DirSep, AltSep}:
           dec d
         if d > 0:
diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim
index c649749e3..d713bfe0c 100644
--- a/tests/stdlib/tos.nim
+++ b/tests/stdlib/tos.nim
@@ -389,6 +389,19 @@ block ospaths:
   doAssert joinPath("foo", "./") == unixToNativePath"foo/"
   doAssert joinPath("foo", "", "bar/") == unixToNativePath"foo/bar/"
 
+  # issue #13579
+  doAssert joinPath("/foo", "../a") == unixToNativePath"/a"
+  doAssert joinPath("/foo/", "../a") == unixToNativePath"/a"
+  doAssert joinPath("/foo/.", "../a") == unixToNativePath"/a"
+  doAssert joinPath("/foo/.b", "../a") == unixToNativePath"/foo/a"
+  doAssert joinPath("/foo///", "..//a/") == unixToNativePath"/a/"
+  doAssert joinPath("foo/", "../a") == unixToNativePath"a"
+
+  when doslikeFileSystem:
+    doAssert joinPath("C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\Common7\\Tools\\", "..\\..\\VC\\vcvarsall.bat") == r"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"
+    doAssert joinPath("C:\\foo", "..\\a") == r"C:\a"
+    doAssert joinPath("C:\\foo\\", "..\\a") == r"C:\a"
+
 block getTempDir:
   block TMPDIR:
     # TMPDIR env var is not used if either of these are defined.