Merge pull request #5148 from Kelimion/fix-clean-path

Fix os2.clean_path on Windows
This commit is contained in:
Jeroen van Rijn
2025-05-11 15:44:40 +02:00
committed by GitHub
5 changed files with 108 additions and 65 deletions
+1
View File
@@ -294,4 +294,5 @@ build.sh
# RAD debugger project file # RAD debugger project file
*.raddbg *.raddbg
*.rdi *.rdi
tests/issues/build/*
misc/featuregen/featuregen misc/featuregen/featuregen
+5 -1
View File
@@ -187,7 +187,6 @@ init_long_path_support :: proc() {
if value == 1 { if value == 1 {
can_use_long_paths = true can_use_long_paths = true
} }
} }
@(require_results) @(require_results)
@@ -271,6 +270,11 @@ _clean_path_handle_start :: proc(path: string, buffer: []u8) -> (rooted: bool, s
start += 1 start += 1
} }
copy(buffer, path[:start]) copy(buffer, path[:start])
for n in 0..<start {
if _is_path_separator(buffer[n]) {
buffer[n] = _Path_Separator
}
}
} }
return return
} }
+54 -28
View File
@@ -329,42 +329,68 @@ _is_reserved_name :: proc(path: string) -> bool {
return false return false
} }
_is_UNC :: proc(path: string) -> bool { _volume_name_len :: proc(path: string) -> (length: int) {
return _volume_name_len(path) > 2
}
_volume_name_len :: proc(path: string) -> int {
if len(path) < 2 { if len(path) < 2 {
return 0 return 0
} }
c := path[0]
if path[1] == ':' { if path[1] == ':' {
switch c { switch path[0] {
case 'a'..='z', 'A'..='Z': case 'a'..='z', 'A'..='Z':
return 2 return 2
} }
} }
// URL: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx /*
if l := len(path); l >= 5 && _is_path_separator(path[0]) && _is_path_separator(path[1]) && See: URL: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
!_is_path_separator(path[2]) && path[2] != '.' { Further allowed paths can be of the form of:
for n := 3; n < l-1; n += 1 { - \\server\share or \\server\share\more\path
if _is_path_separator(path[n]) { - \\?\C:\...
n += 1 - \\.\PhysicalDriveX
if !_is_path_separator(path[n]) { */
if path[n] == '.' { // Any remaining kind of path has to start with two slashes.
break if !_is_path_separator(path[0]) || !_is_path_separator(path[1]) {
}
}
for ; n < l; n += 1 {
if _is_path_separator(path[n]) {
break
}
}
return n
}
break
}
}
return 0 return 0
}
// Device path. The volume name is the whole string
if len(path) >= 5 && path[2] == '.' && _is_path_separator(path[3]) {
return len(path)
}
// We're a UNC share `\\host\share`, file namespace `\\?\C:` or UNC in file namespace `\\?\\host\share`
prefix := 2
// File namespace.
if len(path) >= 5 && path[2] == '?' && _is_path_separator(path[3]) {
if _is_path_separator(path[4]) {
// `\\?\\` UNC path in file namespace
prefix = 5
}
if len(path) >= 6 && path[5] == ':' {
switch path[4] {
case 'a'..='z', 'A'..='Z':
return 6
case:
return 0
}
}
}
// UNC path, minimum version of the volume is `\\h\s` for host, share.
// Can also contain an IP address in the host position.
slash_count := 0
for i in prefix..<len(path) {
// Host needs to be at least 1 character
if _is_path_separator(path[i]) && i > 0 {
slash_count += 1
if slash_count == 2 {
return i
}
}
}
return len(path)
} }
+17 -6
View File
@@ -40,6 +40,23 @@ test_clean_path :: proc(t: ^testing.T) {
expected: string, expected: string,
} }
when ODIN_OS == .Windows {
test_cases := [?]Test_Case {
{`W:/odin\examples\demo/demo.odin`, `W:\odin\examples\demo\demo.odin`},
{`\\server\share\path\file.ext`, `\\server\share\path\file.ext`},
{`//server\share/path\file.ext`, `\\server\share\path\file.ext`},
{`/\192.168.0.10\share/path\file.ext`, `\\192.168.0.10\share\path\file.ext`},
{`\\?\C:/Users/Foo/path\file.ext`, `\\?\C:\Users\Foo\path\file.ext`},
{`\\?\\localhost\share\file.ext`, `\\?\\localhost\share\file.ext`},
{`//?\/192.168.0.10\share\file.ext`, `\\?\\192.168.0.10\share\file.ext`},
{`\\.\PhysicalDrive3`, `\\.\PhysicalDrive3`},
{`/\./PhysicalDrive3`, `\\.\PhysicalDrive3`},
{`C:\a\..\..`, `C:\`},
{`C:\a\..`, `C:\`},
{`C:\あ/a/..`, `C:\あ`},
{`C:\あ/a/../あ`, `C:\あ\あ`},
}
} else {
test_cases := [?]Test_Case { test_cases := [?]Test_Case {
{`../../foo/../../`, `../../..`}, {`../../foo/../../`, `../../..`},
{`../../foo/..`, `../..`}, {`../../foo/..`, `../..`},
@@ -72,12 +89,6 @@ test_clean_path :: proc(t: ^testing.T) {
{`foo`, `foo`}, {`foo`, `foo`},
{`home/foo/bar/../../`, `home`}, {`home/foo/bar/../../`, `home`},
} }
when ODIN_OS == .Windows {
for &tc in test_cases {
tc.path = posix_to_dos_path(tc.path)
tc.expected = posix_to_dos_path(tc.expected)
}
} }
for tc in test_cases { for tc in test_cases {
+1
View File
@@ -1,3 +1,4 @@
#+build !windows
package tests_core_os_os2 package tests_core_os_os2
import os "core:os/os2" import os "core:os/os2"