From 53c7f2eedd00787e6d04a443be3ba78f775aeb95 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Thu, 28 Mar 2019 23:22:58 -0400 Subject: [PATCH] Add more os module functions. --- CHANGELOG.md | 1 + src/core/capi.c | 9 +++ src/core/inttypes.c | 1 + src/core/os.c | 171 ++++++++++++++++++++++++++++++++-------- src/include/janet.h | 1 + src/include/janetconf.h | 1 + 6 files changed, 150 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05fe629f..84122f2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file. - Add (break) special form and improve loop macro - Allow abstract types to specify custom tostring method - Extend C API for marshalling abstract types and other values +- Add functions to `os` module. ## 0.4.0 - 2019-03-08 - Fix a number of smaller bugs diff --git a/src/core/capi.c b/src/core/capi.c index 0c7d342a..845303f9 100644 --- a/src/core/capi.c +++ b/src/core/capi.c @@ -98,6 +98,15 @@ DEFINE_GETTER(cfunction, CFUNCTION, JanetCFunction) DEFINE_GETTER(boolean, BOOLEAN, int) DEFINE_GETTER(pointer, POINTER, void *) +const char *janet_getcstring(const Janet *argv, int32_t n) { + const uint8_t *jstr = janet_getstring(argv, n); + const char *cstr = (const char *)jstr; + if (strlen(cstr) != (size_t) janet_string_length(jstr)) { + janet_panicf("string %v contains embedded 0s"); + } + return cstr; +} + int32_t janet_getinteger(const Janet *argv, int32_t n) { Janet x = argv[n]; if (!janet_checkint(x)) { diff --git a/src/core/inttypes.c b/src/core/inttypes.c index abafc6ee..f21bb79e 100644 --- a/src/core/inttypes.c +++ b/src/core/inttypes.c @@ -24,6 +24,7 @@ #include #include #include +#include #ifndef JANET_AMALG #include diff --git a/src/core/os.c b/src/core/os.c index f7e50c01..87289b5e 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -26,16 +26,24 @@ #endif #include + +#ifndef JANET_REDUCED_OS + #include #ifdef JANET_WINDOWS #include #include #else +#include +#include #include +#include +#include #include #include #include +#include #endif /* For macos */ @@ -44,6 +52,12 @@ #include #endif +#endif + +/* Core OS functions */ + +/* Full OS functions */ + static Janet os_which(int32_t argc, Janet *argv) { janet_fixarity(argc, 0); (void) argv; @@ -58,6 +72,30 @@ static Janet os_which(int32_t argc, Janet *argv) { #endif } +static Janet os_exit(int32_t argc, Janet *argv) { + janet_arity(argc, 0, 1); + if (argc == 0) { + exit(EXIT_SUCCESS); + } else if (janet_checkint(argv[0])) { + exit(janet_unwrap_integer(argv[0])); + } else { + exit(EXIT_FAILURE); + } + return janet_wrap_nil(); +} + +#ifdef JANET_REDUCED_OS +/* Provide a dud os/getenv so init.janet works, but nothing else */ + +static Janet os_getenv(int32_t argc, Janet *argv) { + (void) argv; + janet_fixarity(argc, 1); + return janet_wrap_nil(); +} + +#else +/* Provide full os functionality */ + #ifdef JANET_WINDOWS static Janet os_execute(int32_t argc, Janet *argv) { janet_arity(argc, 1, -1); @@ -124,13 +162,13 @@ static Janet os_execute(int32_t argc, Janet *argv) { #else static Janet os_execute(int32_t argc, Janet *argv) { janet_arity(argc, 1, -1); - const uint8_t **child_argv = malloc(sizeof(uint8_t *) * (argc + 1)); + const char **child_argv = malloc(sizeof(char *) * (argc + 1)); int status = 0; if (NULL == child_argv) { JANET_OUT_OF_MEMORY; } for (int32_t i = 0; i < argc; i++) { - child_argv[i] = janet_getstring(argv, i); + child_argv[i] = janet_getcstring(argv, i); } child_argv[argc] = NULL; @@ -139,7 +177,7 @@ static Janet os_execute(int32_t argc, Janet *argv) { if (pid < 0) { janet_panic("failed to execute"); } else if (pid == 0) { - if (-1 == execve((const char *)child_argv[0], (char **)child_argv, NULL)) { + if (-1 == execve(child_argv[0], (char **)child_argv, NULL)) { exit(1); } } else { @@ -153,7 +191,7 @@ static Janet os_execute(int32_t argc, Janet *argv) { static Janet os_shell(int32_t argc, Janet *argv) { janet_arity(argc, 0, 1); const char *cmd = argc - ? (const char *)janet_getstring(argv, 0) + ? janet_getcstring(argv, 0) : NULL; int stat = system(cmd); return argc @@ -163,10 +201,9 @@ static Janet os_shell(int32_t argc, Janet *argv) { static Janet os_getenv(int32_t argc, Janet *argv) { janet_fixarity(argc, 1); - const uint8_t *k = janet_getstring(argv, 0); - const char *cstr = (const char *) k; + const char *cstr = janet_getcstring(argv, 0); const char *res = getenv(cstr); - return (res && cstr) + return res ? janet_cstringv(res) : janet_wrap_nil(); } @@ -180,25 +217,11 @@ static Janet os_setenv(int32_t argc, Janet *argv) { #define UNSETENV(K) unsetenv(K) #endif janet_arity(argc, 1, 2); - const uint8_t *k = janet_getstring(argv, 0); - const char *ks = (const char *) k; + const char *ks = janet_getcstring(argv, 0); if (argc == 1 || janet_checktype(argv[1], JANET_NIL)) { UNSETENV(ks); } else { - const uint8_t *v = janet_getstring(argv, 1); - SETENV(ks, (const char *)v); - } - return janet_wrap_nil(); -} - -static Janet os_exit(int32_t argc, Janet *argv) { - janet_arity(argc, 0, 1); - if (argc == 0) { - exit(EXIT_SUCCESS); - } else if (janet_checkint(argv[0])) { - exit(janet_unwrap_integer(argv[0])); - } else { - exit(EXIT_FAILURE); + SETENV(ks, janet_getcstring(argv, 1)); } return janet_wrap_nil(); } @@ -301,7 +324,68 @@ static Janet os_date(int32_t argc, Janet *argv) { return janet_wrap_struct(janet_struct_end(st)); } +static Janet os_link(int32_t argc, Janet *argv) { + janet_arity(argc, 2, 3); +#ifdef JANET_WINDOWS + (void) argc; + (void) argv; + janet_panic("os/link not supported on Windows"); + return janet_wrap_nil(); +#else + const char *oldpath = janet_getcstring(argv, 0); + const char *newpath = janet_getcstring(argv, 1); + int res = ((argc == 3 && janet_getboolean(argv, 2)) ? symlink : link)(oldpath, newpath); + if (res == -1) janet_panicv(janet_cstringv(strerror(errno))); + return janet_wrap_integer(res); +#endif +} + +static Janet os_mkdir(int32_t argc, Janet *argv) { + janet_fixarity(argc, 1); + const char *path = janet_getcstring(argv, 0); +#ifdef JANET_WINDOWS + int res = _mkdir(path); +#else + int res = mkdir(path, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH); +#endif + return janet_wrap_boolean(res != -1); +} + +static Janet os_cd(int32_t argc, Janet *argv) { + janet_fixarity(argc, 1); + const char *path = janet_getcstring(argv, 0); + int res = chdir(path); + return janet_wrap_boolean(res != -1); +} + +static Janet os_touch(int32_t argc, Janet *argv) { + janet_arity(argc, 1, 3); + const char *path = janet_getcstring(argv, 0); + struct utimbuf timebuf, *bufp; + if (argc >= 2) { + bufp = &timebuf; + timebuf.actime = (time_t) janet_getnumber(argv, 1); + if (argc >= 3) { + timebuf.modtime = (time_t) janet_getnumber(argv, 2); + } else { + timebuf.modtime = timebuf.actime; + } + } else { + bufp = NULL; + } + int res = utime(path, bufp); + return janet_wrap_boolean(res != -1); +} + +#endif /* JANET_REDUCED_OS */ + static const JanetReg os_cfuns[] = { + { + "os/exit", os_exit, + JDOC("(os/exit x)\n\n" + "Exit from janet with an exit code equal to x. If x is not an integer, " + "the exit with status equal the hash of x.") + }, { "os/which", os_which, JDOC("(os/which)\n\n" @@ -310,6 +394,35 @@ static const JanetReg os_cfuns[] = { "\t:macos - Apple macos\n" "\t:posix - A POSIX compatible system (default)") }, + { + "os/getenv", os_getenv, + JDOC("(os/getenv variable)\n\n" + "Get the string value of an environment variable.") + }, +#ifndef JANET_REDUCED_OS + { + "os/touch", os_touch, + JDOC("(os/touch path [, actime [, modtime]])\n\n" + "Update the access time and modification times for a file. By default, sets " + "times to the current time.") + }, + { + "os/cd", os_cd, + JDOC("(os/cd path)\n\n" + "Change current directory to path. Returns true on success, false on failure.") + }, + { + "os/mkdir", os_mkdir, + JDOC("(os/mkdir path)\n\n" + "Create a new directory. The path will be relative to the current directory if relative, otherwise " + "it will be an absolute path.") + }, + { + "os/link", os_link, + JDOC("(os/link oldpath newpath [, symlink])\n\n" + "Create a symlink from oldpath to newpath. The 3 optional paramater " + "enables a hard link over a soft link. Does not work on Windows.") + }, { "os/execute", os_execute, JDOC("(os/execute program & args)\n\n" @@ -321,17 +434,6 @@ static const JanetReg os_cfuns[] = { JDOC("(os/shell str)\n\n" "Pass a command string str directly to the system shell.") }, - { - "os/exit", os_exit, - JDOC("(os/exit x)\n\n" - "Exit from janet with an exit code equal to x. If x is not an integer, " - "the exit with status equal the hash of x.") - }, - { - "os/getenv", os_getenv, - JDOC("(os/getenv variable)\n\n" - "Get the string value of an environment variable.") - }, { "os/setenv", os_setenv, JDOC("(os/setenv variable value)\n\n" @@ -375,6 +477,7 @@ static const JanetReg os_cfuns[] = { "\t:year-day - day of the year [0-365]\n" "\t:dst - If Day Light Savings is in effect") }, +#endif {NULL, NULL, NULL} }; diff --git a/src/include/janet.h b/src/include/janet.h index 48cd96ea..88cc309c 100644 --- a/src/include/janet.h +++ b/src/include/janet.h @@ -1256,6 +1256,7 @@ JANET_API const Janet *janet_gettuple(const Janet *argv, int32_t n); JANET_API JanetTable *janet_gettable(const Janet *argv, int32_t n); JANET_API const JanetKV *janet_getstruct(const Janet *argv, int32_t n); JANET_API const uint8_t *janet_getstring(const Janet *argv, int32_t n); +JANET_API const char *janet_getcstring(const Janet *argv, int32_t n); JANET_API const uint8_t *janet_getsymbol(const Janet *argv, int32_t n); JANET_API const uint8_t *janet_getkeyword(const Janet *argv, int32_t n); JANET_API JanetBuffer *janet_getbuffer(const Janet *argv, int32_t n); diff --git a/src/include/janetconf.h b/src/include/janetconf.h index 267b8036..e3c3b81e 100644 --- a/src/include/janetconf.h +++ b/src/include/janetconf.h @@ -34,6 +34,7 @@ /* #define JANET_NO_PEG */ /* #define JANET_NO_TYPED_ARRAY */ /* #define JANET_NO_INT_TYPES */ +/* #define JANET_REDUCED_OS */ /* #define JANET_API __attribute__((visibility ("default"))) */ /* #define JANET_OUT_OF_MEMORY do { printf("janet out of memory\n"); exit(1); } while (0) */ /* #define JANET_RECURSION_GUARD 1024 */