From 898b29b86ae55bfab2c71cb9ed1b0bac42772c13 Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Thu, 17 Jul 2025 12:22:04 -0500 Subject: [PATCH] update fs module --- modules/fs/fs.go | 978 ++++++++++++++++++++--------------------------- 1 file changed, 416 insertions(+), 562 deletions(-) diff --git a/modules/fs/fs.go b/modules/fs/fs.go index 898091c..0a40a2d 100644 --- a/modules/fs/fs.go +++ b/modules/fs/fs.go @@ -11,568 +11,422 @@ import ( luajit "git.sharkk.net/Sky/LuaJIT-to-Go" ) -func GetFSFunctions() map[string]luajit.GoFunction { +func GetFunctionList() map[string]luajit.GoFunction { return map[string]luajit.GoFunction{ - "file_exists": func(s *luajit.State) int { - if err := s.CheckMinArgs(1); err != nil { - return s.PushError("file_exists: %v", err) - } - path, err := s.SafeToString(1) - if err != nil { - return s.PushError("file_exists: argument must be a string") - } - _, err = os.Stat(path) - s.PushBoolean(err == nil) - return 1 - }, - - "file_size": func(s *luajit.State) int { - if err := s.CheckMinArgs(1); err != nil { - return s.PushError("file_size: %v", err) - } - path, err := s.SafeToString(1) - if err != nil { - return s.PushError("file_size: argument must be a string") - } - info, err := os.Stat(path) - if err != nil { - s.PushNumber(-1) - return 1 - } - s.PushNumber(float64(info.Size())) - return 1 - }, - - "file_is_dir": func(s *luajit.State) int { - if err := s.CheckMinArgs(1); err != nil { - return s.PushError("file_is_dir: %v", err) - } - path, err := s.SafeToString(1) - if err != nil { - return s.PushError("file_is_dir: argument must be a string") - } - info, err := os.Stat(path) - if err != nil { - s.PushBoolean(false) - return 1 - } - s.PushBoolean(info.IsDir()) - return 1 - }, - - "file_read": func(s *luajit.State) int { - if err := s.CheckMinArgs(1); err != nil { - return s.PushError("file_read: %v", err) - } - path, err := s.SafeToString(1) - if err != nil { - return s.PushError("file_read: argument must be a string") - } - data, err := os.ReadFile(path) - if err != nil { - s.PushNil() - s.PushString(err.Error()) - return 2 - } - s.PushString(string(data)) - return 1 - }, - - "file_write": func(s *luajit.State) int { - if err := s.CheckExactArgs(2); err != nil { - return s.PushError("file_write: %v", err) - } - path, err := s.SafeToString(1) - if err != nil { - return s.PushError("file_write: first argument must be a string") - } - content, err := s.SafeToString(2) - if err != nil { - return s.PushError("file_write: second argument must be a string") - } - err = os.WriteFile(path, []byte(content), 0644) - if err != nil { - s.PushBoolean(false) - s.PushString(err.Error()) - return 2 - } - s.PushBoolean(true) - return 1 - }, - - "file_append": func(s *luajit.State) int { - if err := s.CheckExactArgs(2); err != nil { - return s.PushError("file_append: %v", err) - } - path, err := s.SafeToString(1) - if err != nil { - return s.PushError("file_append: first argument must be a string") - } - content, err := s.SafeToString(2) - if err != nil { - return s.PushError("file_append: second argument must be a string") - } - file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - s.PushBoolean(false) - s.PushString(err.Error()) - return 2 - } - defer file.Close() - _, err = file.WriteString(content) - if err != nil { - s.PushBoolean(false) - s.PushString(err.Error()) - return 2 - } - s.PushBoolean(true) - return 1 - }, - - "file_copy": func(s *luajit.State) int { - if err := s.CheckExactArgs(2); err != nil { - return s.PushError("file_copy: %v", err) - } - src, err := s.SafeToString(1) - if err != nil { - return s.PushError("file_copy: first argument must be a string") - } - dst, err := s.SafeToString(2) - if err != nil { - return s.PushError("file_copy: second argument must be a string") - } - - srcFile, err := os.Open(src) - if err != nil { - s.PushBoolean(false) - s.PushString(err.Error()) - return 2 - } - defer srcFile.Close() - - dstFile, err := os.Create(dst) - if err != nil { - s.PushBoolean(false) - s.PushString(err.Error()) - return 2 - } - defer dstFile.Close() - - _, err = io.Copy(dstFile, srcFile) - if err != nil { - s.PushBoolean(false) - s.PushString(err.Error()) - return 2 - } - s.PushBoolean(true) - return 1 - }, - - "file_move": func(s *luajit.State) int { - if err := s.CheckExactArgs(2); err != nil { - return s.PushError("file_move: %v", err) - } - src, err := s.SafeToString(1) - if err != nil { - return s.PushError("file_move: first argument must be a string") - } - dst, err := s.SafeToString(2) - if err != nil { - return s.PushError("file_move: second argument must be a string") - } - err = os.Rename(src, dst) - if err != nil { - s.PushBoolean(false) - s.PushString(err.Error()) - return 2 - } - s.PushBoolean(true) - return 1 - }, - - "file_delete": func(s *luajit.State) int { - if err := s.CheckMinArgs(1); err != nil { - return s.PushError("file_delete: %v", err) - } - path, err := s.SafeToString(1) - if err != nil { - return s.PushError("file_delete: argument must be a string") - } - err = os.Remove(path) - if err != nil { - s.PushBoolean(false) - s.PushString(err.Error()) - return 2 - } - s.PushBoolean(true) - return 1 - }, - - "file_mtime": func(s *luajit.State) int { - if err := s.CheckMinArgs(1); err != nil { - return s.PushError("file_mtime: %v", err) - } - path, err := s.SafeToString(1) - if err != nil { - return s.PushError("file_mtime: argument must be a string") - } - info, err := os.Stat(path) - if err != nil { - s.PushNumber(-1) - return 1 - } - s.PushNumber(float64(info.ModTime().Unix())) - return 1 - }, - - "dir_create": func(s *luajit.State) int { - if err := s.CheckMinArgs(1); err != nil { - return s.PushError("dir_create: %v", err) - } - path, err := s.SafeToString(1) - if err != nil { - return s.PushError("dir_create: argument must be a string") - } - err = os.MkdirAll(path, 0755) - if err != nil { - s.PushBoolean(false) - s.PushString(err.Error()) - return 2 - } - s.PushBoolean(true) - return 1 - }, - - "dir_remove": func(s *luajit.State) int { - if err := s.CheckMinArgs(1); err != nil { - return s.PushError("dir_remove: %v", err) - } - path, err := s.SafeToString(1) - if err != nil { - return s.PushError("dir_remove: argument must be a string") - } - err = os.RemoveAll(path) - if err != nil { - s.PushBoolean(false) - s.PushString(err.Error()) - return 2 - } - s.PushBoolean(true) - return 1 - }, - - "dir_list": func(s *luajit.State) int { - if err := s.CheckMinArgs(1); err != nil { - return s.PushError("dir_list: %v", err) - } - path, err := s.SafeToString(1) - if err != nil { - return s.PushError("dir_list: argument must be a string") - } - entries, err := os.ReadDir(path) - if err != nil { - s.PushNil() - s.PushString(err.Error()) - return 2 - } - - s.CreateTable(len(entries), 0) - for i, entry := range entries { - s.PushNumber(float64(i + 1)) - s.CreateTable(0, 4) - - s.PushString("name") - s.PushString(entry.Name()) - s.SetTable(-3) - - s.PushString("is_dir") - s.PushBoolean(entry.IsDir()) - s.SetTable(-3) - - if info, err := entry.Info(); err == nil { - s.PushString("size") - s.PushNumber(float64(info.Size())) - s.SetTable(-3) - - s.PushString("mtime") - s.PushNumber(float64(info.ModTime().Unix())) - s.SetTable(-3) - } - - s.SetTable(-3) - } - return 1 - }, - - "path_join": func(s *luajit.State) int { - var parts []string - for i := 1; i <= s.GetTop(); i++ { - part, err := s.SafeToString(i) - if err != nil { - return s.PushError("path_join: argument %d must be a string", i) - } - parts = append(parts, part) - } - s.PushString(filepath.Join(parts...)) - return 1 - }, - - "path_dir": func(s *luajit.State) int { - if err := s.CheckMinArgs(1); err != nil { - return s.PushError("path_dir: %v", err) - } - path, err := s.SafeToString(1) - if err != nil { - return s.PushError("path_dir: argument must be a string") - } - s.PushString(filepath.Dir(path)) - return 1 - }, - - "path_basename": func(s *luajit.State) int { - if err := s.CheckMinArgs(1); err != nil { - return s.PushError("path_basename: %v", err) - } - path, err := s.SafeToString(1) - if err != nil { - return s.PushError("path_basename: argument must be a string") - } - s.PushString(filepath.Base(path)) - return 1 - }, - - "path_ext": func(s *luajit.State) int { - if err := s.CheckMinArgs(1); err != nil { - return s.PushError("path_ext: %v", err) - } - path, err := s.SafeToString(1) - if err != nil { - return s.PushError("path_ext: argument must be a string") - } - s.PushString(filepath.Ext(path)) - return 1 - }, - - "path_abs": func(s *luajit.State) int { - if err := s.CheckMinArgs(1); err != nil { - return s.PushError("path_abs: %v", err) - } - path, err := s.SafeToString(1) - if err != nil { - return s.PushError("path_abs: argument must be a string") - } - abs, err := filepath.Abs(path) - if err != nil { - s.PushNil() - s.PushString(err.Error()) - return 2 - } - s.PushString(abs) - return 1 - }, - - "path_clean": func(s *luajit.State) int { - if err := s.CheckMinArgs(1); err != nil { - return s.PushError("path_clean: %v", err) - } - path, err := s.SafeToString(1) - if err != nil { - return s.PushError("path_clean: argument must be a string") - } - s.PushString(filepath.Clean(path)) - return 1 - }, - - "path_split": func(s *luajit.State) int { - if err := s.CheckMinArgs(1); err != nil { - return s.PushError("path_split: %v", err) - } - path, err := s.SafeToString(1) - if err != nil { - return s.PushError("path_split: argument must be a string") - } - dir, file := filepath.Split(path) - s.PushString(dir) - s.PushString(file) - return 2 - }, - - "temp_file": func(s *luajit.State) int { - var prefix string - if s.GetTop() >= 1 { - if p, err := s.SafeToString(1); err == nil { - prefix = p - } - } - - file, err := os.CreateTemp("", prefix) - if err != nil { - s.PushNil() - s.PushString(err.Error()) - return 2 - } - defer file.Close() - - s.PushString(file.Name()) - return 1 - }, - - "temp_dir": func(s *luajit.State) int { - var prefix string - if s.GetTop() >= 1 { - if p, err := s.SafeToString(1); err == nil { - prefix = p - } - } - - dir, err := os.MkdirTemp("", prefix) - if err != nil { - s.PushNil() - s.PushString(err.Error()) - return 2 - } - - s.PushString(dir) - return 1 - }, - - "glob": func(s *luajit.State) int { - if err := s.CheckMinArgs(1); err != nil { - return s.PushError("glob: %v", err) - } - pattern, err := s.SafeToString(1) - if err != nil { - return s.PushError("glob: argument must be a string") - } - - matches, err := filepath.Glob(pattern) - if err != nil { - s.PushNil() - s.PushString(err.Error()) - return 2 - } - - if err := s.PushValue(matches); err != nil { - return s.PushError("glob: failed to push result: %v", err) - } - return 1 - }, - - "walk": func(s *luajit.State) int { - if err := s.CheckMinArgs(1); err != nil { - return s.PushError("walk: %v", err) - } - root, err := s.SafeToString(1) - if err != nil { - return s.PushError("walk: argument must be a string") - } - - var files []string - err = filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { - if err != nil { - return nil // Skip errors - } - files = append(files, path) - return nil - }) - - if err != nil { - s.PushNil() - s.PushString(err.Error()) - return 2 - } - - if err := s.PushValue(files); err != nil { - return s.PushError("walk: failed to push result: %v", err) - } - return 1 - }, - - "getcwd": func(s *luajit.State) int { - cwd, err := os.Getwd() - if err != nil { - s.PushNil() - s.PushString(err.Error()) - return 2 - } - s.PushString(cwd) - return 1 - }, - - "chdir": func(s *luajit.State) int { - if err := s.CheckMinArgs(1); err != nil { - return s.PushError("chdir: %v", err) - } - path, err := s.SafeToString(1) - if err != nil { - return s.PushError("chdir: argument must be a string") - } - err = os.Chdir(path) - if err != nil { - s.PushBoolean(false) - s.PushString(err.Error()) - return 2 - } - s.PushBoolean(true) - return 1 - }, - - "file_lines": func(s *luajit.State) int { - if err := s.CheckMinArgs(1); err != nil { - return s.PushError("file_lines: %v", err) - } - path, err := s.SafeToString(1) - if err != nil { - return s.PushError("file_lines: argument must be a string") - } - - data, err := os.ReadFile(path) - if err != nil { - s.PushNil() - s.PushString(err.Error()) - return 2 - } - - lines := strings.Split(string(data), "\n") - // Remove empty last line if file ends with newline - if len(lines) > 0 && lines[len(lines)-1] == "" { - lines = lines[:len(lines)-1] - } - - if err := s.PushValue(lines); err != nil { - return s.PushError("file_lines: failed to push result: %v", err) - } - return 1 - }, - - "touch": func(s *luajit.State) int { - if err := s.CheckMinArgs(1); err != nil { - return s.PushError("touch: %v", err) - } - path, err := s.SafeToString(1) - if err != nil { - return s.PushError("touch: argument must be a string") - } - - now := time.Now() - err = os.Chtimes(path, now, now) - if os.IsNotExist(err) { - // Create file if it doesn't exist - file, err := os.Create(path) - if err != nil { - s.PushBoolean(false) - s.PushString(err.Error()) - return 2 - } - file.Close() - } else if err != nil { - s.PushBoolean(false) - s.PushString(err.Error()) - return 2 - } - - s.PushBoolean(true) - return 1 - }, + "file_exists": file_exists, + "file_size": file_size, + "file_is_dir": file_is_dir, + "file_read": file_read, + "file_write": file_write, + "file_append": file_append, + "file_copy": file_copy, + "file_move": file_move, + "file_delete": file_delete, + "file_mtime": file_mtime, + "dir_create": dir_create, + "dir_remove": dir_remove, + "dir_list": dir_list, + "path_join": path_join, + "path_dir": path_dir, + "path_basename": path_basename, + "path_ext": path_ext, + "path_abs": path_abs, + "path_clean": path_clean, + "path_split": path_split, + "temp_file": temp_file, + "temp_dir": temp_dir, + "glob": glob, + "walk": walk, + "getcwd": getcwd, + "chdir": chdir, + "file_lines": file_lines, + "touch": touch, } } + +func file_exists(s *luajit.State) int { + path := s.ToString(1) + _, err := os.Stat(path) + s.PushBoolean(err == nil) + return 1 +} + +func file_size(s *luajit.State) int { + path := s.ToString(1) + info, err := os.Stat(path) + if err != nil { + s.PushNumber(-1) + return 1 + } + s.PushNumber(float64(info.Size())) + return 1 +} + +func file_is_dir(s *luajit.State) int { + path := s.ToString(1) + info, err := os.Stat(path) + if err != nil { + s.PushBoolean(false) + return 1 + } + s.PushBoolean(info.IsDir()) + return 1 +} + +func file_read(s *luajit.State) int { + path := s.ToString(1) + data, err := os.ReadFile(path) + if err != nil { + s.PushNil() + s.PushString(err.Error()) + return 2 + } + s.PushString(string(data)) + return 1 +} + +func file_write(s *luajit.State) int { + path := s.ToString(1) + content := s.ToString(2) + err := os.WriteFile(path, []byte(content), 0644) + if err != nil { + s.PushBoolean(false) + s.PushString(err.Error()) + return 2 + } + s.PushBoolean(true) + return 1 +} + +func file_append(s *luajit.State) int { + path := s.ToString(1) + content := s.ToString(2) + file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + s.PushBoolean(false) + s.PushString(err.Error()) + return 2 + } + defer file.Close() + _, err = file.WriteString(content) + if err != nil { + s.PushBoolean(false) + s.PushString(err.Error()) + return 2 + } + s.PushBoolean(true) + return 1 +} + +func file_copy(s *luajit.State) int { + src := s.ToString(1) + dst := s.ToString(2) + + srcFile, err := os.Open(src) + if err != nil { + s.PushBoolean(false) + s.PushString(err.Error()) + return 2 + } + defer srcFile.Close() + + dstFile, err := os.Create(dst) + if err != nil { + s.PushBoolean(false) + s.PushString(err.Error()) + return 2 + } + defer dstFile.Close() + + _, err = io.Copy(dstFile, srcFile) + if err != nil { + s.PushBoolean(false) + s.PushString(err.Error()) + return 2 + } + s.PushBoolean(true) + return 1 +} + +func file_move(s *luajit.State) int { + src := s.ToString(1) + dst := s.ToString(2) + err := os.Rename(src, dst) + if err != nil { + s.PushBoolean(false) + s.PushString(err.Error()) + return 2 + } + s.PushBoolean(true) + return 1 +} + +func file_delete(s *luajit.State) int { + path := s.ToString(1) + err := os.Remove(path) + if err != nil { + s.PushBoolean(false) + s.PushString(err.Error()) + return 2 + } + s.PushBoolean(true) + return 1 +} + +func file_mtime(s *luajit.State) int { + path := s.ToString(1) + info, err := os.Stat(path) + if err != nil { + s.PushNumber(-1) + return 1 + } + s.PushNumber(float64(info.ModTime().Unix())) + return 1 +} + +func dir_create(s *luajit.State) int { + path := s.ToString(1) + err := os.MkdirAll(path, 0755) + if err != nil { + s.PushBoolean(false) + s.PushString(err.Error()) + return 2 + } + s.PushBoolean(true) + return 1 +} + +func dir_remove(s *luajit.State) int { + path := s.ToString(1) + err := os.RemoveAll(path) + if err != nil { + s.PushBoolean(false) + s.PushString(err.Error()) + return 2 + } + s.PushBoolean(true) + return 1 +} + +func dir_list(s *luajit.State) int { + path := s.ToString(1) + entries, err := os.ReadDir(path) + if err != nil { + s.PushNil() + s.PushString(err.Error()) + return 2 + } + + s.CreateTable(len(entries), 0) + for i, entry := range entries { + s.PushNumber(float64(i + 1)) + s.CreateTable(0, 4) + + s.PushString("name") + s.PushString(entry.Name()) + s.SetTable(-3) + + s.PushString("is_dir") + s.PushBoolean(entry.IsDir()) + s.SetTable(-3) + + if info, err := entry.Info(); err == nil { + s.PushString("size") + s.PushNumber(float64(info.Size())) + s.SetTable(-3) + + s.PushString("mtime") + s.PushNumber(float64(info.ModTime().Unix())) + s.SetTable(-3) + } + + s.SetTable(-3) + } + return 1 +} + +func path_join(s *luajit.State) int { + var parts []string + for i := 1; i <= s.GetTop(); i++ { + parts = append(parts, s.ToString(i)) + } + s.PushString(filepath.Join(parts...)) + return 1 +} + +func path_dir(s *luajit.State) int { + path := s.ToString(1) + s.PushString(filepath.Dir(path)) + return 1 +} + +func path_basename(s *luajit.State) int { + path := s.ToString(1) + s.PushString(filepath.Base(path)) + return 1 +} + +func path_ext(s *luajit.State) int { + path := s.ToString(1) + s.PushString(filepath.Ext(path)) + return 1 +} + +func path_abs(s *luajit.State) int { + path := s.ToString(1) + abs, err := filepath.Abs(path) + if err != nil { + s.PushNil() + s.PushString(err.Error()) + return 2 + } + s.PushString(abs) + return 1 +} + +func path_clean(s *luajit.State) int { + path := s.ToString(1) + s.PushString(filepath.Clean(path)) + return 1 +} + +func path_split(s *luajit.State) int { + path := s.ToString(1) + dir, file := filepath.Split(path) + s.PushString(dir) + s.PushString(file) + return 2 +} + +func temp_file(s *luajit.State) int { + var prefix string + if s.GetTop() >= 1 { + prefix = s.ToString(1) + } + + file, err := os.CreateTemp("", prefix) + if err != nil { + s.PushNil() + s.PushString(err.Error()) + return 2 + } + defer file.Close() + + s.PushString(file.Name()) + return 1 +} + +func temp_dir(s *luajit.State) int { + var prefix string + if s.GetTop() >= 1 { + prefix = s.ToString(1) + } + + dir, err := os.MkdirTemp("", prefix) + if err != nil { + s.PushNil() + s.PushString(err.Error()) + return 2 + } + + s.PushString(dir) + return 1 +} + +func glob(s *luajit.State) int { + pattern := s.ToString(1) + matches, err := filepath.Glob(pattern) + if err != nil { + s.PushNil() + s.PushString(err.Error()) + return 2 + } + s.PushValue(matches) + return 1 +} + +func walk(s *luajit.State) int { + root := s.ToString(1) + var files []string + err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return nil // Skip errors + } + files = append(files, path) + return nil + }) + + if err != nil { + s.PushNil() + s.PushString(err.Error()) + return 2 + } + + s.PushValue(files) + return 1 +} + +func getcwd(s *luajit.State) int { + cwd, err := os.Getwd() + if err != nil { + s.PushNil() + s.PushString(err.Error()) + return 2 + } + s.PushString(cwd) + return 1 +} + +func chdir(s *luajit.State) int { + path := s.ToString(1) + err := os.Chdir(path) + if err != nil { + s.PushBoolean(false) + s.PushString(err.Error()) + return 2 + } + s.PushBoolean(true) + return 1 +} + +func file_lines(s *luajit.State) int { + path := s.ToString(1) + data, err := os.ReadFile(path) + if err != nil { + s.PushNil() + s.PushString(err.Error()) + return 2 + } + + lines := strings.Split(string(data), "\n") + // Remove empty last line if file ends with newline + if len(lines) > 0 && lines[len(lines)-1] == "" { + lines = lines[:len(lines)-1] + } + + s.PushValue(lines) + return 1 +} + +func touch(s *luajit.State) int { + path := s.ToString(1) + now := time.Now() + err := os.Chtimes(path, now, now) + if os.IsNotExist(err) { + // Create file if it doesn't exist + file, err := os.Create(path) + if err != nil { + s.PushBoolean(false) + s.PushString(err.Error()) + return 2 + } + file.Close() + } else if err != nil { + s.PushBoolean(false) + s.PushString(err.Error()) + return 2 + } + + s.PushBoolean(true) + return 1 +}