From 603dc769af832d7a9670acc0997b83d63e8047ae Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Sun, 23 May 2021 17:35:01 +0200 Subject: [PATCH 1/2] Prefer File.getCanonicalFile over Path.normalize The former normalizes Windows 8.3 paths to long paths, whereas the latter doesn't. --- os/src/Path.scala | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/os/src/Path.scala b/os/src/Path.scala index 10717e0b..7149d63f 100644 --- a/os/src/Path.scala +++ b/os/src/Path.scala @@ -1,6 +1,7 @@ package os import collection.JavaConverters._ +import util.Properties trait PathChunk{ def segments: Seq[String] @@ -384,7 +385,20 @@ object Path { throw PathError.AbsolutePathOutsideRoot } - val normalized = f.normalize() + fromNIO(f) + } + + private def fromNIO(p: java.nio.file.Path): Path = { + // On Windows, File.getCanonicalFile normalizes 8.3 paths as long paths, + // whereas Path.normalize doesn't. So we use the former here. + val shouldUseIO = Properties.isWin && + p.getFileSystem.getClass.getName == "sun.nio.fs.WindowsFileSystem" + val normalized = + if (shouldUseIO) { + require(p.isAbsolute, s"$p is not an absolute path") + p.toFile.getCanonicalFile.toPath + } + else p.normalize() new Path(normalized) } @@ -437,8 +451,8 @@ class Path private[os](val wrapped: java.nio.file.Path) def /(chunk: PathChunk): ThisType = { if (chunk.ups > wrapped.getNameCount) throw PathError.AbsolutePathOutsideRoot - val resolved = wrapped.resolve(chunk.toString).normalize() - new Path(resolved) + val resolved = wrapped.resolve(chunk.toString) + Path.fromNIO(resolved) } override def toString = wrapped.toString From 8c07af2770bdd3bf778939b49241dd9e90b83f8a Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Tue, 8 Jun 2021 13:26:21 +0200 Subject: [PATCH 2/2] Add test --- os/test/src/PathTests.scala | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/os/test/src/PathTests.scala b/os/test/src/PathTests.scala index 37c2e363..b9a74a4f 100644 --- a/os/test/src/PathTests.scala +++ b/os/test/src/PathTests.scala @@ -4,6 +4,7 @@ import java.nio.file.Paths import os._ import utest.{assert => _, _} +import scala.util.Properties object PathTests extends TestSuite{ val tests = Tests { test("Basic"){ @@ -381,5 +382,21 @@ object PathTests extends TestSuite{ } } } + test("Windows short paths"){ + if (Properties.isWin) { + val dir = os.temp.dir(prefix = "os-lib-tests") + + val file = dir / "long-dir-name" / "long-file-name.txt" + // uncomment this line to make the test fail :/ + // val shortBefore = dir / "LONG-D~1" / "LONG-F~1.txt" + + val content = "os-lib test" + os.write(file, content, createFolders = true) + + val short = dir / "LONG-D~1" / "LONG-F~1.txt" + assert(os.read(short) == content) + assert(file == short) + } + } } }