@@ -40,6 +40,12 @@ public expect class Path {
40
40
*/
41
41
public val isAbsolute: Boolean
42
42
43
+ /* *
44
+ * Returns normalized version of this path where all `..` and `.` segments are resolved
45
+ * and all sequential path separators are collapsed.
46
+ */
47
+ public fun normalized (): Path
48
+
43
49
/* *
44
50
* Returns a string representation of this path.
45
51
*
@@ -174,3 +180,51 @@ private fun removeTrailingSeparatorsWindows(suffixLength: Int, path: String): St
174
180
}
175
181
return path.substring(0 , idx)
176
182
}
183
+
184
+ internal fun Path.normalizedInternal (preserveDrive : Boolean , vararg separators : Char ): String {
185
+ var isAbs = isAbsolute
186
+ var stringRepresentation = toString()
187
+ var drive = " "
188
+ if (preserveDrive && stringRepresentation.length >= 2 && stringRepresentation[1 ] == ' :' ) {
189
+ drive = stringRepresentation.substring(0 , 2 )
190
+ stringRepresentation = stringRepresentation.substring(2 )
191
+ isAbs = stringRepresentation.isNotEmpty() && separators.contains(stringRepresentation.first())
192
+ }
193
+ val parts = stringRepresentation.split(* separators)
194
+ val constructedPath = mutableListOf<String >()
195
+ for (idx in parts.indices) {
196
+ when (val part = parts[idx]) {
197
+ " ." -> continue
198
+ " .." -> if (isAbs) {
199
+ constructedPath.removeLastOrNull()
200
+ } else {
201
+ if (constructedPath.isEmpty() || constructedPath.last() == " .." ) {
202
+ constructedPath.add(" .." )
203
+ } else {
204
+ constructedPath.removeLast()
205
+ }
206
+ }
207
+
208
+ else -> {
209
+ if (part.isNotEmpty()) {
210
+ constructedPath.add(part)
211
+ }
212
+ }
213
+ }
214
+ }
215
+ return buildString {
216
+ append(drive)
217
+ var skipFirstSeparator = true
218
+ if (isAbs) {
219
+ append(SystemPathSeparator )
220
+ }
221
+ for (segment in constructedPath) {
222
+ if (skipFirstSeparator) {
223
+ skipFirstSeparator = false
224
+ } else {
225
+ append(SystemPathSeparator )
226
+ }
227
+ append(segment)
228
+ }
229
+ }
230
+ }
0 commit comments