os: add a glob() function (#10497)

pull/9796/head
Bastian Buck 2021-06-30 07:30:18 +02:00 committed by GitHub
parent f029f7e897
commit d2f19ac494
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 179 additions and 1 deletions

View File

@ -0,0 +1,62 @@
import os
fn deep_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() ?
}
assert true
}
fn test_glob_can_find_files_in_current_folder() ? {
os.chdir(@VMODROOT)
matches := os.glob('*') ?
assert 'README.md' in matches
assert 'v.mod' in matches
assert 'cmd/' in matches
assert 'vlib/' in matches
for f in matches {
assert !f.ends_with('.v')
}
}
fn test_glob_can_be_used_with_multiple_patterns() ? {
os.chdir(@VMODROOT)
matches := os.glob('*', 'cmd/tools/*') ?
assert 'README.md' in matches
assert 'Makefile' in matches
$if !windows {
assert 'cmd/tools/test_if_v_test_system_works.v' in matches
}
$if windows {
assert 'test_if_v_test_system_works.v' in matches
}
}
fn test_glob_star() ? {
os.chdir(@VMODROOT)
matches := os.glob('*ake*') ?
assert 'Makefile' in matches
assert 'make.bat' in matches
}
fn test_glob_not_found() ? {
os.glob('an_unknown_folder/*.v') or {
assert true
return
}
}

View File

@ -183,7 +183,7 @@ pub fn file_size(path string) u64 {
} }
$if x32 { $if x32 {
$if debug { $if debug {
println('Using os.file_size() on 32bit systems may not work on big files.') eprintln('Using os.file_size() on 32bit systems may not work on big files.')
} }
$if windows { $if windows {
if C._wstat(path.to_wide(), voidptr(&s)) != 0 { if C._wstat(path.to_wide(), voidptr(&s)) != 0 {

View File

@ -8,6 +8,7 @@ import strings
#include <sys/utsname.h> #include <sys/utsname.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/ptrace.h> #include <sys/ptrace.h>
#include <glob.h>
pub const ( pub const (
path_separator = '/' path_separator = '/'
@ -49,6 +50,14 @@ mut:
machine &char machine &char
} }
[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
}
fn C.uname(name voidptr) int fn C.uname(name voidptr) int
fn C.symlink(&char, &char) int fn C.symlink(&char, &char) int
@ -67,6 +76,37 @@ fn C.getgid() int
fn C.getegid() int fn C.getegid() int
fn C.ptrace(u32, u32, voidptr, int) u64 fn C.ptrace(u32, u32, voidptr, int) u64
fn C.glob(&char, int, voidptr, voidptr) int
fn C.globfree(voidptr)
pub fn glob(patterns ...string) ?[]string {
mut matches := []string{}
if patterns.len == 0 {
return matches
}
mut globdata := C.glob_t{
gl_pathv: 0
}
mut flags := int(C.GLOB_DOOFFS | C.GLOB_MARK)
for i, pattern in patterns {
if i > 0 {
flags |= C.GLOB_APPEND
}
unsafe {
if C.glob(&char(pattern.str), flags, C.NULL, &globdata) != 0 {
return error_with_code(posix_get_error_msg(C.errno), C.errno)
}
}
}
for i := 0; i < int(globdata.gl_pathc); i++ {
unsafe {
matches << cstring_to_vstring(globdata.gl_pathv[i])
}
}
C.globfree(&globdata)
return matches
}
pub fn uname() Uname { pub fn uname() Uname {
mut u := Uname{} mut u := Uname{}

View File

@ -699,3 +699,28 @@ fn test_truncate() {
fn test_hostname() { fn test_hostname() {
assert os.hostname().len > 2 assert os.hostname().len > 2
} }
fn test_glob() {
os.mkdir('test_dir') or { panic(err) }
for i in 0 .. 4 {
if i == 3 {
mut f := os.create('test_dir/test0_another') or { panic(err) }
f.close()
mut f1 := os.create('test_dir/test') or { panic(err) }
f1.close()
} else {
mut f := os.create('test_dir/test' + i.str()) or { panic(err) }
f.close()
}
}
files := os.glob('test_dir/t*') or { panic(err) }
assert files.len == 5
assert os.base(files[0]) == 'test'
for i in 0 .. 3 {
os.rm('test_dir/test' + i.str()) or { panic(err) }
}
os.rm('test_dir/test0_another') or { panic(err) }
os.rm('test_dir/test') or { panic(err) }
os.rmdir_all('test_dir') or { panic(err) }
}

View File

@ -94,6 +94,57 @@ fn init_os_args_wide(argc int, argv &&byte) []string {
return args_ 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) ? {
$if debug {
// FindFirstFile() and FindNextFile() both have a globbing function.
// Unfortunately this is not as pronounced as under Unix, but should provide some functionality
eprintln('os.glob() does not have all the features on Windows as it has on Unix operating systems')
}
mut find_file_data := Win32finddata{}
wpattern := pattern.replace('/', '\\').to_wide()
h_find_files := C.FindFirstFile(wpattern, voidptr(&find_file_data))
defer {
C.FindClose(h_find_files)
}
if h_find_files == C.INVALID_HANDLE_VALUE {
return error('os.glob(): Could not get a file handle: ' +
get_error_msg(int(C.GetLastError())))
}
// save first finding
fname := unsafe { string_from_wide(&find_file_data.c_file_name[0]) }
if fname !in ['.', '..'] {
mut fp := fname.replace('\\', '/')
if find_file_data.dw_file_attributes & u32(C.FILE_ATTRIBUTE_DIRECTORY) > 0 {
fp += '/'
}
matches << fp
}
// check and save next findings
for i := 0; C.FindNextFile(h_find_files, voidptr(&find_file_data)) > 0; i++ {
filename := unsafe { string_from_wide(&find_file_data.c_file_name[0]) }
if filename in ['.', '..'] {
continue
}
mut fpath := filename.replace('\\', '/')
if find_file_data.dw_file_attributes & u32(C.FILE_ATTRIBUTE_DIRECTORY) > 0 {
fpath += '/'
}
matches << fpath
}
}
pub fn ls(path string) ?[]string { pub fn ls(path string) ?[]string {
mut find_file_data := Win32finddata{} mut find_file_data := Win32finddata{}
mut dir_files := []string{} mut dir_files := []string{}