Skip to content

Commit 7436264

Browse files
kcieplakbnbarham
authored andcommitted
Revert "Resolves #505 - Fix handling for Windows long paths (#506)"
This reverts commit eb4c83d.
1 parent eb4c83d commit 7436264

File tree

4 files changed

+67
-793
lines changed

4 files changed

+67
-793
lines changed

Sources/TSCBasic/CMakeLists.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ add_library(TSCBasic
4949
TerminalController.swift
5050
Thread.swift
5151
Tuple.swift
52-
misc.swift
53-
Win32Error.swift)
52+
misc.swift)
5453

5554
target_compile_options(TSCBasic PUBLIC
5655
# Ignore secure function warnings on Windows.

Sources/TSCBasic/Path.swift

Lines changed: 15 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
4+
Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See http://swift.org/LICENSE.txt for license information
@@ -32,7 +32,6 @@ import var Foundation.NSLocalizedDescriptionKey
3232
/// - Removing `.` path components
3333
/// - Removing any trailing path separator
3434
/// - Removing any redundant path separators
35-
/// - Converting the disk designator to uppercase (Windows) i.e. c:\ to C:\
3635
///
3736
/// This string manipulation may change the meaning of a path if any of the
3837
/// path components are symbolic links on disk. However, the file system is
@@ -507,30 +506,21 @@ private struct WindowsPath: Path, Sendable {
507506
var components: [String] {
508507
let normalized: UnsafePointer<Int8> = string.fileSystemRepresentation
509508
defer { normalized.deallocate() }
510-
// Remove prefix from the components, allowing for comparison across normalized paths.
511-
var prefixStrippedPath = PathCchStripPrefix(String(cString: normalized))
512-
// The '\\.\'' prefix is not removed by PathCchStripPrefix do this manually.
513-
if prefixStrippedPath.starts(with: #"\\.\"#) {
514-
prefixStrippedPath = String(prefixStrippedPath.dropFirst(4))
515-
}
516-
return prefixStrippedPath.components(separatedBy: #"\"#).filter { !$0.isEmpty }
509+
510+
return String(cString: normalized).components(separatedBy: "\\").filter { !$0.isEmpty }
517511
}
518512

519513
var parentDirectory: Self {
520514
return self == .root ? self : Self(string: dirname)
521515
}
522516

523517
init(string: String) {
524-
let noPrefixPath = PathCchStripPrefix(string)
525-
let prefix = string.replacingOccurrences(of: noPrefixPath, with: "") // Just the prefix or empty
526-
527-
// Perform drive designator normalization i.e. 'c:\' to 'C:\' on string.
528-
if noPrefixPath.first?.isASCII ?? false, noPrefixPath.first?.isLetter ?? false, noPrefixPath.first?.isLowercase ?? false,
529-
noPrefixPath.count > 1, noPrefixPath[noPrefixPath.index(noPrefixPath.startIndex, offsetBy: 1)] == ":"
518+
if string.first?.isASCII ?? false, string.first?.isLetter ?? false, string.first?.isLowercase ?? false,
519+
string.count > 1, string[string.index(string.startIndex, offsetBy: 1)] == ":"
530520
{
531-
self.string = "\(prefix)\(noPrefixPath.first!.uppercased())\(noPrefixPath.dropFirst(1))"
521+
self.string = "\(string.first!.uppercased())\(string.dropFirst(1))"
532522
} else {
533-
self.string = prefix + noPrefixPath
523+
self.string = string
534524
}
535525
}
536526

@@ -546,13 +536,7 @@ private struct WindowsPath: Path, Sendable {
546536
if !Self.isAbsolutePath(realpath) {
547537
throw PathValidationError.invalidAbsolutePath(path)
548538
}
549-
do {
550-
let canonicalizedPath = try canonicalPathRepresentation(realpath)
551-
let normalizedPath = PathCchRemoveBackslash(canonicalizedPath) // AbsolutePath states paths have no trailing separator.
552-
self.init(string: normalizedPath)
553-
} catch {
554-
throw PathValidationError.invalidAbsolutePath("\(path): \(error)")
555-
}
539+
self.init(string: realpath)
556540
}
557541

558542
init(validatingRelativePath path: String) throws {
@@ -570,20 +554,12 @@ private struct WindowsPath: Path, Sendable {
570554

571555
func suffix(withDot: Bool) -> String? {
572556
return self.string.withCString(encodedAs: UTF16.self) {
573-
if let dotPointer = PathFindExtensionW($0) {
574-
// If the dotPointer is the same as the full path, there are no components before
575-
// the suffix and therefore there is no suffix.
576-
if dotPointer == $0 {
577-
return nil
578-
}
579-
let substring = String(decodingCString: dotPointer, as: UTF16.self)
580-
// Substring must have a dot and one more character to be considered a suffix
581-
guard substring.length > 1 else {
582-
return nil
583-
}
584-
return withDot ? substring : String(substring.dropFirst(1))
585-
}
586-
return nil
557+
if let pointer = PathFindExtensionW($0) {
558+
let substring = String(decodingCString: pointer, as: UTF16.self)
559+
guard substring.length > 0 else { return nil }
560+
return withDot ? substring : String(substring.dropFirst(1))
561+
}
562+
return nil
587563
}
588564
}
589565

@@ -609,111 +585,6 @@ private struct WindowsPath: Path, Sendable {
609585
return Self(string: String(decodingCString: result!, as: UTF16.self))
610586
}
611587
}
612-
613-
fileprivate func HRESULT_CODE(_ hr: HRESULT) -> DWORD {
614-
DWORD(hr) & 0xFFFF
615-
}
616-
617-
@inline(__always)
618-
fileprivate func HRESULT_FACILITY(_ hr: HRESULT) -> DWORD {
619-
DWORD(hr << 16) & 0x1FFF
620-
}
621-
622-
@inline(__always)
623-
fileprivate func SUCCEEDED(_ hr: HRESULT) -> Bool {
624-
hr >= 0
625-
}
626-
627-
// This is a non-standard extension to the Windows SDK that allows us to convert
628-
// an HRESULT to a Win32 error code.
629-
@inline(__always)
630-
fileprivate func WIN32_FROM_HRESULT(_ hr: HRESULT) -> DWORD {
631-
if SUCCEEDED(hr) { return DWORD(ERROR_SUCCESS) }
632-
if HRESULT_FACILITY(hr) == FACILITY_WIN32 {
633-
return HRESULT_CODE(hr)
634-
}
635-
return DWORD(hr)
636-
}
637-
638-
/// Create a canonicalized path representation for Windows.
639-
/// Returns a potentially `\\?\`-prefixed version of the path,
640-
/// to ensure long paths greater than MAX_PATH (260) characters are handled correctly.
641-
///
642-
/// - seealso: https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
643-
fileprivate func canonicalPathRepresentation(_ path: String) throws -> String {
644-
return try path.withCString(encodedAs: UTF16.self) { pwszPlatformPath in
645-
// 1. Normalize the path first.
646-
// Contrary to the documentation, this works on long paths independently
647-
// of the registry or process setting to enable long paths (but it will also
648-
// not add the \\?\ prefix required by other functions under these conditions).
649-
let dwLength: DWORD = GetFullPathNameW(pwszPlatformPath, 0, nil, nil)
650-
651-
return try withUnsafeTemporaryAllocation(of: WCHAR.self, capacity: Int(dwLength)) { pwszFullPath in
652-
guard (1 ..< dwLength).contains(GetFullPathNameW(pwszPlatformPath, DWORD(pwszFullPath.count), pwszFullPath.baseAddress, nil)) else {
653-
throw Win32Error(GetLastError())
654-
}
655-
// 1.5 Leave \\.\ prefixed paths alone since device paths are already an exact representation and PathCchCanonicalizeEx will mangle these.
656-
if pwszFullPath.count >= 4 {
657-
if let base = pwszFullPath.baseAddress,
658-
base[0] == UInt8(ascii: "\\"),
659-
base[1] == UInt8(ascii: "\\"),
660-
base[2] == UInt8(ascii: "."),
661-
base[3] == UInt8(ascii: "\\")
662-
{
663-
return String(decodingCString: base, as: UTF16.self)
664-
}
665-
}
666-
// 2. Canonicalize the path.
667-
// This will add the \\?\ prefix if needed based on the path's length.
668-
var pwszCanonicalPath: LPWSTR?
669-
let flags: ULONG = numericCast(PATHCCH_ALLOW_LONG_PATHS.rawValue)
670-
let result = PathAllocCanonicalize(pwszFullPath.baseAddress, flags, &pwszCanonicalPath)
671-
if let pwszCanonicalPath {
672-
defer { LocalFree(pwszCanonicalPath) }
673-
if result == S_OK {
674-
// 3. Perform the operation on the normalized path.
675-
return String(decodingCString: pwszCanonicalPath, as: UTF16.self)
676-
}
677-
}
678-
throw Win32Error(WIN32_FROM_HRESULT(result))
679-
}
680-
}
681-
}
682-
683-
/// Removes the "\\?\" prefix, if present, from a file path. When this function returns successfully,
684-
/// the same path string will have the prefix removed,if the prefix was present.
685-
/// If no prefix was present,the string will be unchanged.
686-
fileprivate func PathCchStripPrefix(_ path: String) -> String {
687-
return path.withCString(encodedAs: UTF16.self) { cStringPtr in
688-
withUnsafeTemporaryAllocation(of: WCHAR.self, capacity: path.utf16.count + 1) { buffer in
689-
buffer.initialize(from: UnsafeBufferPointer(start: cStringPtr, count: path.utf16.count + 1))
690-
let result = PathCchStripPrefix(buffer.baseAddress!, buffer.count)
691-
if result == S_OK {
692-
return String(decodingCString: buffer.baseAddress!, as: UTF16.self)
693-
}
694-
return path
695-
}
696-
}
697-
}
698-
699-
/// Remove a trailing backslash from a path if the following conditions
700-
/// are true:
701-
/// * Path is not a root path
702-
/// * Pash has a trailing backslash
703-
/// If conditions are not met then the string is returned unchanged.
704-
fileprivate func PathCchRemoveBackslash(_ path: String) -> String {
705-
return path.withCString(encodedAs: UTF16.self) { cStringPtr in
706-
return withUnsafeTemporaryAllocation(of: WCHAR.self, capacity: path.utf16.count + 1) { buffer in
707-
buffer.initialize(from: UnsafeBufferPointer(start: cStringPtr, count: path.utf16.count + 1))
708-
let result = PathCchRemoveBackslash(buffer.baseAddress!, path.utf16.count + 1)
709-
if result == S_OK {
710-
return String(decodingCString: buffer.baseAddress!, as: UTF16.self)
711-
}
712-
return path
713-
}
714-
return path
715-
}
716-
}
717588
#else
718589
private struct UNIXPath: Path, Sendable {
719590
let string: String
@@ -1095,8 +966,7 @@ extension AbsolutePath {
1095966
}
1096967
}
1097968

1098-
assert(AbsolutePath(base, result) == self, "base:\(base) result:\(result) self: \(self)")
1099-
969+
assert(AbsolutePath(base, result) == self)
1100970
return result
1101971
}
1102972

Sources/TSCBasic/Win32Error.swift

Lines changed: 0 additions & 37 deletions
This file was deleted.

0 commit comments

Comments
 (0)