Skip to content

Commit c23c543

Browse files
authored
os: simplify and unify os.join_path and os.join_path_single, and add more tests (#21494)
1 parent 76142b1 commit c23c543

File tree

4 files changed

+134
-50
lines changed

4 files changed

+134
-50
lines changed

vlib/os/join_path_test.v

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import os
2+
3+
fn test_join_path() {
4+
assert os.join_path('', '', '') == ''
5+
assert os.join_path('', '') == ''
6+
assert os.join_path('') == ''
7+
assert os.join_path('b', '', '') == 'b'
8+
assert os.join_path('b', '') == 'b'
9+
assert os.join_path('b') == 'b'
10+
assert os.join_path('', '', './b') == 'b'
11+
assert os.join_path('', '', '/b') == 'b'
12+
assert os.join_path('', '', 'b') == 'b'
13+
assert os.join_path('', './b') == 'b'
14+
assert os.join_path('', '/b') == 'b'
15+
assert os.join_path('', 'b') == 'b'
16+
assert os.join_path('b', '') == 'b'
17+
$if windows {
18+
assert os.join_path('./b', '') == r'.\b'
19+
assert os.join_path('/b', '') == r'\b'
20+
assert os.join_path('./a', './b') == r'.\a\b'
21+
assert os.join_path('/', 'test') == r'\test'
22+
assert os.join_path('v', 'vlib', 'os') == r'v\vlib\os'
23+
assert os.join_path('', 'f1', 'f2') == r'f1\f2'
24+
assert os.join_path('v', '', 'dir') == r'v\dir'
25+
assert os.join_path(r'foo\bar', r'.\file.txt') == r'foo\bar\file.txt'
26+
assert os.join_path('foo/bar', './file.txt') == r'foo\bar\file.txt'
27+
assert os.join_path('/opt/v', './x') == r'\opt\v\x'
28+
assert os.join_path('v', 'foo/bar', 'dir') == r'v\foo\bar\dir'
29+
assert os.join_path('v', 'foo/bar\\baz', '/dir') == r'v\foo\bar\baz\dir'
30+
assert os.join_path('C:', 'f1\\..', 'f2') == r'C:\f1\..\f2'
31+
} $else {
32+
assert os.join_path('./b', '') == './b'
33+
assert os.join_path('/b', '') == '/b'
34+
assert os.join_path('./a', './b') == './a/b'
35+
assert os.join_path('/', 'test') == '/test'
36+
assert os.join_path('v', 'vlib', 'os') == 'v/vlib/os'
37+
assert os.join_path('', 'f1', 'f2') == 'f1/f2'
38+
assert os.join_path('v', '', 'dir') == 'v/dir'
39+
assert os.join_path('/foo/bar', './file.txt') == '/foo/bar/file.txt'
40+
assert os.join_path('foo/bar', './file.txt') == 'foo/bar/file.txt'
41+
assert os.join_path('/opt/v', './x') == '/opt/v/x'
42+
assert os.join_path('/foo/bar', './.././file.txt') == '/foo/bar/../file.txt'
43+
assert os.join_path('v', 'foo/bar\\baz', '/dir') == r'v/foo/bar/baz/dir'
44+
}
45+
}
46+
47+
fn test_join_path_single() {
48+
assert os.join_path_single('', '') == ''
49+
assert os.join_path_single('', './b') == 'b'
50+
assert os.join_path_single('', '/b') == 'b'
51+
assert os.join_path_single('', 'b') == 'b'
52+
assert os.join_path_single('b', '') == 'b'
53+
$if windows {
54+
assert os.join_path_single('./b', '') == r'.\b'
55+
assert os.join_path_single('/b', '') == r'\b'
56+
assert os.join_path_single('/foo/bar', './file.txt') == r'\foo\bar\file.txt'
57+
assert os.join_path_single('/', 'test') == r'\test'
58+
assert os.join_path_single('foo\\bar', '.\\file.txt') == r'foo\bar\file.txt'
59+
assert os.join_path_single('/opt/v', './x') == r'\opt\v\x'
60+
} $else {
61+
assert os.join_path_single('./b', '') == r'./b'
62+
assert os.join_path_single('/b', '') == r'/b'
63+
assert os.join_path_single('/foo/bar', './file.txt') == '/foo/bar/file.txt'
64+
assert os.join_path_single('/', 'test') == '/test'
65+
assert os.join_path_single('foo/bar', './file.txt') == 'foo/bar/file.txt'
66+
assert os.join_path_single('/opt/v', './x') == '/opt/v/x'
67+
assert os.join_path_single('./a', './b') == './a/b'
68+
assert os.join_path_single('a', './b') == 'a/b'
69+
}
70+
}

vlib/os/os.v

Lines changed: 60 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -578,36 +578,22 @@ pub fn join_path(base string, dirs ...string) string {
578578
defer {
579579
unsafe { sb.free() }
580580
}
581-
mut needs_sep := false
582-
if base != '' {
583-
$if windows {
584-
sb.write_string(base.replace('/', '\\'))
585-
} $else {
586-
sb.write_string(base)
587-
}
588-
needs_sep = !base.ends_with(path_separator)
589-
}
590-
for od in dirs {
591-
if od != '' && od != '.' {
592-
mut md := od
593-
$if windows {
594-
md = md.replace('/', '\\')
595-
}
596-
// NOTE(hholst80): split_any not available in js backend,
597-
// which could have been more clean way to implement this.
598-
nestdirs := md.split(path_separator)
599-
for id in nestdirs {
600-
if id != '' && id != '.' {
601-
if needs_sep {
602-
sb.write_string(path_separator)
603-
}
604-
sb.write_string(id)
605-
needs_sep = !id.ends_with(path_separator)
606-
}
607-
}
581+
sbase := base.trim_right('\\/')
582+
defer {
583+
unsafe { sbase.free() }
584+
}
585+
sb.write_string(sbase)
586+
for d in dirs {
587+
if d != '' {
588+
sb.write_string(path_separator)
589+
sb.write_string(d)
608590
}
609591
}
610-
res := sb.str()
592+
normalize_path_in_builder(mut sb)
593+
mut res := sb.str()
594+
if base == '' {
595+
res = res.trim_left(path_separator)
596+
}
611597
return res
612598
}
613599

@@ -625,12 +611,54 @@ pub fn join_path_single(base string, elem string) string {
625611
defer {
626612
unsafe { sbase.free() }
627613
}
628-
if base != '' {
629-
sb.write_string(sbase)
614+
sb.write_string(sbase)
615+
if elem != '' {
630616
sb.write_string(path_separator)
617+
sb.write_string(elem)
618+
}
619+
normalize_path_in_builder(mut sb)
620+
mut res := sb.str()
621+
if base == '' {
622+
res = res.trim_left(path_separator)
623+
}
624+
return res
625+
}
626+
627+
@[direct_array_access]
628+
fn normalize_path_in_builder(mut sb strings.Builder) {
629+
mut fs := `\\`
630+
mut rs := `/`
631+
$if windows {
632+
fs = `/`
633+
rs = `\\`
634+
}
635+
for idx in 0 .. sb.len {
636+
unsafe {
637+
if sb[idx] == fs {
638+
sb[idx] = rs
639+
}
640+
}
641+
}
642+
for idx in 0 .. sb.len - 3 {
643+
if sb[idx] == rs && sb[idx + 1] == `.` && sb[idx + 2] == rs {
644+
unsafe {
645+
// let `/foo/./bar.txt` become `/foo/bar.txt` in place
646+
for j := idx + 1; j < sb.len - 2; j++ {
647+
sb[j] = sb[j + 2]
648+
}
649+
sb.len -= 2
650+
}
651+
}
652+
if sb[idx] == rs && sb[idx + 1] == rs {
653+
unsafe {
654+
// let `/foo//bar.txt` become `/foo/bar.txt` in place
655+
for j := idx + 1; j < sb.len - 1; j++ {
656+
sb[j] = sb[j + 1]
657+
}
658+
sb.len -= 1
659+
}
660+
}
631661
}
632-
sb.write_string(elem)
633-
return sb.str()
634662
}
635663

636664
// walk_ext returns a recursive list of all files in `path` ending with `ext`.

vlib/os/os_test.c.v

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -614,24 +614,6 @@ fn test_file_ext() {
614614
assert os.file_ext('\\.git\\') == ''
615615
}
616616

617-
fn test_join() {
618-
$if windows {
619-
assert os.join_path('v', 'vlib', 'os') == 'v\\vlib\\os'
620-
assert os.join_path('', 'f1', 'f2') == 'f1\\f2'
621-
assert os.join_path('v', '', 'dir') == 'v\\dir'
622-
assert os.join_path('v', 'foo/bar', 'dir') == 'v\\foo\\bar\\dir'
623-
assert os.join_path('v', 'foo/bar\\baz', '/dir') == 'v\\foo\\bar\\baz\\dir'
624-
assert os.join_path('C:', 'f1\\..', 'f2') == 'C:\\f1\\..\\f2'
625-
} $else {
626-
assert os.join_path('v', 'vlib', 'os') == 'v/vlib/os'
627-
assert os.join_path('/foo/bar', './file.txt') == '/foo/bar/file.txt'
628-
assert os.join_path('', 'f1', 'f2') == 'f1/f2'
629-
assert os.join_path('v', '', 'dir') == 'v/dir'
630-
assert os.join_path('/', 'test') == '/test'
631-
assert os.join_path('/foo/bar', './.././file.txt') == '/foo/bar/../file.txt'
632-
}
633-
}
634-
635617
fn test_rmdir_all() {
636618
mut dirs := ['some/dir', 'some/.hidden/directory']
637619
$if windows {

vlib/v/parser/parser.v

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3632,6 +3632,10 @@ fn (mut p Parser) import_stmt() ast.Import {
36323632
return import_node
36333633
}
36343634
mut source_name := p.check_name()
3635+
if source_name == '' {
3636+
p.error_with_pos('import name can not be empty', pos)
3637+
return import_node
3638+
}
36353639
mut mod_name_arr := []string{}
36363640
mod_name_arr << source_name
36373641
if import_pos.line_nr != pos.line_nr {

0 commit comments

Comments
 (0)