Skip to content

Commit 2a69b7c

Browse files
authored
os: add split_path/1: os.split_path('/usr/lib/test.so') -> ('/usr/lib','test','.so'); fix platform dependent behaviour of os.dir/1, os.base/1, os.file_name/1 (#23532)
1 parent 305a272 commit 2a69b7c

2 files changed

Lines changed: 156 additions & 53 deletions

File tree

‎vlib/os/os.v‎

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -281,15 +281,14 @@ pub fn file_ext(opath string) string {
281281
// If the path is empty, dir returns ".". If the path consists entirely of separators,
282282
// dir returns a single separator.
283283
// The returned path does not end in a separator unless it is the root directory.
284-
pub fn dir(opath string) string {
285-
if opath == '' {
284+
pub fn dir(path string) string {
285+
if path == '' {
286286
return '.'
287287
}
288-
other_separator := if path_separator == '/' { '\\' } else { '/' }
289-
path := opath.replace(other_separator, path_separator)
290-
pos := path.last_index(path_separator) or { return '.' }
291-
if pos == 0 && path_separator == '/' {
292-
return '/'
288+
detected_path_separator := if path.contains('/') { '/' } else { '\\' }
289+
pos := path.last_index(detected_path_separator) or { return '.' }
290+
if pos == 0 {
291+
return detected_path_separator
293292
}
294293
return path[..pos]
295294
}
@@ -298,30 +297,71 @@ pub fn dir(opath string) string {
298297
// Trailing path separators are removed before extracting the last element.
299298
// If the path is empty, base returns ".". If the path consists entirely of separators, base returns a
300299
// single separator.
301-
pub fn base(opath string) string {
302-
if opath == '' {
300+
pub fn base(path string) string {
301+
if path == '' {
303302
return '.'
304303
}
305-
other_separator := if path_separator == '/' { '\\' } else { '/' }
306-
path := opath.replace(other_separator, path_separator)
307-
if path == path_separator {
308-
return path_separator
304+
detected_path_separator := if path.contains('/') { '/' } else { '\\' }
305+
if path == detected_path_separator {
306+
return detected_path_separator
309307
}
310-
if path.ends_with(path_separator) {
308+
if path.ends_with(detected_path_separator) {
311309
path2 := path[..path.len - 1]
312-
pos := path2.last_index(path_separator) or { return path2.clone() }
310+
pos := path2.last_index(detected_path_separator) or { return path2.clone() }
313311
return path2[pos + 1..]
314312
}
315-
pos := path.last_index(path_separator) or { return path.clone() }
313+
pos := path.last_index(detected_path_separator) or { return path.clone() }
316314
return path[pos + 1..]
317315
}
318316

319317
// file_name will return all characters found after the last occurrence of `path_separator`.
320318
// file extension is included.
321-
pub fn file_name(opath string) string {
322-
other_separator := if path_separator == '/' { '\\' } else { '/' }
323-
path := opath.replace(other_separator, path_separator)
324-
return path.all_after_last(path_separator)
319+
pub fn file_name(path string) string {
320+
detected_path_separator := if path.contains('/') { '/' } else { '\\' }
321+
return path.all_after_last(detected_path_separator)
322+
}
323+
324+
// split_path will split `path` into (`dir`,`filename`,`ext`).
325+
// Examples:
326+
// ```v
327+
// dir,filename,ext := os.split_path('/usr/lib/test.so')
328+
// assert [dir,filename,ext] == ['/usr/lib','test','.so']
329+
// ```
330+
pub fn split_path(path string) (string, string, string) {
331+
if path == '' {
332+
return '.', '', ''
333+
} else if path == '.' {
334+
return '.', '', ''
335+
} else if path == '..' {
336+
return '..', '', ''
337+
}
338+
339+
detected_path_separator := if path.contains('/') { '/' } else { '\\' }
340+
341+
if path == detected_path_separator {
342+
return detected_path_separator, '', ''
343+
}
344+
if path.ends_with(detected_path_separator) {
345+
return path[..path.len - 1], '', ''
346+
}
347+
mut dir := '.'
348+
/*
349+
TODO: JS backend does not support IfGuard yet.
350+
*/
351+
pos := path.last_index(detected_path_separator) or { -1 }
352+
if pos == -1 {
353+
dir = '.'
354+
} else if pos == 0 {
355+
dir = detected_path_separator
356+
} else {
357+
dir = path[..pos]
358+
}
359+
file_name := path.all_after_last(detected_path_separator)
360+
pos_ext := file_name.last_index_u8(`.`)
361+
if pos_ext == -1 || pos_ext == 0 || pos_ext + 1 >= file_name.len {
362+
return dir, file_name, ''
363+
}
364+
return dir, file_name[..pos_ext], file_name[pos_ext..]
325365
}
326366

327367
// input_opt returns a one-line string from stdin, after printing a prompt.

‎vlib/os/os_test.c.v‎

Lines changed: 96 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,7 @@ fn test_is_executable_writable_readable() {
596596
}
597597

598598
fn test_file_ext() {
599+
assert os.file_ext('') == ''
599600
assert os.file_ext('file.v') == '.v'
600601
assert os.file_ext('file.js.v') == '.v'
601602
assert os.file_ext('file.ext1.ext2.ext3') == '.ext3'
@@ -640,50 +641,112 @@ fn test_rmdir_not_exist() ! {
640641
}
641642

642643
fn test_dir() {
643-
$if windows {
644-
assert os.dir('C:\\a\\b\\c') == 'C:\\a\\b'
645-
assert os.dir('C:\\a\\b\\') == 'C:\\a\\b'
646-
assert os.dir('C:/a/b/c') == 'C:\\a\\b'
647-
assert os.dir('C:/a/b/') == 'C:\\a\\b'
648-
} $else {
649-
assert os.dir('/') == '/'
650-
assert os.dir('/abc') == '/'
651-
assert os.dir('/var/tmp/foo') == '/var/tmp'
652-
assert os.dir('/var/tmp/') == '/var/tmp'
653-
assert os.dir('C:\\a\\b\\c') == 'C:/a/b'
654-
assert os.dir('C:\\a\\b\\') == 'C:/a/b'
655-
}
644+
assert os.dir('') == '.'
645+
assert os.dir('\\') == '\\'
646+
assert os.dir('C:\\a\\b\\c') == 'C:\\a\\b'
647+
assert os.dir('C:\\a\\b\\') == 'C:\\a\\b'
648+
assert os.dir('C:/a/b/c') == 'C:/a/b'
649+
assert os.dir('C:/a/b/') == 'C:/a/b'
650+
assert os.dir('/') == '/'
651+
assert os.dir('/abc') == '/'
652+
assert os.dir('/var/tmp/foo') == '/var/tmp'
653+
assert os.dir('/var/tmp/') == '/var/tmp'
656654
assert os.dir('os') == '.'
657655
}
658656

659657
fn test_base() {
660-
$if windows {
661-
assert os.base('v\\vlib\\os') == 'os'
662-
assert os.base('v\\vlib\\os\\') == 'os'
663-
assert os.base('v/vlib/os') == 'os'
664-
assert os.base('v/vlib/os/') == 'os'
665-
} $else {
666-
assert os.base('v/vlib/os') == 'os'
667-
assert os.base('v/vlib/os/') == 'os'
668-
assert os.base('v\\vlib\\os') == 'os'
669-
assert os.base('v\\vlib\\os\\') == 'os'
670-
}
658+
assert os.base('') == '.'
659+
assert os.base('v\\vlib\\os') == 'os'
660+
assert os.base('v\\vlib\\os\\') == 'os'
661+
assert os.base('v/vlib/os') == 'os'
662+
assert os.base('v/vlib/os/') == 'os'
663+
assert os.base('v/vlib/os') == 'os'
664+
assert os.base('v/vlib/os/') == 'os'
665+
assert os.base('v\\vlib\\os') == 'os'
666+
assert os.base('v\\vlib\\os\\') == 'os'
671667
assert os.base('filename') == 'filename'
672668
}
673669

674670
fn test_file_name() {
675-
$if windows {
676-
assert os.file_name('v\\vlib\\os\\os.v') == 'os.v'
677-
assert os.file_name('v\\vlib\\os\\') == ''
678-
assert os.file_name('v\\vlib\\os') == 'os'
679-
} $else {
680-
assert os.file_name('v/vlib/os/os.v') == 'os.v'
681-
assert os.file_name('v/vlib/os/') == ''
682-
assert os.file_name('v/vlib/os') == 'os'
683-
}
671+
assert os.file_name('') == ''
672+
assert os.file_name('v\\vlib\\os\\os.v') == 'os.v'
673+
assert os.file_name('v\\vlib\\os\\') == ''
674+
assert os.file_name('v\\vlib\\os') == 'os'
675+
assert os.file_name('v/vlib/os/os.v') == 'os.v'
676+
assert os.file_name('v/vlib/os/') == ''
677+
assert os.file_name('v/vlib/os') == 'os'
684678
assert os.file_name('filename') == 'filename'
685679
}
686680

681+
fn test_split_path() {
682+
mut dir := ''
683+
mut filename := ''
684+
mut ext := ''
685+
686+
dir, filename, ext = os.split_path('')
687+
assert [dir, filename, ext] == ['.', '', '']
688+
689+
dir, filename, ext = os.split_path('a')
690+
assert [dir, filename, ext] == ['.', 'a', '']
691+
692+
dir, filename, ext = os.split_path('.')
693+
assert [dir, filename, ext] == ['.', '', '']
694+
695+
dir, filename, ext = os.split_path('..')
696+
assert [dir, filename, ext] == ['..', '', '']
697+
698+
dir, filename, ext = os.split_path('\\')
699+
assert [dir, filename, ext] == ['\\', '', '']
700+
701+
dir, filename, ext = os.split_path('\\x.c.v')
702+
assert [dir, filename, ext] == ['\\', 'x.c', '.v']
703+
704+
dir, filename, ext = os.split_path('.\\x.c.v')
705+
assert [dir, filename, ext] == ['.', 'x.c', '.v']
706+
707+
dir, filename, ext = os.split_path('x.c.v')
708+
assert [dir, filename, ext] == ['.', 'x.c', '.v']
709+
710+
dir, filename, ext = os.split_path('..\\x.c.v')
711+
assert [dir, filename, ext] == ['..', 'x.c', '.v']
712+
713+
dir, filename, ext = os.split_path('\\lib\\x.c.v')
714+
assert [dir, filename, ext] == ['\\lib', 'x.c', '.v']
715+
716+
dir, filename, ext = os.split_path('\\lib\\x.c.v\\')
717+
assert [dir, filename, ext] == ['\\lib\\x.c.v', '', '']
718+
719+
dir, filename, ext = os.split_path('\\lib\\x.c.')
720+
assert [dir, filename, ext] == ['\\lib', 'x.c.', '']
721+
722+
dir, filename, ext = os.split_path('C:\\lib\\x.c.')
723+
assert [dir, filename, ext] == ['C:\\lib', 'x.c.', '']
724+
725+
dir, filename, ext = os.split_path('/')
726+
assert [dir, filename, ext] == ['/', '', '']
727+
728+
dir, filename, ext = os.split_path('/x.c.v')
729+
assert [dir, filename, ext] == ['/', 'x.c', '.v']
730+
731+
dir, filename, ext = os.split_path('./x.c.v')
732+
assert [dir, filename, ext] == ['.', 'x.c', '.v']
733+
734+
dir, filename, ext = os.split_path('../x.c.v')
735+
assert [dir, filename, ext] == ['..', 'x.c', '.v']
736+
737+
dir, filename, ext = os.split_path('/lib/x.c.v')
738+
assert [dir, filename, ext] == ['/lib', 'x.c', '.v']
739+
740+
dir, filename, ext = os.split_path('/lib/x.c.v/')
741+
assert [dir, filename, ext] == ['/lib/x.c.v', '', '']
742+
743+
dir, filename, ext = os.split_path('/lib/../x.c.v/')
744+
assert [dir, filename, ext] == ['/lib/../x.c.v', '', '']
745+
746+
dir, filename, ext = os.split_path('/lib/x.c.')
747+
assert [dir, filename, ext] == ['/lib', 'x.c.', '']
748+
}
749+
687750
fn test_uname() {
688751
u := os.uname()
689752
assert u.sysname.len > 0

0 commit comments

Comments
 (0)