os: add existing_path function (#14536)

master
Ben 2022-05-31 05:32:12 +02:00 committed by GitHub
parent 928dafeb6d
commit 4ffdcf8058
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 82 additions and 2 deletions

View File

@ -123,6 +123,52 @@ pub fn norm_path(path string) string {
return res return res
} }
// existing_path returns the existing part of the given `path`.
// An error is returned if there is no existing part of the given `path`.
pub fn existing_path(path string) ?string {
err := error('path does not exist')
if path.len == 0 {
return err
}
if exists(path) {
return path
}
mut volume_len := 0
$if windows {
volume_len = win_volume_len(path)
}
if volume_len > 0 && is_slash(path[volume_len - 1]) {
volume_len++
}
mut sc := textscanner.new(path[volume_len..])
mut recent_path := path[..volume_len]
for sc.next() != -1 {
curr := u8(sc.current())
peek := sc.peek()
back := sc.peek_back()
if is_curr_dir_ref(back, curr, peek) {
continue
}
range := sc.ilen - sc.remaining() + volume_len
if is_slash(curr) && !is_slash(u8(peek)) {
recent_path = path[..range]
continue
}
if !is_slash(curr) && (peek == -1 || is_slash(u8(peek))) {
curr_path := path[..range]
if exists(curr_path) {
recent_path = curr_path
continue
}
if recent_path.len == 0 {
break
}
return recent_path
}
}
return err
}
// clean_path returns the "cleaned" version of the given `path` // clean_path returns the "cleaned" version of the given `path`
// by turning forward slashes into back slashes // by turning forward slashes into back slashes
// on a Windows system and eliminating: // on a Windows system and eliminating:
@ -144,8 +190,7 @@ fn clean_path(path string) string {
continue continue
} }
// skip reference to current dir (.) // skip reference to current dir (.)
if (back == -1 || is_slash(u8(back))) && curr == os.dot if is_curr_dir_ref(back, curr, peek) {
&& (peek == -1 || is_slash(u8(peek))) {
// skip if the next byte is a path separator // skip if the next byte is a path separator
if peek != -1 && is_slash(u8(peek)) { if peek != -1 && is_slash(u8(peek)) {
sc.skip_n(1) sc.skip_n(1)
@ -246,3 +291,13 @@ fn is_normal_path(path string) bool {
return (plen == 1 && is_slash(path[0])) || (plen >= 2 && is_slash(path[0]) return (plen == 1 && is_slash(path[0])) || (plen >= 2 && is_slash(path[0])
&& !is_slash(path[1])) && !is_slash(path[1]))
} }
// is_curr_dir_ref returns `true` if the 3 given integer construct
// a reference to a current directory (.).
// NOTE: a negative integer means that no byte is present
fn is_curr_dir_ref(byte_one int, byte_two int, byte_three int) bool {
if u8(byte_two) != os.dot {
return false
}
return (byte_one < 0 || is_slash(u8(byte_one))) && (byte_three < 0 || is_slash(u8(byte_three)))
}

View File

@ -36,11 +36,13 @@ fn test_clean_path() {
assert clean_path(r'\./path/dir\\file.exe') == r'\path\dir\file.exe' assert clean_path(r'\./path/dir\\file.exe') == r'\path\dir\file.exe'
assert clean_path(r'.') == '' assert clean_path(r'.') == ''
assert clean_path(r'./') == '' assert clean_path(r'./') == ''
assert clean_path('') == ''
assert clean_path(r'\./') == '\\' assert clean_path(r'\./') == '\\'
assert clean_path(r'//\/\/////') == '\\' assert clean_path(r'//\/\/////') == '\\'
return return
} }
assert clean_path('./../.././././//') == '../..' assert clean_path('./../.././././//') == '../..'
assert clean_path('') == ''
assert clean_path('.') == '' assert clean_path('.') == ''
assert clean_path('./path/to/file.v//./') == 'path/to/file.v' assert clean_path('./path/to/file.v//./') == 'path/to/file.v'
assert clean_path('./') == '' assert clean_path('./') == ''
@ -127,3 +129,26 @@ fn test_abs_path() {
assert abs_path('path/../file.v/..') == wd assert abs_path('path/../file.v/..') == wd
assert abs_path('///') == '/' assert abs_path('///') == '/'
} }
fn test_existing_path() {
wd := getwd()
$if windows {
assert existing_path('') or { '' } == ''
assert existing_path('..') or { '' } == '..'
assert existing_path('.') or { '' } == '.'
assert existing_path(wd) or { '' } == wd
assert existing_path('\\') or { '' } == '\\'
assert existing_path('$wd\\.\\\\does/not/exist\\.\\') or { '' } == '$wd\\.\\\\'
assert existing_path('$wd\\\\/\\.\\.\\/.') or { '' } == '$wd\\\\/\\.\\.\\/.'
assert existing_path('$wd\\././/\\/oh') or { '' } == '$wd\\././/\\/'
return
}
assert existing_path('') or { '' } == ''
assert existing_path('..') or { '' } == '..'
assert existing_path('.') or { '' } == '.'
assert existing_path(wd) or { '' } == wd
assert existing_path('/') or { '' } == '/'
assert existing_path('$wd/does/.///not/exist///.//') or { '' } == '$wd/'
assert existing_path('$wd//././/.//') or { '' } == '$wd//././/.//'
assert existing_path('$wd//././/.//oh') or { '' } == '$wd//././/.//'
}