vlib: reimplement glob in V for UNIX to not depend on libc (#10707)

pull/10716/head
pancake 2021-07-09 02:27:16 +02:00 committed by GitHub
parent 151cd0bfe6
commit 47bf64473c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 152 additions and 49 deletions

View File

@ -15,9 +15,25 @@ fn deep_glob() ? {
}
}
fn redeep_glob() ? {
os.chdir(@VMODROOT)
matches := os.glob('vlib/v/**/*.v') or { panic(err) }
assert matches.len > 10
assert 'vlib/v/ast/ast.v' in matches
assert 'vlib/v/ast/table.v' in matches
assert 'vlib/v/token/token.v' in matches
for f in matches {
if !f.starts_with('vlib/v/') {
assert false
}
assert f.ends_with('.v')
}
}
fn test_glob_can_find_v_files_3_levels_deep() ? {
$if !windows {
deep_glob() ?
redeep_glob() ?
}
assert true
}
@ -30,7 +46,9 @@ fn test_glob_can_find_files_in_current_folder() ? {
assert 'cmd/' in matches
assert 'vlib/' in matches
for f in matches {
assert !f.ends_with('.v')
if !f.ends_with(os.path_separator) {
assert !f.ends_with('.v')
}
}
}

View File

@ -627,3 +627,12 @@ pub fn is_atty(fd int) int {
return C.isatty(fd)
}
}
pub fn glob(patterns ...string) ?[]string {
mut matches := []string{}
for pattern in patterns {
native_glob_pattern(pattern, mut matches) ?
}
matches.sort()
return matches
}

View File

@ -8,7 +8,6 @@ import strings
#include <sys/utsname.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <glob.h>
#include <utime.h>
pub const (
@ -42,14 +41,6 @@ pub const (
s_ixoth = 0o0001 // Execute by others
)
[typedef]
struct C.glob_t {
mut:
gl_pathc size_t // number of matched paths
gl_pathv &&char // list of matched pathnames
gl_offs size_t // slots to reserve in gl_pathv
}
struct C.utsname {
mut:
sysname &char
@ -84,45 +75,138 @@ fn C.getgid() int
fn C.getegid() int
fn C.ptrace(u32, u32, voidptr, int) u64
fn C.glob(&char, int, voidptr, voidptr) int
fn C.globfree(voidptr)
enum GlobMatch {
exact
ends_with
starts_with
start_and_ends_with
contains
any
}
pub fn glob(patterns ...string) ?[]string {
$if android {
return error('os.glob() is not supported on android yet')
fn glob_match(dir string, pattern string, next_pattern string, mut matches []string) []string {
mut subdirs := []string{}
if is_file(dir) {
return subdirs
}
mut matches := []string{}
if patterns.len == 0 {
return matches
}
mut globdata := C.glob_t{
gl_pathv: 0
gl_pathc: size_t(0)
gl_offs: size_t(0)
}
mut flags := int(C.GLOB_DOOFFS | C.GLOB_MARK)
for i, pattern in patterns {
if i > 0 {
flags |= C.GLOB_APPEND
}
unsafe {
$if !android {
if C.glob(&char(pattern.str), flags, C.NULL, &globdata) != 0 {
return error_with_code(posix_get_error_msg(C.errno), C.errno)
mut files := ls(dir) or { return subdirs }
mut mode := GlobMatch.exact
mut pat := pattern
if pat == '*' {
mode = GlobMatch.any
if next_pattern != pattern && next_pattern != '' {
for file in files {
if is_dir('$dir/$file') {
subdirs << '$dir/$file'
}
}
return subdirs
}
}
if pat == '**' {
files = walk_ext(dir, '')
pat = next_pattern
}
if pat.starts_with('*') {
mode = .ends_with
pat = pat[1..]
}
if pat.ends_with('*') {
mode = if mode == .ends_with { GlobMatch.contains } else { GlobMatch.starts_with }
pat = pat[..pat.len - 1]
}
if pat.contains('*') {
mode = .start_and_ends_with
}
for file in files {
mut fpath := file
f := if file.contains(os.path_separator) {
pathwalk := file.split(os.path_separator)
pathwalk[pathwalk.len - 1]
} else {
fpath = if dir == '.' { file } else { '$dir/$file' }
file
}
if f in ['.', '..'] || f == '' {
continue
}
hit := match mode {
.any {
true
}
.exact {
f == pat
}
.starts_with {
f.starts_with(pat)
}
.ends_with {
f.ends_with(pat)
}
.start_and_ends_with {
p := pat.split('*')
f.starts_with(p[0]) && f.ends_with(p[1])
}
.contains {
f.contains(pat)
}
}
if hit {
if is_dir(fpath) {
subdirs << fpath
if next_pattern == pattern && next_pattern != '' {
matches << '$fpath$os.path_separator'
}
} else {
matches << fpath
}
}
}
for i := 0; i < int(globdata.gl_pathc); i++ {
unsafe {
matches << cstring_to_vstring(globdata.gl_pathv[i])
return subdirs
}
fn native_glob_pattern(pattern string, mut matches []string) ? {
steps := pattern.split(os.path_separator)
mut cwd := if pattern.starts_with(os.path_separator) { os.path_separator } else { '.' }
mut subdirs := [cwd]
for i := 0; i < steps.len; i++ {
step := steps[i]
step2 := if i + 1 == steps.len { step } else { steps[i + 1] }
if step == '' {
continue
}
if is_dir('$cwd$os.path_separator$step') {
dd := if cwd == '/' {
step
} else {
if cwd == '.' || cwd == '' {
step
} else {
if step == '.' || step == '/' { cwd } else { '$cwd/$step' }
}
}
if i + 1 != steps.len {
if dd !in subdirs {
subdirs << dd
}
}
}
mut subs := []string{}
for sd in subdirs {
d := if cwd == '/' {
sd
} else {
if cwd == '.' || cwd == '' {
sd
} else {
if sd == '.' || sd == '/' { cwd } else { '$cwd/$sd' }
}
}
subs << glob_match(d.replace('//', '/'), step, step2, mut matches)
}
subdirs = subs.clone()
}
$if !android {
C.globfree(&globdata)
}
return matches
}
pub fn utime(path string, actime int, modtime int) ? {

View File

@ -102,15 +102,7 @@ fn init_os_args_wide(argc int, argv &&byte) []string {
return args_
}
pub fn glob(patterns ...string) ?[]string {
mut matches := []string{}
for pattern in patterns {
windows_glob_pattern(pattern, mut matches) ?
}
return matches
}
fn windows_glob_pattern(pattern string, mut matches []string) ? {
fn native_glob_pattern(pattern string, mut matches []string) ? {
$if debug {
// FindFirstFile() and FindNextFile() both have a globbing function.
// Unfortunately this is not as pronounced as under Unix, but should provide some functionality