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`.
|
// chdir changes the current working directory to the new directory in `path`.
|
||||||
pub fn chdir(path string) ? {
|
pub fn chdir(path string) ? {
|
||||||
ret := $if windows { C._wchdir(path.to_wide()) } $else { C.chdir(&char(path.str)) }
|
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`.
|
// walk traverses the given directory `path`.
|
||||||
// When a file is encountred it will call the
|
// When a file is encountred, it will call the callback `f` with current file as argument.
|
||||||
// callback function `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)) {
|
pub fn walk(path string, f fn (string)) {
|
||||||
if path.len == 0 {
|
if path.len == 0 {
|
||||||
return
|
return
|
||||||
@ -578,29 +579,36 @@ pub fn walk(path string, f fn (string)) {
|
|||||||
if !is_dir(path) {
|
if !is_dir(path) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
mut files := ls(path) or { return }
|
mut remaining := []string{cap: 1000}
|
||||||
mut local_path_separator := path_separator
|
clean_path := path.trim_right(path_separator)
|
||||||
if path.ends_with(path_separator) {
|
$if windows {
|
||||||
local_path_separator = ''
|
remaining << clean_path.replace('/', '\\')
|
||||||
|
} $else {
|
||||||
|
remaining << clean_path
|
||||||
}
|
}
|
||||||
for file in files {
|
for remaining.len > 0 {
|
||||||
p := path + local_path_separator + file
|
cpath := remaining.pop()
|
||||||
if is_dir(p) && !is_link(p) {
|
pkind := kind_of_existing_path(cpath)
|
||||||
walk(p, f)
|
if pkind.is_link || !pkind.is_dir {
|
||||||
} else if exists(p) {
|
f(cpath)
|
||||||
f(p)
|
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
|
// FnWalkContextCB is used to define the callback functions, passed to os.walk_context
|
||||||
pub type FnWalkContextCB = fn (voidptr, string)
|
pub type FnWalkContextCB = fn (voidptr, string)
|
||||||
|
|
||||||
// walk_with_context traverses the given directory `path`.
|
// 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,
|
// passing it the arbitrary `context` in its first parameter,
|
||||||
// and the path to the file in its second 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) {
|
pub fn walk_with_context(path string, context voidptr, fcb FnWalkContextCB) {
|
||||||
if path.len == 0 {
|
if path.len == 0 {
|
||||||
return
|
return
|
||||||
@ -608,19 +616,30 @@ pub fn walk_with_context(path string, context voidptr, fcb FnWalkContextCB) {
|
|||||||
if !is_dir(path) {
|
if !is_dir(path) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
mut files := ls(path) or { return }
|
mut remaining := []string{cap: 1000}
|
||||||
mut local_path_separator := path_separator
|
clean_path := path.trim_right(path_separator)
|
||||||
if path.ends_with(path_separator) {
|
$if windows {
|
||||||
local_path_separator = ''
|
remaining << clean_path.replace('/', '\\')
|
||||||
|
} $else {
|
||||||
|
remaining << clean_path
|
||||||
}
|
}
|
||||||
for file in files {
|
mut loops := 0
|
||||||
p := path + local_path_separator + file
|
for remaining.len > 0 {
|
||||||
fcb(context, p)
|
loops++
|
||||||
if is_dir(p) && !is_link(p) {
|
cpath := remaining.pop()
|
||||||
walk_with_context(p, context, fcb)
|
// 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` ...
|
// log will print "os.log: "+`s` ...
|
||||||
|
@ -29,6 +29,24 @@ pub fn is_link(path string) bool {
|
|||||||
return res
|
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 {
|
pub fn exists(path string) bool {
|
||||||
res := false
|
res := false
|
||||||
$if js_node {
|
$if js_node {
|
||||||
|
Loading…
Reference in New Issue
Block a user