Skip to content

normalizePath drops the UNC // prefix on Windows #22

Closed

Description

@odrobnik

On Windows, Shell.normalizePath(_:) (Sources/ShellKit/Shell+Path.swift) turns a UNC path into a root-relative path:

input output
\\server\share\x\.. /server/share
//server/share/file /server/share/file
C:\Users\foo\..\bar C:/Users/bar (correct)
C:\.. C:/ (correct)

The backslash-to-slash pass turns the UNC prefix into //, and isUnixAbsolute matches it. split(separator: "/", omittingEmptySubsequences: true) then drops the empty segment, so only a single leading / is rebuilt. On Windows, /server/share means \server\share on the current drive, not the UNC share.

Repro: there's no Windows host here. I ran a verbatim copy of the function on macOS with its two #if os(Windows) blocks forced on, which produced the table above. ShellKit has no direct unit tests for normalizePath.

Impact: SwiftBash's Shell.isAbsolutePath treats UNC paths as absolute, and resolvePath documents them as supported (Sources/BashInterpreter/API/Shell+Path.swift in Cocoanetics/SwiftBash). But resolvePath runs every path through this normalizePath, so resolvePath(#"\\server\share\f"#) returns /server/share/f. This is not a regression from #17: SwiftBash's pre-#17 copy had a byte-identical body.

Suggested fix: on Windows only, after the separator pass, recognise a leading //server/share as the root (just as C: is kept as the drive root), clamp .. there, and rebuild with the // prefix intact. While at it, add unit tests for normalizePath covering the POSIX cases and #if os(Windows) drive and UNC cases.

Keep it Windows-only. POSIX leaves a leading // implementation-defined. bash keeps exactly two slashes (cd //tmp; pwd prints //tmp), while SwiftBash deliberately collapses them (/tmp). Mount routing depends on that collapse, so the POSIX branch shouldn't change as part of this fix.

Found while checking the drive-letter handling for the Cocoanetics/SwiftBash fresh-clone build break (Cocoanetics/SwiftBash#89).

Activity

  1. odrobnik commented on Oct 1, 2026

    @odrobnik
    ContributorAuthor

    🚀 Shipped (via Codex Team [erika] · 21% used) — opened #23

    Implemented and opened PR #23 with squash auto-merge enabled.

    Changes:

    • Preserves Windows UNC //server/share roots.
    • Clamps .. at the share root.
    • Leaves POSIX double-slash collapsing unchanged.
    • Adds POSIX, drive-letter, and Windows UNC normalization tests.

    Verification passed:

    • swift build --build-tests --scratch-path /Volumes/SSD/SwiftPM/ShellKit-agent
    • swift test --skip-build --scratch-path /Volumes/SSD/SwiftPM/ShellKit-agent — 65 tests passed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions