1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

os: rewrite os.walk and os.walk_with_context to use iteration, instead of recursion

This commit is contained in:
Delyan Angelov 2022-08-22 17:10:46 +03:00
parent 21917f5b00
commit a689641c1b
No known key found for this signature in database
GPG Key ID: 66886C0F12D595ED
3 changed files with 96 additions and 24 deletions

View File

@ -738,6 +738,41 @@ pub fn is_link(path string) bool {
}
}
struct PathKind {
mut:
is_dir bool
is_link bool
}
fn kind_of_existing_path(path string) PathKind {
mut res := PathKind{}
$if windows {
attr := C.GetFileAttributesW(path.to_wide())
if attr != u32(C.INVALID_FILE_ATTRIBUTES) {
if (int(attr) & C.FILE_ATTRIBUTE_DIRECTORY) != 0 {
res.is_dir = true
}
if (int(attr) & 0x400) != 0 {
res.is_link = true
}
}
} $else {
statbuf := C.stat{}
// ref: https://code.woboq.org/gcc/include/sys/stat.h.html
res_stat := unsafe { C.lstat(&char(path.str), &statbuf) }
if res_stat == 0 {
kind := (int(statbuf.st_mode) & s_ifmt)
if kind == s_ifdir {
res.is_dir = true
}
if kind == s_iflnk {
res.is_link = true
}
}
}
return res
}
// chdir changes the current working directory to the new directory in `path`.
pub fn chdir(path string) ? {
ret := $if windows { C._wchdir(path.to_wide()) } $else { C.chdir(&char(path.str)) }

View File

@ -569,8 +569,9 @@ fn impl_walk_ext(path string, ext string, mut out []string) {
}
// walk traverses the given directory `path`.
// When a file is encountred it will call the
// callback function `f` with current file as argument.
// When a file is encountred, it will call the callback `f` with current file as argument.
// Note: walk can be called even for deeply nested folders,
// since it does not recurse, but processes them iteratively.
pub fn walk(path string, f fn (string)) {
if path.len == 0 {
return
@ -578,29 +579,36 @@ pub fn walk(path string, f fn (string)) {
if !is_dir(path) {
return
}
mut files := ls(path) or { return }
mut local_path_separator := path_separator
if path.ends_with(path_separator) {
local_path_separator = ''
mut remaining := []string{cap: 1000}
clean_path := path.trim_right(path_separator)
$if windows {
remaining << clean_path.replace('/', '\\')
} $else {
remaining << clean_path
}
for file in files {
p := path + local_path_separator + file
if is_dir(p) && !is_link(p) {
walk(p, f)
} else if exists(p) {
f(p)
for remaining.len > 0 {
cpath := remaining.pop()
pkind := kind_of_existing_path(cpath)
if pkind.is_link || !pkind.is_dir {
f(cpath)
continue
}
mut files := ls(cpath) or { continue }
for idx := files.len - 1; idx >= 0; idx-- {
remaining << cpath + path_separator + files[idx]
}
}
return
}
// FnWalkContextCB is used to define the callback functions, passed to os.walk_context
pub type FnWalkContextCB = fn (voidptr, string)
// walk_with_context traverses the given directory `path`.
// For each encountred file and directory, it will call your `fcb` callback,
// For each encountred file *and* directory, it will call your `fcb` callback,
// passing it the arbitrary `context` in its first parameter,
// and the path to the file in its second parameter.
// Note: walk_with_context can be called even for deeply nested folders,
// since it does not recurse, but processes them iteratively.
pub fn walk_with_context(path string, context voidptr, fcb FnWalkContextCB) {
if path.len == 0 {
return
@ -608,19 +616,30 @@ pub fn walk_with_context(path string, context voidptr, fcb FnWalkContextCB) {
if !is_dir(path) {
return
}
mut files := ls(path) or { return }
mut local_path_separator := path_separator
if path.ends_with(path_separator) {
local_path_separator = ''
mut remaining := []string{cap: 1000}
clean_path := path.trim_right(path_separator)
$if windows {
remaining << clean_path.replace('/', '\\')
} $else {
remaining << clean_path
}
for file in files {
p := path + local_path_separator + file
fcb(context, p)
if is_dir(p) && !is_link(p) {
walk_with_context(p, context, fcb)
mut loops := 0
for remaining.len > 0 {
loops++
cpath := remaining.pop()
// call `fcb` for everything, but the initial folder:
if loops > 1 {
fcb(context, cpath)
}
pkind := kind_of_existing_path(cpath)
if pkind.is_link || !pkind.is_dir {
continue
}
mut files := ls(cpath) or { continue }
for idx := files.len - 1; idx >= 0; idx-- {
remaining << cpath + path_separator + files[idx]
}
}
return
}
// log will print "os.log: "+`s` ...

View File

@ -29,6 +29,24 @@ pub fn is_link(path string) bool {
return res
}
struct PathKind {
is_dir bool
is_link bool
}
fn kind_of_existing_path(path string) PathKind {
is_link := false
is_dir := false
$if js_node {
#is_link.val = $fs.existsSync(path.str) && $fs.lstatSync(path.str).isSymbolicLink()
#is_dir.val = $fs.existsSync(path,str) && $fs.lstatSync(path.str).isDirectory()
}
return PathKind{
is_dir: is_dir
is_link: is_link
}
}
pub fn exists(path string) bool {
res := false
$if js_node {