1
0

FilePathParsing.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. /*
  2. This source file is part of the Swift System open source project
  3. Copyright (c) 2020 Apple Inc. and the Swift System project authors
  4. Licensed under Apache License v2.0 with Runtime Library Exception
  5. See https://swift.org/LICENSE.txt for license information
  6. */
  7. // FIXME: Need to rewrite and simplify this code now that SystemString
  8. // manages (and hides) the null terminator
  9. // The separator we use internally
  10. private var genericSeparator: SystemChar { .slash }
  11. // The platform preferred separator
  12. //
  13. // TODO: Make private
  14. internal var platformSeparator: SystemChar {
  15. _windowsPaths ? .backslash : genericSeparator
  16. }
  17. // Whether the character is the canonical separator
  18. // TODO: Make private
  19. internal func isSeparator(_ c: SystemChar) -> Bool {
  20. c == platformSeparator
  21. }
  22. // Whether the character is a pre-normalized separator
  23. internal func isPrenormalSeparator(_ c: SystemChar) -> Bool {
  24. c == genericSeparator || c == platformSeparator
  25. }
  26. // Separator normalization, checking, and root parsing is internally hosted
  27. // on SystemString for ease of unit testing.
  28. extension SystemString {
  29. // For invariant enforcing/checking. Should always return false on
  30. // a fully-formed path
  31. fileprivate func _hasTrailingSeparator() -> Bool {
  32. // Just a root: do nothing
  33. guard _relativePathStart != endIndex else { return false }
  34. assert(!isEmpty)
  35. return isSeparator(self.last!)
  36. }
  37. // Enforce invariants by removing a trailing separator.
  38. //
  39. // Precondition: There is exactly zero or one trailing slashes
  40. //
  41. // Postcondition: Path is root, or has no trailing separator
  42. internal mutating func _removeTrailingSeparator() {
  43. if _hasTrailingSeparator() {
  44. self.removeLast()
  45. assert(!_hasTrailingSeparator())
  46. }
  47. }
  48. // Enforce invariants by normalizing the internal separator representation.
  49. //
  50. // 1) Normalize all separators to platform-preferred separator
  51. // 2) Drop redundant separators
  52. // 3) Drop trailing separators
  53. //
  54. // On Windows, UNC and device paths are allowed to begin with two separators,
  55. // and partial or mal-formed roots are completed.
  56. //
  57. // The POSIX standard does allow two leading separators to
  58. // denote implementation-specific handling, but Darwin and Linux
  59. // do not treat these differently.
  60. //
  61. internal mutating func _normalizeSeparators() {
  62. guard !isEmpty else { return }
  63. var (writeIdx, readIdx) = (startIndex, startIndex)
  64. if _windowsPaths {
  65. // Normalize forwards slashes to backslashes.
  66. //
  67. // NOTE: Ideally this would be done as part of separator coalescing
  68. // below. However, prenormalizing roots such as UNC paths requires
  69. // parsing and (potentially) fixing up semi-formed roots. This
  70. // normalization reduces the complexity of the task by allowing us to
  71. // use a read-only lexer.
  72. self._replaceAll(genericSeparator, with: platformSeparator)
  73. // Windows roots can have meaningful repeated backslashes or may
  74. // need backslashes inserted for partially-formed roots. Delegate that to
  75. // `_prenormalizeWindowsRoots` and resume.
  76. readIdx = _prenormalizeWindowsRoots()
  77. writeIdx = readIdx
  78. // Skip redundant separators
  79. while readIdx < endIndex && isSeparator(self[readIdx]) {
  80. self.formIndex(after: &readIdx)
  81. }
  82. } else {
  83. assert(genericSeparator == platformSeparator)
  84. }
  85. while readIdx < endIndex {
  86. assert(writeIdx <= readIdx)
  87. // Swap and advance our indices.
  88. let wasSeparator = isSeparator(self[readIdx])
  89. self.swapAt(writeIdx, readIdx)
  90. self.formIndex(after: &writeIdx)
  91. self.formIndex(after: &readIdx)
  92. while wasSeparator, readIdx < endIndex, isSeparator(self[readIdx]) {
  93. self.formIndex(after: &readIdx)
  94. }
  95. }
  96. self.removeLast(self.distance(from: writeIdx, to: readIdx))
  97. self._removeTrailingSeparator()
  98. }
  99. }
  100. @available(System 0.0.1, *)
  101. extension FilePath {
  102. internal mutating func _removeTrailingSeparator() {
  103. _storage._removeTrailingSeparator()
  104. }
  105. internal mutating func _normalizeSeparators() {
  106. _storage._normalizeSeparators()
  107. }
  108. // Remove any `.` and `..` components
  109. internal mutating func _normalizeSpecialDirectories() {
  110. guard !isLexicallyNormal else { return }
  111. defer { assert(isLexicallyNormal) }
  112. let relStart = _relativeStart
  113. let hasRoot = relStart != _storage.startIndex
  114. // TODO: all this logic might be nicer if _parseComponent considered
  115. // the null character index to be the next start...
  116. var (writeIdx, readIdx) = (relStart, relStart)
  117. while readIdx < _storage.endIndex {
  118. let (compEnd, nextStart) = _parseComponent(startingAt: readIdx)
  119. assert(readIdx < nextStart && compEnd <= nextStart)
  120. let component = readIdx..<compEnd
  121. // `.` is skipped over
  122. if _isCurrentDirectory(component) {
  123. readIdx = nextStart
  124. continue
  125. }
  126. // `..`s are preserved at the very beginning of a relative path,
  127. // otherwise parse-back a component to remove the parent (but stop at
  128. // root).
  129. if _isParentDirectory(component) {
  130. // Skip over it if we're at the root
  131. if hasRoot && writeIdx == relStart {
  132. readIdx = nextStart
  133. continue
  134. }
  135. // Drop the parent (unless we're preserving `..`s)
  136. if writeIdx != relStart {
  137. let priorComponent = _parseComponent(priorTo: writeIdx)
  138. if !_isParentDirectory(priorComponent) {
  139. writeIdx = priorComponent.lowerBound
  140. readIdx = nextStart
  141. continue
  142. }
  143. assert(self.root == nil && self.components.first!.kind == .parentDirectory)
  144. }
  145. }
  146. if readIdx == writeIdx {
  147. (readIdx, writeIdx) = (nextStart, nextStart)
  148. continue
  149. }
  150. while readIdx != nextStart {
  151. _storage.swapAt(readIdx, writeIdx)
  152. readIdx = _storage.index(after: readIdx)
  153. writeIdx = _storage.index(after: writeIdx)
  154. }
  155. }
  156. assert(readIdx == _storage.endIndex && readIdx >= writeIdx)
  157. if readIdx != writeIdx {
  158. _storage.removeSubrange(writeIdx...)
  159. _removeTrailingSeparator()
  160. }
  161. }
  162. }
  163. extension SystemString {
  164. internal var _relativePathStart: Index {
  165. _parseRoot().relativeBegin
  166. }
  167. }
  168. @available(System 0.0.1, *)
  169. extension FilePath {
  170. internal var _relativeStart: SystemString.Index {
  171. _storage._relativePathStart
  172. }
  173. internal var _hasRoot: Bool {
  174. _relativeStart != _storage.startIndex
  175. }
  176. }
  177. // Parse separators
  178. @available(System 0.0.1, *)
  179. extension FilePath {
  180. internal typealias _Index = SystemString.Index
  181. // Parse a component that starts at `i`. Returns the end
  182. // of the component and the start of the next. Parsing terminates
  183. // at the index of the null byte.
  184. internal func _parseComponent(
  185. startingAt i: _Index
  186. ) -> (componentEnd: _Index, nextStart: _Index) {
  187. assert(i < _storage.endIndex)
  188. // Parse the root
  189. if i == _storage.startIndex {
  190. let relativeStart = _relativeStart
  191. if i != relativeStart {
  192. return (relativeStart, relativeStart)
  193. }
  194. }
  195. assert(!isSeparator(_storage[i]))
  196. guard let nextSep = _storage[i...].firstIndex(where: isSeparator) else {
  197. return (_storage.endIndex, _storage.endIndex)
  198. }
  199. return (nextSep, _storage.index(after: nextSep))
  200. }
  201. // Parse a component prior to the one that starts at `i`. Returns
  202. // the start of the prior component. If `i` is the index of null,
  203. // returns the last component.
  204. internal func _parseComponent(
  205. priorTo i: _Index
  206. ) -> Range<_Index> {
  207. precondition(i > _storage.startIndex)
  208. let relStart = _relativeStart
  209. if i == relStart { return _storage.startIndex..<relStart }
  210. assert(i > relStart)
  211. var slice = _storage[..<i]
  212. if i != _storage.endIndex {
  213. assert(isSeparator(slice.last!))
  214. slice.removeLast()
  215. }
  216. let end = slice.endIndex
  217. while slice.endIndex != relStart, let c = slice.last, !isSeparator(c) {
  218. slice.removeLast()
  219. }
  220. return slice.endIndex ..< end
  221. }
  222. internal func _isCurrentDirectory(_ component: Range<_Index>) -> Bool {
  223. _storage[component].elementsEqual([.dot])
  224. }
  225. internal func _isParentDirectory(_ component: Range<_Index>) -> Bool {
  226. _storage[component].elementsEqual([.dot, .dot])
  227. }
  228. internal func _isSpecialDirectory(_ component: Range<_Index>) -> Bool {
  229. _isCurrentDirectory(component) || _isParentDirectory(component)
  230. }
  231. }
  232. @available(System 0.0.2, *)
  233. extension FilePath.ComponentView {
  234. // TODO: Store this...
  235. internal var _relativeStart: SystemString.Index {
  236. _path._relativeStart
  237. }
  238. }
  239. extension SystemString {
  240. internal func _parseRoot() -> (
  241. rootEnd: Index, relativeBegin: Index
  242. ) {
  243. guard !isEmpty else { return (startIndex, startIndex) }
  244. // Windows roots are more complex
  245. if _windowsPaths { return _parseWindowsRoot() }
  246. // A leading `/` is a root
  247. guard isSeparator(self.first!) else { return (startIndex, startIndex) }
  248. let next = self.index(after: startIndex)
  249. return (next, next)
  250. }
  251. }
  252. @available(System 0.0.2, *)
  253. extension FilePath.Root {
  254. // Asserting self is a root, returns whether this is an
  255. // absolute root.
  256. //
  257. // On Unix, all roots are absolute. On Windows, `\` and `X:` are
  258. // relative roots
  259. //
  260. // TODO: public
  261. internal var isAbsolute: Bool {
  262. assert(FilePath(SystemString(self._slice)).root == self, "not a root")
  263. guard _windowsPaths else { return true }
  264. // `\` or `C:` are the only form of relative roots, and all
  265. // absolute roots are at least 3 chars long.
  266. let slice = self._slice
  267. guard slice.count < 3 else { return true }
  268. assert(
  269. (slice.count == 1 && slice.first == .backslash) ||
  270. (slice.count == 2 && slice.last == .colon))
  271. return false
  272. }
  273. }
  274. @available(System 0.0.1, *)
  275. extension FilePath {
  276. internal var _portableDescription: String {
  277. guard _windowsPaths else { return description }
  278. let utf8 = description.utf8.map { $0 == UInt8(ascii: #"\"#) ? UInt8(ascii: "/") : $0 }
  279. return String(decoding: utf8, as: UTF8.self)
  280. }
  281. }
  282. // Whether we are providing Windows paths
  283. @inline(__always)
  284. internal var _windowsPaths: Bool {
  285. if let forceWindowsPaths = forceWindowsPaths {
  286. return forceWindowsPaths
  287. }
  288. #if os(Windows)
  289. return true
  290. #else
  291. return false
  292. #endif
  293. }
  294. @available(System 0.0.1, *)
  295. extension FilePath {
  296. // Whether we should add a separator when doing an append
  297. internal var _needsSeparatorForAppend: Bool {
  298. guard let last = _storage.last, !isSeparator(last) else { return false }
  299. // On Windows, we can have a path of the form `C:` which is a root and
  300. // does not need a separator after it
  301. if _windowsPaths && _relativeStart == _storage.endIndex {
  302. return false
  303. }
  304. return true
  305. }
  306. // Perform an append, inseting a separator if needed.
  307. // Note that this will not check whether `content` is a root
  308. internal mutating func _append(unchecked content: Slice<SystemString>) {
  309. assert(FilePath(SystemString(content)).root == nil)
  310. if content.isEmpty { return }
  311. if _needsSeparatorForAppend {
  312. _storage.append(platformSeparator)
  313. }
  314. _storage.append(contentsOf: content)
  315. }
  316. }
  317. // MARK: - Invariants
  318. @available(System 0.0.1, *)
  319. extension FilePath {
  320. internal func _invariantsSatisfied() -> Bool {
  321. var normal = self
  322. normal._normalizeSeparators()
  323. guard self == normal else { return false }
  324. guard !self._storage._hasTrailingSeparator() else { return false }
  325. guard _hasRoot == (self.root != nil) else { return false }
  326. return true
  327. }
  328. internal func _invariantCheck() {
  329. #if DEBUG
  330. precondition(_invariantsSatisfied())
  331. #endif // DEBUG
  332. }
  333. }