From 03738d3a70d4d04217859e9d3cf4fed02f0abb51 Mon Sep 17 00:00:00 2001 From: Max Qian Date: Mon, 6 May 2024 20:31:32 +0800 Subject: [PATCH] add stat and glob module --- include/pocketpy/io.h | 2 + include/pocketpy/vm.h | 1 + src/io.cpp | 223 ++++++++++++++++++++++++++++++++++++++++++ src/pocketpy.cpp | 2 + 4 files changed, 228 insertions(+) diff --git a/include/pocketpy/io.h b/include/pocketpy/io.h index 675c6cbc..be6ab003 100644 --- a/include/pocketpy/io.h +++ b/include/pocketpy/io.h @@ -6,4 +6,6 @@ namespace pkpy{ unsigned char* _default_import_handler(const char*, int*); void add_module_os(VM* vm); void add_module_io(VM* vm); + void add_module_stat(VM* vm); + void add_module_glob(VM* vm); } diff --git a/include/pocketpy/vm.h b/include/pocketpy/vm.h index 603f1c09..4ef9e71c 100644 --- a/include/pocketpy/vm.h +++ b/include/pocketpy/vm.h @@ -331,6 +331,7 @@ public: void _error(PyObject*); void StackOverflowError() { __builtin_error("StackOverflowError"); } void IOError(const Str& msg) { __builtin_error("IOError", msg); } + void OSError(const Str& msg) { __builtin_error("OSError", msg); } void NotImplementedError(){ __builtin_error("NotImplementedError"); } void TypeError(const Str& msg){ __builtin_error("TypeError", msg); } void TypeError(Type expected, Type actual) { TypeError("expected " + _type_name(vm, expected).escape() + ", got " + _type_name(vm, actual).escape()); } diff --git a/src/io.cpp b/src/io.cpp index f29d9135..a3874d76 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -3,6 +3,14 @@ #if PK_ENABLE_OS #include #include +#include +#include +#ifdef _WIN32 +#include +#else +#include +#include +#endif #endif namespace pkpy{ @@ -238,6 +246,221 @@ void add_module_os(VM* vm){ return VAR(std::filesystem::absolute(path).string()); }); } + +void add_module_stat(VM* vm){ + PyObject* mod = vm->new_module("stat"); + + mod->attr().set("S_IFDIR", VAR(0040000)); + mod->attr().set("S_IFCHR", VAR(0020000)); + mod->attr().set("S_IFBLK", VAR(0060000)); + mod->attr().set("S_IFREG", VAR(0100000)); + mod->attr().set("S_IFIFO", VAR(0010000)); + mod->attr().set("S_IFLNK", VAR(0120000)); + mod->attr().set("S_IFSOCK", VAR(0140000)); + + mod->attr().set("S_ISUID", VAR(04000)); + mod->attr().set("S_ISGID", VAR(02000)); + mod->attr().set("S_ISVTX", VAR(01000)); + + mod->attr().set("S_IRWXU", VAR(00700)); + mod->attr().set("S_IRUSR", VAR(00400)); + mod->attr().set("S_IWUSR", VAR(00200)); + mod->attr().set("S_IXUSR", VAR(00100)); + + mod->attr().set("S_IRWXG", VAR(00070)); + mod->attr().set("S_IRGRP", VAR(00040)); + mod->attr().set("S_IWGRP", VAR(00020)); + mod->attr().set("S_IXGRP", VAR(00010)); + + mod->attr().set("S_IRWXO", VAR(00007)); + mod->attr().set("S_IROTH", VAR(00004)); + mod->attr().set("S_IWOTH", VAR(00002)); + mod->attr().set("S_IXOTH", VAR(00001)); + + struct stat_result { + int st_mode; + uint64_t st_ino; + uint64_t st_dev; + uint64_t st_nlink; + uint64_t st_uid; + uint64_t st_gid; + int64_t st_size; + double st_atime; + double st_mtime; + double st_ctime; + + stat_result(const std::filesystem::file_status& status, const std::filesystem::directory_entry& entry) + : st_mode(static_cast(status.permissions())), + st_ino(entry.file_size()), + st_dev(0), + st_nlink(entry.hard_link_count()), + st_uid(0), + st_gid(0), + st_size(entry.file_size()), + st_atime(std::chrono::duration_cast(entry.last_write_time().time_since_epoch()).count()), + st_mtime(std::chrono::duration_cast(entry.last_write_time().time_since_epoch()).count()), + st_ctime(std::chrono::duration_cast(entry.last_write_time().time_since_epoch()).count()) {} + }; + + vm->bind_func(mod, "stat", 1, [](VM* vm, ArgsView args){ + std::filesystem::path path(CAST(Str&, args[0]).sv()); + std::filesystem::directory_entry entry; + try{ + entry = std::filesystem::directory_entry(path); + }catch(std::filesystem::filesystem_error&){ + vm->OSError(path.string()); + } + auto status = entry.status(); + stat_result result(status, entry); + + PyObject* stat_obj = vm->heap.gcnew(vm->tp_object); + stat_obj->attr().set("st_mode", VAR(result.st_mode)); + stat_obj->attr().set("st_ino", VAR(result.st_ino)); + stat_obj->attr().set("st_dev", VAR(result.st_dev)); + stat_obj->attr().set("st_nlink", VAR(result.st_nlink)); + stat_obj->attr().set("st_uid", VAR(result.st_uid)); + stat_obj->attr().set("st_gid", VAR(result.st_gid)); + stat_obj->attr().set("st_size", VAR(result.st_size)); + stat_obj->attr().set("st_atime", VAR(result.st_atime)); + stat_obj->attr().set("st_mtime", VAR(result.st_mtime)); + stat_obj->attr().set("st_ctime", VAR(result.st_ctime)); + + return stat_obj; + }); + + vm->bind_func(mod, "S_ISDIR", 1, [](VM* vm, ArgsView args){ + int mode = CAST(int, args[0]); + return VAR((mode & 0170000) == 0040000); + }); + + vm->bind_func(mod, "S_ISCHR", 1, [](VM* vm, ArgsView args){ + int mode = CAST(int, args[0]); + return VAR((mode & 0170000) == 0020000); + }); + + vm->bind_func(mod, "S_ISBLK", 1, [](VM* vm, ArgsView args){ + int mode = CAST(int, args[0]); + return VAR((mode & 0170000) == 0060000); + }); + + vm->bind_func(mod, "S_ISREG", 1, [](VM* vm, ArgsView args){ + int mode = CAST(int, args[0]); + return VAR((mode & 0170000) == 0100000); + }); + + vm->bind_func(mod, "S_ISFIFO", 1, [](VM* vm, ArgsView args){ + int mode = CAST(int, args[0]); + return VAR((mode & 0170000) == 0010000); + }); + + vm->bind_func(mod, "S_ISLNK", 1, [](VM* vm, ArgsView args){ + int mode = CAST(int, args[0]); + return VAR((mode & 0170000) == 0120000); + }); + + vm->bind_func(mod, "S_ISSOCK", 1, [](VM* vm, ArgsView args){ + int mode = CAST(int, args[0]); + return VAR((mode & 0170000) == 0140000); + }); + + vm->bind_func(mod, "S_IMODE", 1, [](VM* vm, ArgsView args){ + int mode = CAST(int, args[0]); + return VAR(mode & 07777); + }); + + vm->bind_func(mod, "S_IFMT", 1, [](VM* vm, ArgsView args){ + int mode = CAST(int, args[0]); + return VAR(mode & 0170000); + }); +} + +void add_module_glob(VM* vm){ + PyObject* mod = vm->new_module("glob"); + + vm->bind_func(mod, "glob", 1, [](VM* vm, ArgsView args){ + std::string_view pattern = CAST(Str&, args[0]).sv(); + std::optional root_dir_opt = std::nullopt; + bool recursive = false; + bool include_hidden = false; + + if (args.size() > 1) + root_dir_opt = CAST(Str&, args[1]).sv(); + if (args.size() > 2) + recursive = CAST(bool, args[2]); + if (args.size() > 3) + include_hidden = CAST(bool, args[3]); + std::vector result; + + std::string root_dir = root_dir_opt ? std::string(*root_dir_opt) : "."; + std::filesystem::path base_dir(root_dir); + std::filesystem::path search_pattern(pattern.data()); + + std::function search_dir = [&](const std::filesystem::path& dir) { + #ifdef _WIN32 + WIN32_FIND_DATAA data; + std::string pattern_str = (dir / search_pattern).string(); + HANDLE hFind = FindFirstFileA(pattern_str.c_str(), &data); + if (hFind != INVALID_HANDLE_VALUE) { + do { + if (include_hidden || data.cFileName[0] != '.') { + result.push_back((dir / data.cFileName).string()); + } + } while (FindNextFileA(hFind, &data)); + FindClose(hFind); + } + #else + DIR* dp = opendir(dir.c_str()); + if (dp != nullptr) { + dirent* entry; + while ((entry = readdir(dp)) != nullptr) { + std::filesystem::path entry_path = dir / entry->d_name; + if (fnmatch(search_pattern.c_str(), entry->d_name, FNM_PATHNAME) == 0 && + (include_hidden || entry->d_name[0] != '.')) { + result.push_back(entry_path.string()); + } + } + closedir(dp); + } + #endif + }; + + search_dir(base_dir); + + if (recursive) { + for (const auto& entry : std::filesystem::recursive_directory_iterator(base_dir)) { + if (entry.is_directory()) { + search_dir(entry.path()); + } + } + } + + List ret; + for (const auto& path : result) { + ret.push_back(VAR(path)); + } + return VAR(ret); + }); + + vm->bind_func(mod, "escape", 1, [](VM* vm, ArgsView args){ + std::string_view pattern = CAST(Str&, args[0]).sv(); + std::string escaped; + for (char c : pattern) { + if (c == '*' || c == '?' || c == '[' || c == ']') { + escaped += '['; + escaped += c; + escaped += ']'; + } else { + escaped += c; + } + } + return VAR(escaped); + }); + + vm->bind_func(mod, "has_magic", 1, [](VM* vm, ArgsView args){ + std::string_view pattern = CAST(Str&, args[0]).sv(); + return VAR(pattern.find_first_of("*?[") != std::string::npos); + }); +} #else void add_module_io(VM* vm){} diff --git a/src/pocketpy.cpp b/src/pocketpy.cpp index bb1da213..d4c55306 100644 --- a/src/pocketpy.cpp +++ b/src/pocketpy.cpp @@ -1638,6 +1638,8 @@ void VM::__post_init_builtin_types(){ if(enable_os){ add_module_io(this); add_module_os(this); + add_module_stat(this); + add_module_glob(this); _import_handler = _default_import_handler; }