Skip to content

Commit

Permalink
Mask colons in segment paths (#2965)
Browse files Browse the repository at this point in the history
Again a Windows thing, but the evaluator paths should not contain
colons. We mask them with `$colon`.

Fix #2923

Pull request: #2965
  • Loading branch information
lefou authored Jan 23, 2024
1 parent 4e534ef commit 8902251
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 4 deletions.
23 changes: 20 additions & 3 deletions main/eval/src/mill/eval/EvaluatorPaths.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package mill.eval
import mill.api.internal
import mill.define.{NamedTask, Segment, Segments}

import java.util.regex.Matcher

case class EvaluatorPaths private (dest: os.Path, meta: os.Path, log: os.Path) {
// scalafix:off; we want to hide the generic copy method
private def copy(dest: os.Path = dest, meta: os.Path = meta, log: os.Path = log): EvaluatorPaths =
Expand Down Expand Up @@ -47,10 +49,25 @@ object EvaluatorPaths {
// case-insensitive match on reserved names
private val ReservedWinNames =
raw"^([cC][oO][nN]|[pP][rR][nN]|[aA][uU][xX]|[nN][uU][lL]|[cC][oO][mM][0-9¹²³]|[lL][pP][tT][0-9¹²³])($$|[.].*$$)".r
def sanitizePathSegment(segment: String): os.PathChunk = {
segment match {
// Colons are not supported on Windows
private val Colon = "[:]".r
// Dollar sign `$` is our masking-character
private val Dollar = "[$]".r

private val steps: Seq[String => String] = Seq(
// Step 1: mask all existing dollar signs, so we can use the dollar as masking character
s => Dollar.replaceAllIn(s, Matcher.quoteReplacement("$$")),
// Step 2: mask reserved Windows names, like CON1 or LPT1
_ match {
case ReservedWinNames(keyword, rest) => s"${keyword}~${rest}"
case s => s
}
},
// Step 3: Replace colon (:) with $colon
s => Colon.replaceAllIn(s, Matcher.quoteReplacement("$colon"))
)

def sanitizePathSegment(segment: String): os.PathChunk = {
// sanitize and implicitly convert to PathChunk
steps.foldLeft(segment) { (segment, f) => f(segment) }
}
}
9 changes: 8 additions & 1 deletion main/eval/test/src/mill/eval/EvaluatorPathsTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,15 @@ object EvaluatorPathsTests extends TestSuite {
"sanitizedPathSegment" - {
"WindowsReservedNames" - {
val replace = Seq(
// reserved file names under Windows
"com1.json" -> "com1~.json",
"LPT¹" -> "LPT¹~"
"LPT¹" -> "LPT¹~",
// a unsupported character under Windows
"a:b" -> "a$colonb",
// do not collide with the applied `$`-masking character
"a$colonb" -> "a$$colonb",
// replace not just the first $
"a$$b" -> "a$$$$b"
)
val noReplace = Seq(
"con10.json"
Expand Down

0 comments on commit 8902251

Please # to comment.