in Sources/TSCBasic/Path.swift [618:691]
init(normalizingRelativePath path: String) {
#if os(Windows)
if path.isEmpty || path == "." {
self.init(string: ".")
} else {
var buffer: [WCHAR] = Array<WCHAR>(repeating: 0, count: Int(MAX_PATH + 1))
_ = path.replacingOccurrences(of: "/", with: "\\").withCString(encodedAs: UTF16.self) {
PathCanonicalizeW(&buffer, $0)
}
self.init(string: String(decodingCString: buffer, as: UTF16.self))
}
#else
precondition(path.first != "/")
// FIXME: Here we should also keep track of whether anything actually has
// to be changed in the string, and if not, just return the existing one.
// Split the character array into parts, folding components as we go.
// As we do so, we count the number of characters we'll end up with in
// the normalized string representation.
var parts: [String] = []
var capacity = 0
for part in path.split(separator: "/") {
switch part.count {
case 0:
// Ignore empty path components.
continue
case 1 where part.first == ".":
// Ignore `.` path components.
continue
case 2 where part.first == "." && part.last == ".":
// If at beginning, fall through to treat the `..` literally.
guard let prev = parts.last else {
fallthrough
}
// If previous component is anything other than `..`, drop it.
if !(prev.count == 2 && prev.first == "." && prev.last == ".") {
parts.removeLast()
capacity -= prev.count
continue
}
// Otherwise, fall through to treat the `..` literally.
fallthrough
default:
// Any other component gets appended.
parts.append(String(part))
capacity += part.count
}
}
capacity += max(parts.count - 1, 0)
// Create an output buffer using the capacity we've calculated.
// FIXME: Determine the most efficient way to reassemble a string.
var result = ""
result.reserveCapacity(capacity)
// Put the normalized parts back together again.
var iter = parts.makeIterator()
if let first = iter.next() {
result.append(contentsOf: first)
while let next = iter.next() {
result.append("/")
result.append(contentsOf: next)
}
}
// Sanity-check the result (including the capacity we reserved).
assert(result.count == capacity, "count: " +
"\(result.count), cap: \(capacity)")
// If the result is empty, return `.`, otherwise we return it as a string.
self.init(string: result.isEmpty ? "." : result)
#endif
}