From f1dec1198cf320535074968c2ce69f99b56bf7c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Ferreira?= Date: Wed, 25 Nov 2020 18:08:28 +0000 Subject: [PATCH 1/2] Update IO.toURI to avoid syscalls same as https://github.com/eed3si9n/sjson-new/pull/117/files --- io/src/main/scala/sbt/io/IO.scala | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/io/src/main/scala/sbt/io/IO.scala b/io/src/main/scala/sbt/io/IO.scala index 78bb2948..7d338d85 100644 --- a/io/src/main/scala/sbt/io/IO.scala +++ b/io/src/main/scala/sbt/io/IO.scala @@ -1174,11 +1174,18 @@ object IO { /** Converts the given File to a URI. If the File is relative, the URI is relative, unlike File.toURI*/ def toURI(f: File): URI = if (f.isAbsolute) { - f.toPath.toUri + //not using f.toURI to avoid filesystem syscalls + //we use empty string as host to force file:// instead of just file: + new URI(FileScheme, "", normalizeName(slashify(f.getAbsolutePath)), null) } else { // need to use the three argument URI constructor because the single argument version doesn't encode new URI(null, normalizeName(f.getPath), null) } + + private[this] def slashify(name: String) = { + if(name.nonEmpty && name.head != File.separatorChar) File.separatorChar + name + else name + } /** * Resolves `f` against `base`, which must be an absolute directory. From 3727e9149eaa5b4e7597bb8524747020255c8614 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Fri, 27 Nov 2020 11:06:43 -0500 Subject: [PATCH 2/2] Improve toURI on Windows Ref https://github.com/eed3si9n/sjson-new/pull/119 --- io/src/main/scala/sbt/io/IO.scala | 34 ++++++++++++++++++--------- io/src/test/scala/sbt/io/IOSpec.scala | 32 +++++++++++++++++++++---- 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/io/src/main/scala/sbt/io/IO.scala b/io/src/main/scala/sbt/io/IO.scala index 7d338d85..a9176db4 100644 --- a/io/src/main/scala/sbt/io/IO.scala +++ b/io/src/main/scala/sbt/io/IO.scala @@ -15,7 +15,7 @@ import java.net.{ URI, URISyntaxException, URL } import java.nio.charset.Charset import java.nio.file.attribute.PosixFilePermissions import java.nio.file.{ Path => NioPath, _ } -import java.util.Properties +import java.util.{ Locale, Properties } import java.util.jar.{ Attributes, JarEntry, JarFile, JarOutputStream, Manifest } import java.util.zip.{ CRC32, ZipEntry, ZipInputStream, ZipOutputStream } @@ -674,7 +674,7 @@ object IO { ) = { val files = sources .flatMap { - case (file, name) => if (file.isFile) (file, normalizeName(name)) :: Nil else Nil + case (file, name) => if (file.isFile) (file, normalizeToSlash(name)) :: Nil else Nil } .sortBy { case (_, name) => name @@ -734,7 +734,7 @@ object IO { private def allDirectoryPaths(files: Iterable[(File, String)]) = TreeSet[String]() ++ (files flatMap { case (_, name) => directoryPaths(name) }) - private def normalizeName(name: String) = { + private def normalizeToSlash(name: String) = { val sep = File.separatorChar if (sep == '/') name else name.replace(sep, '/') } @@ -1171,20 +1171,32 @@ object IO { dirURI.normalize } + private[sbt] val isWindows: Boolean = + System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows") + /** Converts the given File to a URI. If the File is relative, the URI is relative, unlike File.toURI*/ - def toURI(f: File): URI = - if (f.isAbsolute) { + def toURI(f: File): URI = { + def ensureHeadSlash(name: String) = + if (name.nonEmpty && name.head != File.separatorChar) File.separatorChar + name + else name + + val p = f.getPath + if (isWindows && p.nonEmpty && p.head == File.separatorChar) { + if (p.startsWith("""\\""")) { + // supports \\laptop\My Documents\Some.doc on Windows + new URI(FileScheme, normalizeToSlash(p), null) + } else { + // supports /tmp on Windows + new URI(FileScheme, "", normalizeToSlash(p), null) + } + } else if (f.isAbsolute) { //not using f.toURI to avoid filesystem syscalls //we use empty string as host to force file:// instead of just file: - new URI(FileScheme, "", normalizeName(slashify(f.getAbsolutePath)), null) + new URI(FileScheme, "", normalizeToSlash(ensureHeadSlash(f.getAbsolutePath)), null) } else { // need to use the three argument URI constructor because the single argument version doesn't encode - new URI(null, normalizeName(f.getPath), null) + new URI(null, normalizeToSlash(f.getPath), null) } - - private[this] def slashify(name: String) = { - if(name.nonEmpty && name.head != File.separatorChar) File.separatorChar + name - else name } /** diff --git a/io/src/test/scala/sbt/io/IOSpec.scala b/io/src/test/scala/sbt/io/IOSpec.scala index 6dbf4206..fbcf2d1b 100644 --- a/io/src/test/scala/sbt/io/IOSpec.scala +++ b/io/src/test/scala/sbt/io/IOSpec.scala @@ -84,8 +84,8 @@ class IOSpec extends FunSuite { } test("toURI should make URI") { - val u = IO.toURI(file("/etc/hosts").getAbsoluteFile) - assert(u.toString.startsWith("file:///") && u.toString.endsWith("etc/hosts")) + val u = IO.toURI(file("/etc/hosts")) + assert(u.toString == "file:///etc/hosts") } test("it should make u0 URI from a relative path") { @@ -93,9 +93,17 @@ class IOSpec extends FunSuite { assert(u.toString == "src/main/scala") } + test("it should make u0 URI from a relative path on Windows") { + if (IO.isWindows) { + val input = file("""..\My Documents\test""") + val u = IO.toURI(input) + assert(u.toString == "../My%20Documents/test" && IO.toFile(u) == input) + } else () + } + test("it should make URI that roundtrips") { - val u = IO.toURI(file("/etc/hosts").getAbsoluteFile) - assert(IO.toFile(u) == file("/etc/hosts").getAbsoluteFile) + val u = IO.toURI(file("/etc/hosts")) + assert(IO.toFile(u) == file("/etc/hosts")) } test("it should make u0 URI that roundtrips") { @@ -103,6 +111,22 @@ class IOSpec extends FunSuite { assert(IO.toFile(u) == (file("src") / "main" / "scala")) } + test("it should make u3 URI for an absolute path on Windows that roundtrips") { + if (IO.isWindows) { + val input = file("""C:\Documents and Settings\""") + val u = IO.toURI(input) + assert(u.toString == "file:///C:/Documents%20and%20Settings" && IO.toFile(u) == input) + } else () + } + + test("it should make u2 URI for a UNC path on Windows that roundtrips") { + if (IO.isWindows) { + val input = file("""\\laptop\My Documents\Some.doc""") + val u = IO.toURI(input) + assert(u.toString == "file://laptop/My%20Documents/Some.doc" && IO.toFile(u) == input) + } else () + } + test("getModifiedTimeOrZero should return 0L if the file doesn't exists") { assert(IO.getModifiedTimeOrZero(file("/not/existing/path")) == 0L) }