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:
parent
21917f5b00
commit
a689641c1b
@ -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)) }
|
||||
|
67
vlib/os/os.v
67
vlib/os/os.v
@ -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` ...
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user