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
3 changed files with 96 additions and 24 deletions

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` ...