Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 0 additions & 156 deletions Sources/BashInterpreter/API/Sandbox+BashWorkspace.swift

This file was deleted.

77 changes: 7 additions & 70 deletions Sources/BashInterpreter/API/Shell+Path.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,76 +54,13 @@ extension Shell {
return false
}

/// Lexical path normalisation — collapses `.` / `..` / repeated
/// `/` purely as text, never touching the filesystem. **Does not
/// resolve symlinks** (that's what `cd -L` / `pwd -L` semantics
/// rely on; `cd -P` / `pwd -P` go through
/// ``FileSystem/canonicalize(_:allowMissing:)`` instead).
///
/// Replaces `NSString.standardizingPath`, which is technically
/// supposed to be lexical but on swift-corelibs-foundation
/// (Linux) follows symlinks too — making `cd -L /var` set `$PWD`
/// to `/private/var` instead of preserving `/var`.
///
/// On Windows, backslashes are normalised to forward slashes
/// up front (Win32 path APIs accept both). Drive-letter paths
/// keep their `C:` prefix as the root segment so the result is
/// still a valid Windows path: `C:\Users\foo\..\bar` → `C:/Users/bar`.
static func normalizePath(_ path: String) -> String {
guard !path.isEmpty else { return "" }
#if os(Windows)
let normalized = path.replacingOccurrences(of: "\\", with: "/")
#else
let normalized = path
#endif
// Split on `/`, tracking whether the path is anchored at the
// root (Unix `/foo`) or at a drive (Windows `C:/foo`). For a
// drive-letter path we keep the `C:` segment in the stack so
// the rebuilt string still stems from that drive.
let isUnixAbsolute = normalized.hasPrefix("/")
var stack: [String] = []
var driveRoot: String?
var saw: [Substring] = normalized.split(
separator: "/", omittingEmptySubsequences: true)
#if os(Windows)
// Detect a leading `C:` segment (drive root). After we
// record it, the rest of the segments are walked as if the
// path were absolute beneath that drive.
if let first = saw.first,
first.count == 2,
let firstChar = first.first, firstChar.isLetter,
first.last == ":" {
driveRoot = String(first)
saw = Array(saw.dropFirst())
}
#endif
let anchored = isUnixAbsolute || driveRoot != nil
for seg in saw {
switch seg {
case ".":
continue
case "..":
// For anchored paths, `..` at the root stays at the
// root. For relative paths we let `..` underflow as
// a literal segment so callers can preserve the
// user's intent (rare in practice).
if !stack.isEmpty, stack.last != ".." {
stack.removeLast()
} else if !anchored {
stack.append("..")
}
default:
stack.append(String(seg))
}
}
if let driveRoot {
return driveRoot + "/" + stack.joined(separator: "/")
}
if isUnixAbsolute {
return "/" + stack.joined(separator: "/")
}
return stack.isEmpty ? "." : stack.joined(separator: "/")
}
// NB: `normalizePath(_:)` — the lexical `.` / `..` / `//`
// collapse this resolver relies on — moved down to
// `ShellKit.Shell` with #83 so the shared `PathMapping` core and
// this interpreter normalise identically. Call sites are
// unchanged: the static is inherited. (`cd -L` / `pwd -L`
// semantics rely on it being lexical; `cd -P` / `pwd -P` go
// through ``FileSystem/canonicalize(_:allowMissing:)`` instead.)

private func expandTilde(_ path: String) -> String {
guard path.hasPrefix("~"),
Expand Down
Loading
Loading