mirror of
https://github.com/sbrow/envr.git
synced 2026-06-27 18:48:33 -04:00
117 lines
2.4 KiB
Odin
117 lines
2.4 KiB
Odin
package findr
|
|
|
|
import "core:fmt"
|
|
import "core:os"
|
|
import "core:strings"
|
|
import "core:sys/linux"
|
|
|
|
RawEntry :: struct {
|
|
name: string,
|
|
type: linux.Dirent_Type,
|
|
}
|
|
|
|
walk :: proc(root: string, results: ^[dynamic]string) {
|
|
walk_dir(root, results)
|
|
}
|
|
|
|
walk_dir :: proc(dir_path: string, results: ^[dynamic]string) {
|
|
cpath := strings.clone_to_cstring(dir_path)
|
|
if cpath == nil do return
|
|
defer delete(cpath)
|
|
|
|
fd, err := linux.open(cpath, {.DIRECTORY, .CLOEXEC})
|
|
if err != .NONE do return
|
|
defer linux.close(fd)
|
|
|
|
buf: [8192]u8
|
|
has_git := false
|
|
|
|
entries := make([dynamic]RawEntry)
|
|
defer {
|
|
for &entry in entries {
|
|
delete(entry.name)
|
|
}
|
|
delete(entries)
|
|
}
|
|
|
|
for {
|
|
n, errno := linux.getdents(fd, buf[:])
|
|
if n <= 0 || errno != .NONE do break
|
|
|
|
offs := 0
|
|
for d in linux.dirent_iterate_buf(buf[:n], &offs) {
|
|
name := linux.dirent_name(d)
|
|
if name == "." || name == ".." do continue
|
|
|
|
if name == ".git" && d.type == .DIR {
|
|
has_git = true
|
|
}
|
|
|
|
cloned := strings.clone(name)
|
|
append(&entries, RawEntry{name = cloned, type = d.type})
|
|
}
|
|
}
|
|
|
|
if has_git {
|
|
gi := load_gitignore(dir_path)
|
|
defer if gi != nil {
|
|
destroy(gi)
|
|
free(gi)
|
|
}
|
|
|
|
for entry in entries {
|
|
if entry.name == ".git" do continue
|
|
is_dir := entry.type == .DIR
|
|
if gi != nil && is_ignored(gi, entry.name, is_dir) {
|
|
if !is_dir {
|
|
full_path := join_path(dir_path, entry.name)
|
|
append(results, full_path)
|
|
}
|
|
continue
|
|
}
|
|
if is_dir {
|
|
child_path := join_path(dir_path, entry.name)
|
|
walk_dir(child_path, results)
|
|
delete(child_path)
|
|
}
|
|
}
|
|
} else {
|
|
for entry in entries {
|
|
if entry.type == .DIR {
|
|
child_path := join_path(dir_path, entry.name)
|
|
walk_dir(child_path, results)
|
|
delete(child_path)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
load_gitignore :: proc(dir_path: string) -> ^Gitignore {
|
|
gi_path := join_path(dir_path, ".gitignore")
|
|
defer delete(gi_path)
|
|
|
|
data, err := os.read_entire_file_from_path(gi_path, context.allocator)
|
|
if err != nil do return nil
|
|
|
|
gi := new(Gitignore)
|
|
gi^ = parse(string(data))
|
|
delete(data)
|
|
return gi
|
|
}
|
|
|
|
join_path :: proc(parent, child: string) -> string {
|
|
b: strings.Builder
|
|
strings.builder_init(&b)
|
|
defer strings.builder_destroy(&b)
|
|
|
|
fmt.sbprintf(&b, "%s", parent)
|
|
if len(parent) == 0 || parent[len(parent) - 1] != '/' {
|
|
fmt.sbprintf(&b, "/")
|
|
}
|
|
fmt.sbprintf(&b, "%s", child)
|
|
|
|
s := strings.to_string(b)
|
|
result, _ := strings.clone(s)
|
|
return result
|
|
}
|