os: fix join-path (#21425)

This commit is contained in:
Henrik Holst 2024-05-13 09:37:55 +02:00 committed by GitHub
parent 35f6523e47
commit 76142b18a9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 35 additions and 17 deletions

View file

@ -23,7 +23,7 @@ fn test_ensure_folder_is_writable() {
fn test_expand_tilde_to_home() { fn test_expand_tilde_to_home() {
os.setenv('HOME', '/tmp/home/folder', true) os.setenv('HOME', '/tmp/home/folder', true)
os.setenv('USERPROFILE', '/tmp/home/folder', true) os.setenv('USERPROFILE', r'\tmp\home\folder', true)
// //
home_test := os.join_path(os.home_dir(), 'test', 'tilde', 'expansion') home_test := os.join_path(os.home_dir(), 'test', 'tilde', 'expansion')
home_expansion_test := os.expand_tilde_to_home(os.join_path('~', 'test', 'tilde', home_expansion_test := os.expand_tilde_to_home(os.join_path('~', 'test', 'tilde',

View file

@ -568,6 +568,8 @@ pub fn is_file(path string) bool {
// join_path joins any number of path elements into a single path, separating // join_path joins any number of path elements into a single path, separating
// them with a platform-specific path_separator. Empty elements are ignored. // them with a platform-specific path_separator. Empty elements are ignored.
// Windows platform output will rewrite forward slashes to backslash.
// Consider looking at the unit tests in os_test.v for semi-formal API.
@[manualfree] @[manualfree]
pub fn join_path(base string, dirs ...string) string { pub fn join_path(base string, dirs ...string) string {
// TODO: fix freeing of `dirs` when the passed arguments are variadic, // TODO: fix freeing of `dirs` when the passed arguments are variadic,
@ -576,25 +578,36 @@ pub fn join_path(base string, dirs ...string) string {
defer { defer {
unsafe { sb.free() } unsafe { sb.free() }
} }
sbase := base.trim_right('\\/') mut needs_sep := false
defer { if base != '' {
unsafe { sbase.free() } $if windows {
sb.write_string(base.replace('/', '\\'))
} $else {
sb.write_string(base)
} }
sb.write_string(sbase) needs_sep = !base.ends_with(path_separator)
for d in dirs { }
if d != '' { for od in dirs {
if od != '' && od != '.' {
mut md := od
$if windows {
md = md.replace('/', '\\')
}
// NOTE(hholst80): split_any not available in js backend,
// which could have been more clean way to implement this.
nestdirs := md.split(path_separator)
for id in nestdirs {
if id != '' && id != '.' {
if needs_sep {
sb.write_string(path_separator) sb.write_string(path_separator)
sb.write_string(d) }
sb.write_string(id)
needs_sep = !id.ends_with(path_separator)
} }
} }
mut res := sb.str()
if sbase == '' {
res = res.trim_left(path_separator)
} }
if res.contains('/./') {
// Fix `join_path("/foo/bar", "./file.txt")` => `/foo/bar/./file.txt`
res = res.replace('/./', '/')
} }
res := sb.str()
return res return res
} }

View file

@ -619,11 +619,16 @@ fn test_join() {
assert os.join_path('v', 'vlib', 'os') == 'v\\vlib\\os' assert os.join_path('v', 'vlib', 'os') == 'v\\vlib\\os'
assert os.join_path('', 'f1', 'f2') == 'f1\\f2' assert os.join_path('', 'f1', 'f2') == 'f1\\f2'
assert os.join_path('v', '', 'dir') == 'v\\dir' assert os.join_path('v', '', 'dir') == 'v\\dir'
assert os.join_path('v', 'foo/bar', 'dir') == 'v\\foo\\bar\\dir'
assert os.join_path('v', 'foo/bar\\baz', '/dir') == 'v\\foo\\bar\\baz\\dir'
assert os.join_path('C:', 'f1\\..', 'f2') == 'C:\\f1\\..\\f2'
} $else { } $else {
assert os.join_path('v', 'vlib', 'os') == 'v/vlib/os' assert os.join_path('v', 'vlib', 'os') == 'v/vlib/os'
assert os.join_path('/foo/bar', './file.txt') == '/foo/bar/file.txt' assert os.join_path('/foo/bar', './file.txt') == '/foo/bar/file.txt'
assert os.join_path('', 'f1', 'f2') == 'f1/f2' assert os.join_path('', 'f1', 'f2') == 'f1/f2'
assert os.join_path('v', '', 'dir') == 'v/dir' assert os.join_path('v', '', 'dir') == 'v/dir'
assert os.join_path('/', 'test') == '/test'
assert os.join_path('/foo/bar', './.././file.txt') == '/foo/bar/../file.txt'
} }
} }