From 916424da5aa5f670d284b2e140da9dddebc105fb Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Sat, 28 May 2022 19:14:44 +0530 Subject: [PATCH] OS module implemented --- cli/main.c | 2 +- src/core/debug.c | 2 +- src/include/pocketlang.h | 2 +- src/libs/libs.c | 4 + src/libs/libs.h | 9 +- src/libs/std_io.c | 15 ++- src/libs/std_os.c | 224 +++++++++++++++++++++++++++++++++++++++ src/libs/std_path.c | 33 +----- 8 files changed, 245 insertions(+), 46 deletions(-) create mode 100644 src/libs/std_os.c diff --git a/cli/main.c b/cli/main.c index 8ed9476..a4e89aa 100644 --- a/cli/main.c +++ b/cli/main.c @@ -56,7 +56,7 @@ static PKVM* intializePocketVM() { GetConsoleMode(handle, &outmode); SetConsoleMode(handle, outmode | ENABLE_VIRTUAL_TERMINAL_PROCESSING); #endif - config.use_ansi_color = true; + config.use_ansi_escape = true; } PKVM* vm = pkNewVM(&config); diff --git a/src/core/debug.c b/src/core/debug.c index 2e39e91..73443df 100644 --- a/src/core/debug.c +++ b/src/core/debug.c @@ -15,7 +15,7 @@ // Refactor this. Maybe move to a module, Rgb values are hardcoded ?! // Should check stderr/stdout etc. static void _printRed(PKVM* vm, const char* msg) { - if (vm->config.use_ansi_color) { + if (vm->config.use_ansi_escape) { vm->config.stderr_write(vm, "\033[38;2;220;100;100m"); vm->config.stderr_write(vm, msg); vm->config.stderr_write(vm, "\033[0m"); diff --git a/src/include/pocketlang.h b/src/include/pocketlang.h index 7571e36..754f2b5 100644 --- a/src/include/pocketlang.h +++ b/src/include/pocketlang.h @@ -185,7 +185,7 @@ struct PkConfiguration { pkLoadScriptFn load_script_fn; // If true stderr calls will use ansi color codes. - bool use_ansi_color; + bool use_ansi_escape; // User defined data associated with VM. void* user_data; diff --git a/src/libs/libs.c b/src/libs/libs.c index c73ba89..7908640 100644 --- a/src/libs/libs.c +++ b/src/libs/libs.c @@ -15,8 +15,11 @@ void registerModuleTypes(PKVM* vm); void registerModuleTime(PKVM* vm); void registerModuleIO(PKVM* vm); void registerModulePath(PKVM* vm); +void registerModuleOS(PKVM* vm); void registerModuleDummy(PKVM* vm); +// Extra libraries. + void registerModuleTerm(PKVM* vm); void cleanupModuleTerm(PKVM* vm); @@ -27,6 +30,7 @@ void registerLibs(PKVM* vm) { registerModuleTime(vm); registerModuleIO(vm); registerModulePath(vm); + registerModuleOS(vm); registerModuleDummy(vm); registerModuleTerm(vm); diff --git a/src/libs/libs.h b/src/libs/libs.h index 76a62f0..a00e7b1 100644 --- a/src/libs/libs.h +++ b/src/libs/libs.h @@ -15,10 +15,6 @@ #include #include -/*****************************************************************************/ -/* MODULES INTERNAL */ -/*****************************************************************************/ - // FIXME: // Since this are part of the "standard" pocketlang libraries, we can include // pocketlang internals here using the relative path, however it'll make these @@ -30,6 +26,11 @@ #include "../core/common.h" #endif // PK_AMALGAMATED +#include + +#define REPORT_ERRNO(fn) \ + pkSetRuntimeErrorFmt(vm, "C." #fn " errno:%i - %s.", errno, strerror(errno)) + /*****************************************************************************/ /* SHARED FUNCTIONS */ /*****************************************************************************/ diff --git a/src/libs/std_io.c b/src/libs/std_io.c index dbf89da..2176599 100644 --- a/src/libs/std_io.c +++ b/src/libs/std_io.c @@ -230,8 +230,7 @@ DEF(_fileRead, "") { size_t read = fread(buff, sizeof(char), count, file->fp); if (ferror(file->fp)) { - pkSetRuntimeErrorFmt(vm, "An error occured at C.fread() - '%s'.", - strerror(errno)); + REPORT_ERRNO(fread); goto L_done; } @@ -293,8 +292,7 @@ DEF(_fileGetLine, "") { // End of file or error. if (c == EOF) { if (ferror(file->fp)) { - pkSetRuntimeErrorFmt(vm, "Error while reading line - '%s'.", - strerror(errno)); + REPORT_ERRNO(fgetc); goto L_done; } break; // EOF is reached. @@ -334,8 +332,7 @@ DEF(_fileWrite, "") { clearerr(file->fp); fwrite(text, sizeof(char), (size_t) length, file->fp); if (ferror(file->fp)) { - pkSetRuntimeErrorFmt(vm, "An error occureed at C.fwrite() - '%s'.", - strerror(errno)); + REPORT_ERRNO(fwrite); return; } } @@ -351,7 +348,8 @@ DEF(_fileClose, "") { } if (fclose(file->fp) != 0) { - pkSetRuntimeError(vm, "fclose() failed!."); + REPORT_ERRNO(fclose); + return; } file->fp = NULL; @@ -384,8 +382,7 @@ DEF(_fileSeek, } if (fseek(file->fp, offset, whence) != 0) { - pkSetRuntimeErrorFmt(vm, "An error occureed at C.fseek() - '%s'.", - strerror(errno)); + REPORT_ERRNO(fseek); return; } } diff --git a/src/libs/std_os.c b/src/libs/std_os.c new file mode 100644 index 0000000..de08519 --- /dev/null +++ b/src/libs/std_os.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2020-2022 Thakee Nathees + * Copyright (c) 2021-2022 Pocketlang Contributors + * Distributed Under The MIT License + */ + +#include + +#ifndef PK_AMALGAMATED +#include "libs.h" +#endif + +#include +#include + +#if defined(__EMSCRIPTEN__) + #define _PKOS_WEB_ + #define OS_NAME "web" + +#elif defined(_WIN32) || defined(__NT__) + #define _PKOS_WIN_ + #define OS_NAME "windows" + +#elif defined(__APPLE__) + #define _PKOS_APPLE_ + #define OS_NAME "apple" // TODO: should I use darwin?. + +#elif defined(__linux__) + #define _PKOS_LINUX_ + #define OS_NAME "linux" + +#elif defined(__unix__) + #define _PKOS_UNIX_ + #define OS_NAME "unix" + +#elif defined(_POSIX_VERSION) + #define _PKOS_POSIX_ + #define OS_NAME "posix" + +#else + #define _PKOS_UNKNOWN_ + #define OS_NAME "" +#endif + +#if defined(_MSC_VER) || (defined(_WIN32) && defined(__TINYC__)) + #include + #include + #include + + #define getcwd _getcwd + #define chdir _chdir + #define mkdir _mkdir + #define rmdir _rmdir + #define unlink _unlink + +#else + #include + #include +#endif + +// The maximum path size that pocketlang's default import system supports +// including the null terminator. To be able to support more characters +// override the functions from the host application. Since this is very much +// platform specific we're defining a more general limit. +// See: https://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html +#define MAX_PATH_LEN 4096 + +// Yes both 'os' and 'path' have getcwd functions. +DEF(_osGetCWD, + "os.getcwd() -> String\n" + "Returns the current working directory") { + char cwd[MAX_PATH_LEN]; + if (getcwd(cwd, sizeof(cwd)) == NULL) { + // TODO: Handle error. + } + pkSetSlotString(vm, 0, cwd); +} + +DEF(_osChdir, + "os.chdir(path:String)\n" + "Change the current working directory") { + + const char* path; + if (!pkValidateSlotString(vm, 1, &path, NULL)) return; + + if (chdir(path)) REPORT_ERRNO(chdir); +} + +DEF(_osMkdir, + "os.mkdir(path:String)\n" + "Creates a directory at the path. The path should be valid.") { + + const char* path; + if (!pkValidateSlotString(vm, 1, &path, NULL)) return; + +#if defined(_PKOS_WIN_) + if (mkdir(path)) { +#else + if (mkdir(path, 0x1ff)) { // TODO: mode flags. +#endif + + // If the directory exists (errno == EEXIST) should I skip it silently ? + REPORT_ERRNO(mkdir); + } +} + +DEF(_osRmdir, + "os.rmdir(path:String)\n" + "Removes an empty directory at the path.") { + + const char* path; + if (!pkValidateSlotString(vm, 1, &path, NULL)) return; + if (rmdir(path)) REPORT_ERRNO(rmdir); +} + +DEF(_osUnlink, + "os.rmdir(path:String)\n" + "Removes a file at the path.") { + + const char* path; + if (!pkValidateSlotString(vm, 1, &path, NULL)) return; + if (unlink(path)) REPORT_ERRNO(unlink); +} + +DEF(_osModitime, + "os.moditime(path:String) -> Number\n" + "Returns the modified timestamp of the file.") { + const char* path; + if (!pkValidateSlotString(vm, 1, &path, NULL)) return; + + double mtime = 0; + struct stat path_stat; + if (stat(path, &path_stat) == 0) mtime = (double) path_stat.st_mtime; + pkSetSlotNumber(vm, 0, mtime); +} + +DEF(_osFileSize, + "os.filesize(path:String) -> Number\n" + "Returns the file size in bytes.") { + const char* path; + + if (!pkValidateSlotString(vm, 1, &path, NULL)) return; + + struct stat path_stat; + if (stat(path, &path_stat) || ((path_stat.st_mode & S_IFMT) != S_IFREG)) { + pkSetRuntimeErrorFmt(vm, "Path '%s' wasn't a file.", path); + return; + } + + pkSetSlotNumber(vm, 0, path_stat.st_size); + +} + +DEF(_osSystem, + "os.system(cmd:String) -> Number\n" + "Execute the command in a subprocess, Returns the exit code of the child " + "process.") { + const char* cmd; + + if (!pkValidateSlotString(vm, 1, &cmd, NULL)) return; + + errno = 0; + int code = system(cmd); + if (errno != 0) { + REPORT_ERRNO(system); + return; + } + + pkSetSlotNumber(vm, 0, (double) code); +} + +DEF(_osGetenv, + "os.getenv(name:String) -> String\n" + "Returns the environment variable as String if it exists otherwise it'll " + "return null.") { + + const char* name; + if (!pkValidateSlotString(vm, 1, &name, NULL)) return; + + const char* value = getenv(name); + if (value == NULL) { + pkSetSlotNull(vm, 0); + return; + } + + pkSetSlotString(vm, 0, value); +} + +/*****************************************************************************/ +/* MODULE REGISTER */ +/*****************************************************************************/ + +void registerModuleOS(PKVM* vm) { + + pkReserveSlots(vm, 2); + PkHandle* os = pkNewModule(vm, "os"); + + pkSetSlotHandle(vm, 0, os); // slots[0] = os + pkSetSlotString(vm, 1, OS_NAME); // slots[1] = "windows" + pkSetAttribute(vm, 0, "NAME", 1); // os.NAME = "windows" + + pkModuleAddFunction(vm, os, "getcwd", _osGetCWD, 0); + pkModuleAddFunction(vm, os, "chdir", _osChdir, 1); + pkModuleAddFunction(vm, os, "mkdir", _osMkdir, 1); + pkModuleAddFunction(vm, os, "rmdir", _osRmdir, 1); + pkModuleAddFunction(vm, os, "unlink", _osUnlink, 1); + pkModuleAddFunction(vm, os, "moditime", _osModitime, 1); + pkModuleAddFunction(vm, os, "filesize", _osFileSize, 1); + pkModuleAddFunction(vm, os, "system", _osSystem, 1); + pkModuleAddFunction(vm, os, "getenv", _osGetenv, 1); + + // TODO: + // - Implement makedirs which recursively mkdir(). + // - Implement copyfile() and copytree() + // - Implement rmtree() which recursively rmdir() and file. + // + //pkModuleAddSource(vm, os, "import path; def makedirs(p) ... end"); + + pkRegisterModule(vm, os); + pkReleaseHandle(vm, os); +} + +#undef OS_NAME +#undef MAX_PATH_LEN diff --git a/src/libs/std_path.c b/src/libs/std_path.c index ee9a38b..2b47aaa 100644 --- a/src/libs/std_path.c +++ b/src/libs/std_path.c @@ -17,7 +17,6 @@ #include "thirdparty/cwalk/cwalk.h" //<< AMALG_INLINE >> #undef _CWALK_IMPL -#include #include #ifdef _WIN32 @@ -376,33 +375,6 @@ DEF(_pathListDir, "") { } } -// Returns the modified time of the file. -DEF(_pathMtime, "") { - const char* path; - if (!pkValidateSlotString(vm, 1, &path, NULL)) return; - - double mtime = 0; - struct stat path_stat; - if (stat(path, &path_stat) == 0) mtime = (double) path_stat.st_mtime; - pkSetSlotNumber(vm, 0, mtime); -} - -// Returns the file size in bytes. -DEF(_pathSize, "") { - const char* path; - - if (!pkValidateSlotString(vm, 1, &path, NULL)) return; - - struct stat path_stat; - if (stat(path, &path_stat) || ((path_stat.st_mode & S_IFMT) != S_IFREG)) { - pkSetRuntimeErrorFmt(vm, "Path '%s' wasn't a file.", path); - return; - } - - pkSetSlotNumber(vm, 0, path_stat.st_size); - -} - /*****************************************************************************/ /* MODULE REGISTER */ /*****************************************************************************/ @@ -423,9 +395,10 @@ void registerModulePath(PKVM* vm) { pkModuleAddFunction(vm, path, "isfile", _pathIsFile, 1); pkModuleAddFunction(vm, path, "isdir", _pathIsDir, 1); pkModuleAddFunction(vm, path, "listdir", _pathListDir, -1); - pkModuleAddFunction(vm, path, "mtime", _pathMtime, 1); - pkModuleAddFunction(vm, path, "size", _pathSize, 1); pkRegisterModule(vm, path); pkReleaseHandle(vm, path); } + +#undef MAX_PATH_LEN +#undef MAX_JOIN_PATHS