diff --git a/Sources/TSCBasic/FileSystem.swift b/Sources/TSCBasic/FileSystem.swift index 69e39e89..af779e42 100644 --- a/Sources/TSCBasic/FileSystem.swift +++ b/Sources/TSCBasic/FileSystem.swift @@ -533,7 +533,7 @@ private struct LocalFileSystem: FileSystem { let fsr: UnsafePointer = cwdStr.fileSystemRepresentation defer { fsr.deallocate() } - return try? AbsolutePath(String(cString: fsr)) + return try? AbsolutePath(validating: String(cString: fsr)) #endif } diff --git a/Sources/TSCBasic/Path.swift b/Sources/TSCBasic/Path.swift index 208f9819..d7de5d94 100644 --- a/Sources/TSCBasic/Path.swift +++ b/Sources/TSCBasic/Path.swift @@ -88,7 +88,7 @@ public struct AbsolutePath: Hashable, Sendable { } defer { LocalFree(pwszResult) } - self.init(String(decodingCString: pwszResult, as: UTF16.self)) + try self.init(validating: String(decodingCString: pwszResult, as: UTF16.self)) #else try self.init(basePath, RelativePath(validating: str)) #endif @@ -515,12 +515,29 @@ private struct WindowsPath: Path, Sendable { } init(string: String) { - if string.first?.isASCII ?? false, string.first?.isLetter ?? false, string.first?.isLowercase ?? false, + let path: String + let hasDrive: Bool + if string.first?.isASCII ?? false, string.first?.isLetter ?? false, string.count > 1, string[string.index(string.startIndex, offsetBy: 1)] == ":" { - self.string = "\(string.first!.uppercased())\(string.dropFirst(1))" + hasDrive = true + path = "\(string.first!.uppercased())\(string.dropFirst(1))" } else { - self.string = string + hasDrive = false + path = string + } + var droppedTailing: Bool = false + // There seems to be many assumptions around paths and trailing '\' + var substring = path[path.startIndex..: left + if hasDrive && substring.count == 2 && droppedTailing { + self.string = Self.repr(path) + "\\" + } else { + self.string = Self.repr(String(substring)) } } @@ -544,7 +561,7 @@ private struct WindowsPath: Path, Sendable { self.init(string: ".") } else { let realpath: String = Self.repr(path) - // Treat a relative path as an invalid relative path... + // Treat an absolute path as an invalid relative path if Self.isAbsolutePath(realpath) || realpath.first == "\\" { throw PathValidationError.invalidRelativePath(path) } @@ -568,6 +585,7 @@ private struct WindowsPath: Path, Sendable { _ = string.withCString(encodedAs: UTF16.self) { root in name.withCString(encodedAs: UTF16.self) { path in PathAllocCombine(root, path, ULONG(PATHCCH_ALLOW_LONG_PATHS.rawValue), &result) + _ = PathCchStripPrefix(result, wcslen(result)) } } defer { LocalFree(result) } @@ -579,6 +597,7 @@ private struct WindowsPath: Path, Sendable { _ = string.withCString(encodedAs: UTF16.self) { root in relativePath.string.withCString(encodedAs: UTF16.self) { path in PathAllocCombine(root, path, ULONG(PATHCCH_ALLOW_LONG_PATHS.rawValue), &result) + _ = PathCchStripPrefix(result, wcslen(result)) } } defer { LocalFree(result) } @@ -965,8 +984,7 @@ extension AbsolutePath { preconditionFailure("invalid relative path computed from \(pathString)") } } - - assert(AbsolutePath(base, result) == self) + assert(AbsolutePath(base, result) == self, "\(AbsolutePath(base, result)) != \(self)") return result } diff --git a/Sources/TSCBasic/PathShims.swift b/Sources/TSCBasic/PathShims.swift index 8851751a..d08026af 100644 --- a/Sources/TSCBasic/PathShims.swift +++ b/Sources/TSCBasic/PathShims.swift @@ -44,7 +44,7 @@ public func resolveSymlinks(_ path: AbsolutePath) throws -> AbsolutePath { } else { pathBaseAddress = UnsafePointer($0.baseAddress!) } - return try AbsolutePath(String(decodingCString: pathBaseAddress, as: UTF16.self)) + return try AbsolutePath(validating: String(decodingCString: pathBaseAddress, as: UTF16.self)) } #else let pathStr = path.pathString diff --git a/Sources/TSCUtility/FSWatch.swift b/Sources/TSCUtility/FSWatch.swift index 1987e4e0..e410f717 100644 --- a/Sources/TSCUtility/FSWatch.swift +++ b/Sources/TSCUtility/FSWatch.swift @@ -250,8 +250,9 @@ public final class RDCWatcher { } if !GetOverlappedResult(watch.hDirectory, &watch.overlapped, &dwBytesReturned, false) { + guard let path = try? AbsolutePath(validating: watch.path) else { continue } queue.async { - delegate?.pathsDidReceiveEvent([AbsolutePath(watch.path)]) + delegate?.pathsDidReceiveEvent([path]) } return } @@ -272,7 +273,8 @@ public final class RDCWatcher { String(utf16CodeUnitsNoCopy: &pNotify.pointee.FileName, count: Int(pNotify.pointee.FileNameLength) / MemoryLayout.stride, freeWhenDone: false) - paths.append(AbsolutePath(file)) + guard let path = try? AbsolutePath(validating: file) else { continue } + paths.append(path) pNotify = (UnsafeMutableRawPointer(pNotify) + Int(pNotify.pointee.NextEntryOffset)) .assumingMemoryBound(to: FILE_NOTIFY_INFORMATION.self)