Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix for issue-201 #202

Merged
merged 11 commits into from
Sep 12, 2023
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ target/
.project
.cache
.sbtserver
.scala-build/
.bsp/
project/.sbtserver
tags
nohup.out
out
lowered.hnir

# ignore vim swap files
*.sw[op]
35 changes: 27 additions & 8 deletions os/src/Path.scala
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,12 @@ sealed trait FilePath extends BasePath {
def toNIO: java.nio.file.Path
def resolveFrom(base: os.Path): os.Path
}

object FilePath {
def apply[T: PathConvertible](f0: T) = {
val f = implicitly[PathConvertible[T]].apply(f0)
if (f.isAbsolute) Path(f0)
def f = implicitly[PathConvertible[T]].apply(f0)
// if Windows semi-absolute path, convert it to an absolute path
if (Path.rootRelative(f0) || f.isAbsolute) Path(f0)
else {
val r = RelPath(f0)
if (r.ups == 0) r.asSubPath
Expand Down Expand Up @@ -297,7 +299,7 @@ object RelPath {
def apply[T: PathConvertible](f0: T): RelPath = {
val f = implicitly[PathConvertible[T]].apply(f0)

require(!f.isAbsolute, s"$f is not a relative path")
require(!f.isAbsolute && !Path.rootRelative(f0), s"$f is not a relative path")

val segments = BasePath.chunkify(f.normalize())
val (ups, rest) = segments.partition(_ == "..")
Expand Down Expand Up @@ -399,11 +401,14 @@ object Path {
def apply[T: PathConvertible](f: T, base: Path): Path = apply(FilePath(f), base)
def apply[T: PathConvertible](f0: T): Path = {
val f = implicitly[PathConvertible[T]].apply(f0)
if (f.iterator.asScala.count(_.startsWith("..")) > f.getNameCount / 2) {
throw PathError.AbsolutePathOutsideRoot
def nioPath = java.nio.file.Paths.get(s"$currentDrive/$f0").normalize
val normalized = if (rootRelative(f0)) nioPath
else {
if (f.iterator.asScala.count(_.startsWith("..")) > f.getNameCount / 2) {
throw PathError.AbsolutePathOutsideRoot
}
f.normalize()
}

val normalized = f.normalize()
new Path(normalized)
}

Expand Down Expand Up @@ -435,7 +440,21 @@ object Path {
}
}
}

def rootRelative[T: PathConvertible](f0: T): Boolean = {
if (currentWorkingDrive.isEmpty) {
false // non-Windows os
} else {
f0.toString.take(1) match {
case "\\" | "/" => true
case _ => false
}
}
}
def currentWorkingDrive: String = Paths.get("").toAbsolutePath.getRoot.toString match {
case "/" => ""
case s => s"$s/"
}
def currentDrive: Path = Path(currentWorkingDrive)
}

trait ReadablePath {
Expand Down
49 changes: 26 additions & 23 deletions os/test/src/PathTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ import os._
import utest.{assert => _, _}
import java.net.URI
object PathTests extends TestSuite {
val wintest = Option(System.getenv("WINPATHTEST")).nonEmpty
def enableTest = Unix() || wintest
val tests = Tests {
test("Basic") {
val base = rel / "src" / "main" / "scala"
val subBase = sub / "src" / "main" / "scala"
def norm(s: String) = s.replace('\\', '/')
test("Transformers") {
if (Unix()) {
if (enableTest) {
// os.Path to java.nio.file.Path
assert((root / "omg").wrapped == Paths.get("/omg"))

Expand Down Expand Up @@ -84,7 +87,7 @@ object PathTests extends TestSuite {
test("RelPath") {
test("Constructors") {
test("Symbol") {
if (Unix()) {
if (enableTest) {
val rel1 = base / "ammonite"
assert(
rel1.segments == Seq("src", "main", "scala", "ammonite"),
Expand All @@ -93,7 +96,7 @@ object PathTests extends TestSuite {
}
}
test("String") {
if (Unix()) {
if (enableTest) {
val rel1 = base / "Path.scala"
assert(
rel1.segments == Seq("src", "main", "scala", "Path.scala"),
Expand All @@ -107,25 +110,25 @@ object PathTests extends TestSuite {
rel1.toString == "src/main/scala/sub1/sub2"
)
test("ArrayString") {
if (Unix()) {
if (enableTest) {
val arr = Array("sub1", "sub2")
check(base / arr)
}
}
test("ArraySymbol") {
if (Unix()) {
if (enableTest) {
val arr = Array("sub1", "sub2")
check(base / arr)
}
}
test("SeqString") {
if (Unix()) check(base / Seq("sub1", "sub2"))
if (enableTest) check(base / Seq("sub1", "sub2"))
}
test("SeqSymbol") {
if (Unix()) check(base / Seq("sub1", "sub2"))
if (enableTest) check(base / Seq("sub1", "sub2"))
}
test("SeqSeqSeqSymbol") {
if (Unix()) {
if (enableTest) {
check(
base / Seq(Seq(Seq("sub1"), Seq()), Seq(Seq("sub2")), Seq())
)
Expand All @@ -148,7 +151,7 @@ object PathTests extends TestSuite {
test("SubPath") {
test("Constructors") {
test("Symbol") {
if (Unix()) {
if (enableTest) {
val rel1 = subBase / "ammonite"
assert(
rel1.segments == Seq("src", "main", "scala", "ammonite"),
Expand All @@ -157,7 +160,7 @@ object PathTests extends TestSuite {
}
}
test("String") {
if (Unix()) {
if (enableTest) {
val rel1 = subBase / "Path.scala"
assert(
rel1.segments == Seq("src", "main", "scala", "Path.scala"),
Expand All @@ -171,25 +174,25 @@ object PathTests extends TestSuite {
rel1.toString == "src/main/scala/sub1/sub2"
)
test("ArrayString") {
if (Unix()) {
if (enableTest) {
val arr = Array("sub1", "sub2")
check(subBase / arr)
}
}
test("ArraySymbol") {
if (Unix()) {
if (enableTest) {
val arr = Array("sub1", "sub2")
check(subBase / arr)
}
}
test("SeqString") {
if (Unix()) check(subBase / Seq("sub1", "sub2"))
if (enableTest) check(subBase / Seq("sub1", "sub2"))
}
test("SeqSymbol") {
if (Unix()) check(subBase / Seq("sub1", "sub2"))
if (enableTest) check(subBase / Seq("sub1", "sub2"))
}
test("SeqSeqSeqSymbol") {
if (Unix()) {
if (enableTest) {
check(
subBase / Seq(Seq(Seq("sub1"), Seq()), Seq(Seq("sub2")), Seq())
)
Expand All @@ -210,8 +213,8 @@ object PathTests extends TestSuite {
val d = pwd
val abs = d / base
test("Constructor") {
if (Unix()) assert(
abs.toString.drop(d.toString.length) == "/src/main/scala",
if (enableTest) assert(
norm(abs.toString.drop(d.toString.length)) == "/src/main/scala",
abs.toString.length > d.toString.length
)
}
Expand Down Expand Up @@ -307,7 +310,7 @@ object PathTests extends TestSuite {
intercept[PathError.InvalidSegment](rel / "src" / "..")
}
test("CannotRelativizeAbsAndRel") {
if (Unix()) {
if (enableTest) {
val abs = pwd
val rel = os.rel / "omg" / "wtf"
compileError("""
Expand All @@ -319,7 +322,7 @@ object PathTests extends TestSuite {
}
}
test("InvalidCasts") {
if (Unix()) {
if (enableTest) {
intercept[IllegalArgumentException](Path("omg/cow"))
intercept[IllegalArgumentException](RelPath("/omg/cow"))
}
Expand Down Expand Up @@ -366,7 +369,7 @@ object PathTests extends TestSuite {
}
test("construction") {
test("success") {
if (Unix()) {
if (enableTest) {
val relStr = "hello/cow/world/.."
val absStr = "/hello/world"

Expand All @@ -390,7 +393,7 @@ object PathTests extends TestSuite {
}
}
test("basepath") {
if (Unix()) {
if (enableTest) {
val relStr = "hello/cow/world/.."
val absStr = "/hello/world"
assert(
Expand All @@ -400,7 +403,7 @@ object PathTests extends TestSuite {
}
}
test("based") {
if (Unix()) {
if (enableTest) {
val relStr = "hello/cow/world/.."
val absStr = "/hello/world"
val basePath: FilePath = FilePath(relStr)
Expand All @@ -411,7 +414,7 @@ object PathTests extends TestSuite {
}
}
test("failure") {
if (Unix()) {
if (enableTest) {
val relStr = "hello/.."
intercept[java.lang.IllegalArgumentException] {
Path(relStr)
Expand Down