From 39032b45c9ac9bd46de9d2a9bc5788900d49d687 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Fri, 4 Sep 2020 14:55:47 -0500 Subject: [PATCH] Remove extra file. --- src/core/os.c.orig | 1682 -------------------------------------------- 1 file changed, 1682 deletions(-) delete mode 100644 src/core/os.c.orig diff --git a/src/core/os.c.orig b/src/core/os.c.orig deleted file mode 100644 index c8a68091..00000000 --- a/src/core/os.c.orig +++ /dev/null @@ -1,1682 +0,0 @@ -/* -* Copyright (c) 2020 Calvin Rose -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to -* deal in the Software without restriction, including without limitation the -* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -* sell copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -* IN THE SOFTWARE. -*/ - -#ifndef JANET_AMALG -#include "features.h" -#include -#include "util.h" -#include "gc.h" -#endif - -#ifndef JANET_REDUCED_OS - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef JANET_APPLE -#include -#endif - -#ifdef JANET_WINDOWS -#include -#include -#include -#include -#include -#else -#include -#include -#include -#include -#include -#include -extern char **environ; -#ifdef JANET_THREADS -#include -#endif -#endif - -/* For macos */ -#ifdef __MACH__ -#include -#include -#endif - -/* Not POSIX, but all Unixes but Solaris have this function. */ -#if defined(JANET_POSIX) && !defined(__sun) -time_t timegm(struct tm *tm); -#elif defined(JANET_WINDOWS) -#define timegm _mkgmtime -#endif - -/* Access to some global variables should be synchronized if not in single threaded mode, as - * setenv/getenv are not thread safe. */ -#ifdef JANET_THREADS -# ifdef JANET_WINDOWS -static int env_lock_initialized = 0; -static CRITICAL_SECTION env_lock; -static void janet_lock_environ(void) { - EnterCriticalSection(&env_lock); -} -static void janet_unlock_environ(void) { - LeaveCriticalSection(&env_lock); -} -# else -static pthread_mutex_t env_lock = PTHREAD_MUTEX_INITIALIZER; -static void janet_lock_environ(void) { - pthread_mutex_lock(&env_lock); -} -static void janet_unlock_environ(void) { - pthread_mutex_unlock(&env_lock); -} -# endif -#else -static void janet_lock_environ(void) { -} -static void janet_unlock_environ(void) { -} -#endif - -#endif /* JANET_REDCUED_OS */ - -/* Core OS functions */ - -/* Full OS functions */ - -#define janet_stringify1(x) #x -#define janet_stringify(x) janet_stringify1(x) - -static Janet os_which(int32_t argc, Janet *argv) { - janet_fixarity(argc, 0); - (void) argv; -#if defined(JANET_OS_NAME) - return janet_ckeywordv(janet_stringify(JANET_OS_NAME)); -#elif defined(JANET_WINDOWS) - return janet_ckeywordv("windows"); -#elif defined(JANET_APPLE) - return janet_ckeywordv("macos"); -#elif defined(__EMSCRIPTEN__) - return janet_ckeywordv("web"); -#elif defined(JANET_LINUX) - return janet_ckeywordv("linux"); -#elif defined(__FreeBSD__) - return janet_ckeywordv("freebsd"); -#elif defined(__NetBSD__) - return janet_ckeywordv("netbsd"); -#elif defined(__OpenBSD__) - return janet_ckeywordv("openbsd"); -#elif defined(JANET_BSD) - return janet_ckeywordv("bsd"); -#else - return janet_ckeywordv("posix"); -#endif -} - -/* Detect the ISA we are compiled for */ -static Janet os_arch(int32_t argc, Janet *argv) { - janet_fixarity(argc, 0); - (void) argv; - /* Check 64-bit vs 32-bit */ -#if defined(JANET_ARCH_NAME) - return janet_ckeywordv(janet_stringify(JANET_ARCH_NAME)); -#elif defined(__EMSCRIPTEN__) - return janet_ckeywordv("wasm"); -#elif (defined(__x86_64__) || defined(_M_X64)) - return janet_ckeywordv("x64"); -#elif defined(__i386) || defined(_M_IX86) - return janet_ckeywordv("x86"); -#elif defined(_M_ARM64) || defined(__aarch64__) - return janet_ckeywordv("aarch64"); -#elif defined(_M_ARM) || defined(__arm__) - return janet_ckeywordv("arm"); -#elif (defined(__sparc__)) - return janet_ckeywordv("sparc"); -#elif (defined(__ppc__)) - return janet_ckeywordv("ppc"); -#else - return janet_ckeywordv("unknown"); -#endif -} - -#undef janet_stringify1 -#undef janet_stringify - -static Janet os_exit(int32_t argc, Janet *argv) { - janet_arity(argc, 0, 1); - int status; - if (argc == 0) { - status = EXIT_SUCCESS; - } else if (janet_checkint(argv[0])) { - status = janet_unwrap_integer(argv[0]); - } else { - status = EXIT_FAILURE; - } - janet_deinit(); - exit(status); - return janet_wrap_nil(); -} - -#ifndef JANET_REDUCED_OS - -#ifndef JANET_NO_PROCESSES -/* Get env for os_execute */ -static char **os_execute_env(int32_t argc, const Janet *argv) { - char **envp = NULL; - if (argc > 2) { - JanetDictView dict = janet_getdictionary(argv, 2); - envp = janet_smalloc(sizeof(char *) * ((size_t)dict.len + 1)); - int32_t j = 0; - for (int32_t i = 0; i < dict.cap; i++) { - const JanetKV *kv = dict.kvs + i; - if (!janet_checktype(kv->key, JANET_STRING)) continue; - if (!janet_checktype(kv->value, JANET_STRING)) continue; - const uint8_t *keys = janet_unwrap_string(kv->key); - const uint8_t *vals = janet_unwrap_string(kv->value); - int32_t klen = janet_string_length(keys); - int32_t vlen = janet_string_length(vals); - /* Check keys has no embedded 0s or =s. */ - int skip = 0; - for (int32_t k = 0; k < klen; k++) { - if (keys[k] == '\0' || keys[k] == '=') { - skip = 1; - break; - } - } - if (skip) continue; - char *envitem = janet_smalloc((size_t) klen + (size_t) vlen + 2); - memcpy(envitem, keys, klen); - envitem[klen] = '='; - memcpy(envitem + klen + 1, vals, vlen); - envitem[klen + vlen + 1] = 0; - envp[j++] = envitem; - } - envp[j] = NULL; - } - return envp; -} - -/* Free memory from os_execute */ -static void os_execute_cleanup(char **envp, const char **child_argv) { -#ifdef JANET_WINDOWS - (void) child_argv; -#else - janet_sfree((void *)child_argv); -#endif - if (NULL != envp) { - char **envitem = envp; - while (*envitem != NULL) { - janet_sfree(*envitem); - envitem++; - } - } - janet_sfree(envp); -} - -#ifdef JANET_WINDOWS -/* Windows processes created via CreateProcess get only one command line argument string, and - * must parse this themselves. Each processes is free to do this however they like, but the - * standard parsing method is CommandLineToArgvW. We need to properly escape arguments into - * a single string of this format. Returns a buffer that can be cast into a c string. */ -static JanetBuffer *os_exec_escape(JanetView args) { - JanetBuffer *b = janet_buffer(0); - for (int32_t i = 0; i < args.len; i++) { - const char *arg = janet_getcstring(args.items, i); - - /* Push leading space if not first */ - if (i) janet_buffer_push_u8(b, ' '); - - /* Find first special character */ - const char *first_spec = arg; - while (*first_spec) { - switch (*first_spec) { - case ' ': - case '\t': - case '\v': - case '\n': - case '"': - goto found; - case '\0': - janet_panic("embedded 0 not allowed in command line string"); - default: - first_spec++; - break; - } - } - found: - - /* Check if needs escape */ - if (*first_spec == '\0') { - /* No escape needed */ - janet_buffer_push_cstring(b, arg); - } else { - /* Escape */ - janet_buffer_push_u8(b, '"'); - for (const char *c = arg; ; c++) { - unsigned numBackSlashes = 0; - while (*c == '\\') { - c++; - numBackSlashes++; - } - if (*c == '"') { - /* Escape all backslashes and double quote mark */ - int32_t n = 2 * numBackSlashes + 1; - janet_buffer_extra(b, n + 1); - memset(b->data + b->count, '\\', n); - b->count += n; - janet_buffer_push_u8(b, '"'); - } else if (*c) { - /* Don't escape backslashes. */ - int32_t n = numBackSlashes; - janet_buffer_extra(b, n + 1); - memset(b->data + b->count, '\\', n); - b->count += n; - janet_buffer_push_u8(b, *c); - } else { - /* we finished Escape all backslashes */ - int32_t n = 2 * numBackSlashes; - janet_buffer_extra(b, n + 1); - memset(b->data + b->count, '\\', n); - b->count += n; - break; - } - } - janet_buffer_push_u8(b, '"'); - } - } - janet_buffer_push_u8(b, 0); - return b; -} -#endif - -/* Process type for when running a subprocess and not immediately waiting */ -static const JanetAbstractType ProcAT; -#define JANET_PROC_CLOSED 1 -#define JANET_PROC_WAITED 2 -typedef struct { - int flags; -#ifdef JANET_WINDOWS - HANDLE pHandle; - HANDLE tHandle; -#else - int pid; -#endif - int return_code; - JanetFile *in; - JanetFile *out; - JanetFile *err; -} JanetProc; - -static int janet_proc_gc(void *p, size_t s) { - (void) s; - JanetProc *proc = (JanetProc *) p; -#ifdef JANET_WINDOWS - if (!(proc->flags & JANET_PROC_CLOSED)) { - CloseHandle(proc->pHandle); - CloseHandle(proc->tHandle); - } -#else - if (!(proc->flags & JANET_PROC_WAITED)) { - /* Kill and wait to prevent zombies */ - kill(proc->pid, SIGKILL); - int status; - waitpid(proc->pid, &status, 0); - } -#endif - return 0; -} - -static int janet_proc_mark(void *p, size_t s) { - (void) s; - JanetProc *proc = (JanetProc *)p; - if (NULL != proc->in) janet_mark(janet_wrap_abstract(proc->in)); - if (NULL != proc->out) janet_mark(janet_wrap_abstract(proc->out)); - if (NULL != proc->err) janet_mark(janet_wrap_abstract(proc->err)); - return 0; -} - -static Janet os_proc_wait_impl(JanetProc *proc) { - if (proc->flags & JANET_PROC_WAITED) { - janet_panicf("cannot wait on process that has already finished"); - } - proc->flags |= JANET_PROC_WAITED; - int status = 0; -#ifdef JANET_WINDOWS - WaitForSingleObject(proc->pHandle, INFINITE); - GetExitCodeProcess(proc->pHandle, &status); - if (!(proc->flags & JANET_PROC_CLOSED)) { - proc->flags |= JANET_PROC_CLOSED; - CloseHandle(proc->pHandle); - CloseHandle(proc->tHandle); - } -#else - waitpid(proc->pid, &status, 0); -#endif - proc->return_code = (int32_t) status; - return janet_wrap_integer(proc->return_code); -} - -static Janet os_proc_wait(int32_t argc, Janet *argv) { - janet_fixarity(argc, 1); - JanetProc *proc = janet_getabstract(argv, 0, &ProcAT); - return os_proc_wait_impl(proc); -} - -static Janet os_proc_kill(int32_t argc, Janet *argv) { - janet_arity(argc, 1, 2); - JanetProc *proc = janet_getabstract(argv, 0, &ProcAT); - if (proc->flags & JANET_PROC_WAITED) { - janet_panicf("cannot kill process that has already finished"); - } -#ifdef JANET_WINDOWS - if (proc->flags & JANET_PROC_CLOSED) { - janet_panicf("cannot close process handle that is already closed"); - } - proc->flags |= JANET_PROC_CLOSED; - CloseHandle(proc->pHandle); - CloseHandle(proc->tHandle); -#else - int status = kill(proc->pid, SIGKILL); - if (status) { - janet_panic(strerror(errno)); - } -#endif - /* After killing process we wait on it. */ - if (argc > 1 && janet_truthy(argv[1])) { - return os_proc_wait_impl(proc); - } else { - return argv[0]; - } -} - -static const JanetMethod proc_methods[] = { - {"wait", os_proc_wait}, - {"kill", os_proc_kill}, - {NULL, NULL} -}; - -static int janet_proc_get(void *p, Janet key, Janet *out) { - JanetProc *proc = (JanetProc *)p; - if (janet_keyeq(key, "in")) { - *out = (NULL == proc->in) ? janet_wrap_nil() : janet_wrap_abstract(proc->in); - return 1; - } - if (janet_keyeq(key, "out")) { - *out = (NULL == proc->out) ? janet_wrap_nil() : janet_wrap_abstract(proc->out); - return 1; - } - if (janet_keyeq(key, "err")) { - *out = (NULL == proc->out) ? janet_wrap_nil() : janet_wrap_abstract(proc->err); - return 1; - } - if ((-1 != proc->return_code) && janet_keyeq(key, "return-code")) { - *out = janet_wrap_integer(proc->return_code); - return 1; - } - if (!janet_checktype(key, JANET_KEYWORD)) return 0; - return janet_getmethod(janet_unwrap_keyword(key), proc_methods, out); -} - -static const JanetAbstractType ProcAT = { - "core/process", - janet_proc_gc, - janet_proc_mark, - janet_proc_get, - JANET_ATEND_GET -}; - -static Janet os_execute_impl(int32_t argc, Janet *argv, int is_async) { - janet_arity(argc, 1, 3); - - /* Get flags */ - uint64_t flags = 0; - if (argc > 1) { - flags = janet_getflags(argv, 1, "epx"); - } - - /* Get environment */ - char **envp = os_execute_env(argc, argv); - - /* Get arguments */ - JanetView exargs = janet_getindexed(argv, 0); - if (exargs.len < 1) { - janet_panic("expected at least 1 command line argument"); - } - - /* Optional stdio redirections */ - JanetFile *new_in = NULL, *new_out = NULL, *new_err = NULL; - - /* Get optional redirections */ - if (argc > 2) { - JanetDictView tab = janet_getdictionary(argv, 2); - Janet maybe_stdin = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("in")); - Janet maybe_stdout = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("out")); - Janet maybe_stderr = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("err")); - if (!janet_checktype(maybe_stdin, JANET_NIL)) new_in = janet_getjfile(&maybe_stdin, 0); - if (!janet_checktype(maybe_stdout, JANET_NIL)) new_out = janet_getjfile(&maybe_stdout, 0); - if (!janet_checktype(maybe_stderr, JANET_NIL)) new_err = janet_getjfile(&maybe_stderr, 0); - } - - /* Result */ - int status = 0; - -#ifdef JANET_WINDOWS - - HANDLE pHandle, tHandle; - PROCESS_INFORMATION processInfo; - STARTUPINFO startupInfo; - memset(&processInfo, 0, sizeof(processInfo)); - memset(&startupInfo, 0, sizeof(startupInfo)); - startupInfo.cb = sizeof(startupInfo); - startupInfo.dwFlags |= STARTF_USESTDHANDLES; - - JanetBuffer *buf = os_exec_escape(exargs); - if (buf->count > 8191) { - janet_panic("command line string too long (max 8191 characters)"); - } - const char *path = (const char *) janet_unwrap_string(exargs.items[0]); - - /* Do IO redirection */ - startupInfo.hStdInput = (HANDLE) _get_osfhandle((new_in == NULL) ? 0 : _fileno(new_in->file)); - startupInfo.hStdOutput = (HANDLE) _get_osfhandle((new_out == NULL) ? 1 : _fileno(new_out->file)); - startupInfo.hStdError = (HANDLE) _get_osfhandle((new_err == NULL) ? 2 : _fileno(new_err->file)); - - /* Use _spawn family of functions. */ - /* Windows docs say do this before any spawns. */ - _flushall(); - - /* TODO - redirection, :p flag */ - if (!CreateProcess(janet_flag_at(flags, 1) ? NULL : path, /* NULL? */ - (char *) buf->data, /* Single CLI argument */ - NULL, /* no proc inheritance */ - NULL, /* no thread inheritance */ - TRUE, /* handle inheritance */ - 0, /* flags */ - envp, /* pass in environment */ - NULL, /* use parents starting directory */ - &startupInfo, - &processInfo)) { - janet_panic("failed to create process"); - } - - pHandle = processInfo.hProcess; - tHandle = processInfo.hThread; - - os_execute_cleanup(envp, NULL); - - /* Wait and cleanup immedaitely */ - if (!is_async) { - DWORD code; - WaitForSingleObject(pHandle, INFINITE); - GetExitCodeProcess(pHandle, &code); - status = (int) code; - CloseHandle(pHandle); - CloseHandle(tHandle); - } -#else - - const char **child_argv = janet_smalloc(sizeof(char *) * ((size_t) exargs.len + 1)); - for (int32_t i = 0; i < exargs.len; i++) - child_argv[i] = janet_getcstring(exargs.items, i); - child_argv[exargs.len] = NULL; - /* Coerce to form that works for spawn. I'm fairly confident no implementation - * of posix_spawn would modify the argv array passed in. */ - char *const *cargv = (char *const *)child_argv; - - /* Use posix_spawn to spawn new process */ - - int use_environ = !janet_flag_at(flags, 0); - - if (use_environ) { - janet_lock_environ(); - } - - /* Posix spawn setup */ - posix_spawn_file_actions_t actions; - posix_spawn_file_actions_init(&actions); - if (new_in != NULL) { - posix_spawn_file_actions_adddup2(&actions, fileno(new_in->file), 0); - } - if (new_out != NULL) { - posix_spawn_file_actions_adddup2(&actions, fileno(new_out->file), 1); - } - if (new_err != NULL) { - posix_spawn_file_actions_adddup2(&actions, fileno(new_err->file), 2); - } - - pid_t pid; - if (janet_flag_at(flags, 1)) { - status = posix_spawnp(&pid, - child_argv[0], &actions, NULL, cargv, - use_environ ? environ : envp); - } else { - status = posix_spawn(&pid, - child_argv[0], &actions, NULL, cargv, - use_environ ? environ : envp); - } - - posix_spawn_file_actions_destroy(&actions); - - if (use_environ) { - janet_unlock_environ(); - } - - /* Wait for child */ - if (status) { - os_execute_cleanup(envp, child_argv); - janet_panicf("%p: %s", argv[0], strerror(errno)); - } else if (is_async) { - /* Get process handle */ - os_execute_cleanup(envp, child_argv); - } else { - /* Wait to complete */ - waitpid(pid, &status, 0); - os_execute_cleanup(envp, child_argv); - /* Use POSIX shell semantics for interpreting signals */ - if (WIFEXITED(status)) { - status = WEXITSTATUS(status); - } else if (WIFSTOPPED(status)) { - status = WSTOPSIG(status) + 128; - } else { - status = WTERMSIG(status) + 128; - } - } - -#endif - if (is_async) { - JanetProc *proc = janet_abstract(&ProcAT, sizeof(JanetProc)); - proc->return_code = -1; -#ifdef JANET_WINDOWS - proc->pHandle = pHandle; - proc->tHandle = tHandle; -#else - proc->pid = pid; -#endif - proc->in = new_in; - proc->out = new_out; - proc->err = new_err; - proc->flags = 0; - return janet_wrap_abstract(proc); - } else if (janet_flag_at(flags, 2) && status) { - janet_panicf("command failed with non-zero exit code %d", status); - } else { - return janet_wrap_integer(status); - } -} - -static Janet os_execute(int32_t argc, Janet *argv) { - return os_execute_impl(argc, argv, 0); -} - -static Janet os_spawn(int32_t argc, Janet *argv) { - return os_execute_impl(argc, argv, 1); -} - -static Janet os_shell(int32_t argc, Janet *argv) { - janet_arity(argc, 0, 1); - const char *cmd = argc - ? janet_getcstring(argv, 0) - : NULL; - int stat = system(cmd); - return argc - ? janet_wrap_integer(stat) - : janet_wrap_boolean(stat); -} - -#endif /* JANET_NO_PROCESSES */ - -static Janet os_environ(int32_t argc, Janet *argv) { - (void) argv; - janet_fixarity(argc, 0); - int32_t nenv = 0; - janet_lock_environ(); - char **env = environ; - while (*env++) - nenv += 1; - JanetTable *t = janet_table(nenv); - for (int32_t i = 0; i < nenv; i++) { - char *e = environ[i]; - char *eq = strchr(e, '='); - if (!eq) { - janet_unlock_environ(); - janet_panic("no '=' in environ"); - } - char *v = eq + 1; - int32_t full_len = (int32_t) strlen(e); - int32_t val_len = (int32_t) strlen(v); - janet_table_put( - t, - janet_stringv((const uint8_t *)e, full_len - val_len - 1), - janet_stringv((const uint8_t *)v, val_len) - ); - } - janet_unlock_environ(); - return janet_wrap_table(t); -} - -static Janet os_getenv(int32_t argc, Janet *argv) { - janet_arity(argc, 1, 2); - const char *cstr = janet_getcstring(argv, 0); - const char *res = getenv(cstr); - janet_lock_environ(); - Janet ret = res - ? janet_cstringv(res) - : argc == 2 - ? argv[1] - : janet_wrap_nil(); - janet_unlock_environ(); - return ret; -} - -static Janet os_setenv(int32_t argc, Janet *argv) { -#ifdef JANET_WINDOWS -#define SETENV(K,V) _putenv_s(K, V) -#define UNSETENV(K) _putenv_s(K, "") -#else -#define SETENV(K,V) setenv(K, V, 1) -#define UNSETENV(K) unsetenv(K) -#endif - janet_arity(argc, 1, 2); - const char *ks = janet_getcstring(argv, 0); - const char *vs = janet_optcstring(argv, argc, 1, NULL); - janet_lock_environ(); - if (NULL == vs) { - UNSETENV(ks); - } else { - SETENV(ks, vs); - } - janet_unlock_environ(); - return janet_wrap_nil(); -} - -static Janet os_time(int32_t argc, Janet *argv) { - janet_fixarity(argc, 0); - (void) argv; - double dtime = (double)(time(NULL)); - return janet_wrap_number(dtime); -} - -static Janet os_clock(int32_t argc, Janet *argv) { - janet_fixarity(argc, 0); - (void) argv; - struct timespec tv; - if (janet_gettime(&tv)) janet_panic("could not get time"); - double dtime = tv.tv_sec + (tv.tv_nsec / 1E9); - return janet_wrap_number(dtime); -} - -static Janet os_sleep(int32_t argc, Janet *argv) { - janet_fixarity(argc, 1); - double delay = janet_getnumber(argv, 0); - if (delay < 0) janet_panic("invalid argument to sleep"); -#ifdef JANET_WINDOWS - Sleep((DWORD)(delay * 1000)); -#else - int rc; - struct timespec ts; - ts.tv_sec = (time_t) delay; - ts.tv_nsec = (delay <= UINT32_MAX) - ? (long)((delay - ((uint32_t)delay)) * 1000000000) - : 0; - RETRY_EINTR(rc, nanosleep(&ts, &ts)); -#endif - return janet_wrap_nil(); -} - -static Janet os_cwd(int32_t argc, Janet *argv) { - janet_fixarity(argc, 0); - (void) argv; - char buf[FILENAME_MAX]; - char *ptr; -#ifdef JANET_WINDOWS - ptr = _getcwd(buf, FILENAME_MAX); -#else - ptr = getcwd(buf, FILENAME_MAX); -#endif - if (NULL == ptr) janet_panic("could not get current directory"); - return janet_cstringv(ptr); -} - -static Janet os_cryptorand(int32_t argc, Janet *argv) { - JanetBuffer *buffer; - janet_arity(argc, 1, 2); - int32_t offset; - int32_t n = janet_getinteger(argv, 0); - if (n < 0) janet_panic("expected positive integer"); - if (argc == 2) { - buffer = janet_getbuffer(argv, 1); - offset = buffer->count; - } else { - offset = 0; - buffer = janet_buffer(n); - } - /* We could optimize here by adding setcount_uninit */ - janet_buffer_setcount(buffer, offset + n); - - if (janet_cryptorand(buffer->data + offset, n) != 0) - janet_panic("unable to get sufficient random data"); - - return janet_wrap_buffer(buffer); -} - -static Janet os_date(int32_t argc, Janet *argv) { - janet_arity(argc, 0, 2); - (void) argv; - time_t t; - struct tm t_infos; - struct tm *t_info = NULL; - if (argc) { - int64_t integer = janet_getinteger64(argv, 0); - t = (time_t) integer; - } else { - time(&t); - } - if (argc >= 2 && janet_truthy(argv[1])) { - /* local time */ -#ifdef JANET_WINDOWS - localtime_s(&t_infos, &t); - t_info = &t_infos; -#else - tzset(); - t_info = localtime_r(&t, &t_infos); -#endif - } else { - /* utc time */ -#ifdef JANET_WINDOWS - gmtime_s(&t_infos, &t); - t_info = &t_infos; -#else - t_info = gmtime_r(&t, &t_infos); -#endif - } - JanetKV *st = janet_struct_begin(9); - janet_struct_put(st, janet_ckeywordv("seconds"), janet_wrap_number(t_info->tm_sec)); - janet_struct_put(st, janet_ckeywordv("minutes"), janet_wrap_number(t_info->tm_min)); - janet_struct_put(st, janet_ckeywordv("hours"), janet_wrap_number(t_info->tm_hour)); - janet_struct_put(st, janet_ckeywordv("month-day"), janet_wrap_number(t_info->tm_mday - 1)); - janet_struct_put(st, janet_ckeywordv("month"), janet_wrap_number(t_info->tm_mon)); - janet_struct_put(st, janet_ckeywordv("year"), janet_wrap_number(t_info->tm_year + 1900)); - janet_struct_put(st, janet_ckeywordv("week-day"), janet_wrap_number(t_info->tm_wday)); - janet_struct_put(st, janet_ckeywordv("year-day"), janet_wrap_number(t_info->tm_yday)); - janet_struct_put(st, janet_ckeywordv("dst"), janet_wrap_boolean(t_info->tm_isdst)); - return janet_wrap_struct(janet_struct_end(st)); -} - -static int entry_getdst(Janet env_entry) { - Janet v; - if (janet_checktype(env_entry, JANET_TABLE)) { - JanetTable *entry = janet_unwrap_table(env_entry); - v = janet_table_get(entry, janet_ckeywordv("dst")); - } else if (janet_checktype(env_entry, JANET_STRUCT)) { - const JanetKV *entry = janet_unwrap_struct(env_entry); - v = janet_struct_get(entry, janet_ckeywordv("dst")); - } else { - v = janet_wrap_nil(); - } - if (janet_checktype(v, JANET_NIL)) { - return -1; - } else { - return janet_truthy(v); - } -} - -#ifdef JANET_WINDOWS -typedef int32_t timeint_t; -#else -typedef int64_t timeint_t; -#endif - -static timeint_t entry_getint(Janet env_entry, char *field) { - Janet i; - if (janet_checktype(env_entry, JANET_TABLE)) { - JanetTable *entry = janet_unwrap_table(env_entry); - i = janet_table_get(entry, janet_ckeywordv(field)); - } else if (janet_checktype(env_entry, JANET_STRUCT)) { - const JanetKV *entry = janet_unwrap_struct(env_entry); - i = janet_struct_get(entry, janet_ckeywordv(field)); - } else { - return 0; - } - - if (janet_checktype(i, JANET_NIL)) { - return 0; - } - -#ifdef JANET_WINDOWS - if (!janet_checkint(i)) { - janet_panicf("bad slot #%s, expected 32 bit signed integer, got %v", - field, i); - } -#else - if (!janet_checkint64(i)) { - janet_panicf("bad slot #%s, expected 64 bit signed integer, got %v", - field, i); - } -#endif - - return (timeint_t)janet_unwrap_number(i); -} - -static Janet os_mktime(int32_t argc, Janet *argv) { - janet_arity(argc, 1, 2); - time_t t; - struct tm t_info; - - /* Use memset instead of = {0} to silence paranoid warning in macos */ - memset(&t_info, 0, sizeof(t_info)); - - if (!janet_checktype(argv[0], JANET_TABLE) && - !janet_checktype(argv[0], JANET_STRUCT)) - janet_panic_type(argv[0], 0, JANET_TFLAG_DICTIONARY); - - t_info.tm_sec = entry_getint(argv[0], "seconds"); - t_info.tm_min = entry_getint(argv[0], "minutes"); - t_info.tm_hour = entry_getint(argv[0], "hours"); - t_info.tm_mday = entry_getint(argv[0], "month-day") + 1; - t_info.tm_mon = entry_getint(argv[0], "month"); - t_info.tm_year = entry_getint(argv[0], "year") - 1900; - t_info.tm_isdst = entry_getdst(argv[0]); - - if (argc >= 2 && janet_truthy(argv[1])) { - /* local time */ - t = mktime(&t_info); - } else { - /* utc time */ -#ifdef JANET_NO_UTC_MKTIME - janet_panic("os/mktime UTC not supported on this platform"); - return janet_wrap_nil(); -#else - t = timegm(&t_info); -#endif - } - - if (t == (time_t) -1) { - janet_panicf("%s", strerror(errno)); - } - - return janet_wrap_number((double)t); -} - -#ifdef JANET_NO_SYMLINKS -#define j_symlink link -#else -#define j_symlink symlink -#endif - -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_truthy(argv[2])) ? j_symlink : link)(oldpath, newpath); - if (-1 == res) janet_panicf("%s: %s -> %s", strerror(errno), oldpath, newpath); - return janet_wrap_nil(); -#endif -} - -static Janet os_symlink(int32_t argc, Janet *argv) { - janet_fixarity(argc, 2); -#ifdef JANET_WINDOWS - (void) argc; - (void) argv; - janet_panic("os/symlink 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 = j_symlink(oldpath, newpath); - if (-1 == res) janet_panicf("%s: %s -> %s", strerror(errno), oldpath, newpath); - return janet_wrap_nil(); -#endif -} - -#undef j_symlink - -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 - if (res == 0) return janet_wrap_true(); - if (errno == EEXIST) return janet_wrap_false(); - janet_panicf("%s: %s", strerror(errno), path); -} - -static Janet os_rmdir(int32_t argc, Janet *argv) { - janet_fixarity(argc, 1); - const char *path = janet_getcstring(argv, 0); -#ifdef JANET_WINDOWS - int res = _rmdir(path); -#else - int res = rmdir(path); -#endif - if (-1 == res) janet_panicf("%s: %s", strerror(errno), path); - return janet_wrap_nil(); -} - -static Janet os_cd(int32_t argc, Janet *argv) { - janet_fixarity(argc, 1); - const char *path = janet_getcstring(argv, 0); -#ifdef JANET_WINDOWS - int res = _chdir(path); -#else - int res = chdir(path); -#endif - if (-1 == res) janet_panicf("%s: %s", strerror(errno), path); - return janet_wrap_nil(); -} - -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); - if (-1 == res) janet_panic(strerror(errno)); - return janet_wrap_nil(); -} - -static Janet os_remove(int32_t argc, Janet *argv) { - janet_fixarity(argc, 1); - const char *path = janet_getcstring(argv, 0); - int status = remove(path); - if (-1 == status) janet_panicf("%s: %s", strerror(errno), path); - return janet_wrap_nil(); -} - -#ifndef JANET_NO_SYMLINKS -static Janet os_readlink(int32_t argc, Janet *argv) { - janet_fixarity(argc, 1); -#ifdef JANET_WINDOWS - (void) argc; - (void) argv; - janet_panic("os/readlink not supported on Windows"); - return janet_wrap_nil(); -#else - static char buffer[PATH_MAX]; - const char *path = janet_getcstring(argv, 0); - ssize_t len = readlink(path, buffer, sizeof buffer); - if (len < 0 || (size_t)len >= sizeof buffer) - janet_panicf("%s: %s", strerror(errno), path); - return janet_stringv((const uint8_t *)buffer, len); -#endif -} -#endif - -#ifdef JANET_WINDOWS - -typedef struct _stat jstat_t; -typedef unsigned short jmode_t; - -static int32_t janet_perm_to_unix(unsigned short m) { - int32_t ret = 0; - if (m & S_IEXEC) ret |= 0111; - if (m & S_IWRITE) ret |= 0222; - if (m & S_IREAD) ret |= 0444; - return ret; -} - -static unsigned short janet_perm_from_unix(int32_t x) { - unsigned short m = 0; - if (x & 111) m |= S_IEXEC; - if (x & 222) m |= S_IWRITE; - if (x & 444) m |= S_IREAD; - return m; -} - -static const uint8_t *janet_decode_mode(unsigned short m) { - const char *str = "other"; - if (m & _S_IFREG) str = "file"; - else if (m & _S_IFDIR) str = "directory"; - else if (m & _S_IFCHR) str = "character"; - return janet_ckeyword(str); -} - -static int32_t janet_decode_permissions(jmode_t mode) { - return (int32_t)(mode & (S_IEXEC | S_IWRITE | S_IREAD)); -} - -#else - -typedef struct stat jstat_t; -typedef mode_t jmode_t; - -static int32_t janet_perm_to_unix(mode_t m) { - return (int32_t) m; -} - -static mode_t janet_perm_from_unix(int32_t x) { - return (mode_t) x; -} - -static const uint8_t *janet_decode_mode(mode_t m) { - const char *str = "other"; - if (S_ISREG(m)) str = "file"; - else if (S_ISDIR(m)) str = "directory"; - else if (S_ISFIFO(m)) str = "fifo"; - else if (S_ISBLK(m)) str = "block"; - else if (S_ISSOCK(m)) str = "socket"; - else if (S_ISLNK(m)) str = "link"; - else if (S_ISCHR(m)) str = "character"; - return janet_ckeyword(str); -} - -static int32_t janet_decode_permissions(jmode_t mode) { - return (int32_t)(mode & 0777); -} - -#endif - -static int32_t os_parse_permstring(const uint8_t *perm) { - int32_t m = 0; - if (perm[0] == 'r') m |= 0400; - if (perm[1] == 'w') m |= 0200; - if (perm[2] == 'x') m |= 0100; - if (perm[3] == 'r') m |= 0040; - if (perm[4] == 'w') m |= 0020; - if (perm[5] == 'x') m |= 0010; - if (perm[6] == 'r') m |= 0004; - if (perm[7] == 'w') m |= 0002; - if (perm[8] == 'x') m |= 0001; - return m; -} - -static Janet os_make_permstring(int32_t permissions) { - uint8_t bytes[9] = {0}; - bytes[0] = (permissions & 0400) ? 'r' : '-'; - bytes[1] = (permissions & 0200) ? 'w' : '-'; - bytes[2] = (permissions & 0100) ? 'x' : '-'; - bytes[3] = (permissions & 0040) ? 'r' : '-'; - bytes[4] = (permissions & 0020) ? 'w' : '-'; - bytes[5] = (permissions & 0010) ? 'x' : '-'; - bytes[6] = (permissions & 0004) ? 'r' : '-'; - bytes[7] = (permissions & 0002) ? 'w' : '-'; - bytes[8] = (permissions & 0001) ? 'x' : '-'; - return janet_stringv(bytes, sizeof(bytes)); -} - -static int32_t os_get_unix_mode(const Janet *argv, int32_t n) { - int32_t unix_mode; - if (janet_checkint(argv[n])) { - /* Integer mode */ - int32_t x = janet_unwrap_integer(argv[n]); - if (x < 0 || x > 0777) { - janet_panicf("bad slot #%d, expected integer in range [0, 8r777], got %v", n, argv[n]); - } - unix_mode = x; - } else { - /* Bytes mode */ - JanetByteView bytes = janet_getbytes(argv, n); - if (bytes.len != 9) { - janet_panicf("bad slot #%d: expected byte sequence of length 9, got %v", n, argv[n]); - } - unix_mode = os_parse_permstring(bytes.bytes); - } - return unix_mode; -} - -static jmode_t os_getmode(const Janet *argv, int32_t n) { - return janet_perm_from_unix(os_get_unix_mode(argv, n)); -} - -/* Getters */ -static Janet os_stat_dev(jstat_t *st) { - return janet_wrap_number(st->st_dev); -} -static Janet os_stat_inode(jstat_t *st) { - return janet_wrap_number(st->st_ino); -} -static Janet os_stat_mode(jstat_t *st) { - return janet_wrap_keyword(janet_decode_mode(st->st_mode)); -} -static Janet os_stat_int_permissions(jstat_t *st) { - return janet_wrap_integer(janet_perm_to_unix(janet_decode_permissions(st->st_mode))); -} -static Janet os_stat_permissions(jstat_t *st) { - return os_make_permstring(janet_perm_to_unix(janet_decode_permissions(st->st_mode))); -} -static Janet os_stat_uid(jstat_t *st) { - return janet_wrap_number(st->st_uid); -} -static Janet os_stat_gid(jstat_t *st) { - return janet_wrap_number(st->st_gid); -} -static Janet os_stat_nlink(jstat_t *st) { - return janet_wrap_number(st->st_nlink); -} -static Janet os_stat_rdev(jstat_t *st) { - return janet_wrap_number(st->st_rdev); -} -static Janet os_stat_size(jstat_t *st) { - return janet_wrap_number(st->st_size); -} -static Janet os_stat_accessed(jstat_t *st) { - return janet_wrap_number((double) st->st_atime); -} -static Janet os_stat_modified(jstat_t *st) { - return janet_wrap_number((double) st->st_mtime); -} -static Janet os_stat_changed(jstat_t *st) { - return janet_wrap_number((double) st->st_ctime); -} -#ifdef JANET_WINDOWS -static Janet os_stat_blocks(jstat_t *st) { - return janet_wrap_number(0); -} -static Janet os_stat_blocksize(jstat_t *st) { - return janet_wrap_number(0); -} -#else -static Janet os_stat_blocks(jstat_t *st) { - return janet_wrap_number(st->st_blocks); -} -static Janet os_stat_blocksize(jstat_t *st) { - return janet_wrap_number(st->st_blksize); -} -#endif - -struct OsStatGetter { - const char *name; - Janet(*fn)(jstat_t *st); -}; - -static const struct OsStatGetter os_stat_getters[] = { - {"dev", os_stat_dev}, - {"inode", os_stat_inode}, - {"mode", os_stat_mode}, - {"int-permissions", os_stat_int_permissions}, - {"permissions", os_stat_permissions}, - {"uid", os_stat_uid}, - {"gid", os_stat_gid}, - {"nlink", os_stat_nlink}, - {"rdev", os_stat_rdev}, - {"size", os_stat_size}, - {"blocks", os_stat_blocks}, - {"blocksize", os_stat_blocksize}, - {"accessed", os_stat_accessed}, - {"modified", os_stat_modified}, - {"changed", os_stat_changed}, - {NULL, NULL} -}; - -static Janet os_stat_or_lstat(int do_lstat, int32_t argc, Janet *argv) { - janet_arity(argc, 1, 2); - const char *path = janet_getcstring(argv, 0); - JanetTable *tab = NULL; - int getall = 1; - const uint8_t *key; - if (argc == 2) { - if (janet_checktype(argv[1], JANET_KEYWORD)) { - getall = 0; - key = janet_getkeyword(argv, 1); - } else { - tab = janet_gettable(argv, 1); - } - } else { - tab = janet_table(0); - } - - /* Build result */ - jstat_t st; -#ifdef JANET_WINDOWS - (void) do_lstat; - int res = _stat(path, &st); -#else - int res; - if (do_lstat) { - res = lstat(path, &st); - } else { - res = stat(path, &st); - } -#endif - if (-1 == res) { - return janet_wrap_nil(); - } - - if (getall) { - /* Put results in table */ - for (const struct OsStatGetter *sg = os_stat_getters; sg->name != NULL; sg++) { - janet_table_put(tab, janet_ckeywordv(sg->name), sg->fn(&st)); - } - return janet_wrap_table(tab); - } else { - /* Get one result */ - for (const struct OsStatGetter *sg = os_stat_getters; sg->name != NULL; sg++) { - if (janet_cstrcmp(key, sg->name)) continue; - return sg->fn(&st); - } - janet_panicf("unexpected keyword %v", janet_wrap_keyword(key)); - return janet_wrap_nil(); - } -} - -static Janet os_stat(int32_t argc, Janet *argv) { - return os_stat_or_lstat(0, argc, argv); -} - -static Janet os_lstat(int32_t argc, Janet *argv) { - return os_stat_or_lstat(1, argc, argv); -} - -static Janet os_chmod(int32_t argc, Janet *argv) { - janet_fixarity(argc, 2); - const char *path = janet_getcstring(argv, 0); -#ifdef JANET_WINDOWS - int res = _chmod(path, os_getmode(argv, 1)); -#else - int res = chmod(path, os_getmode(argv, 1)); -#endif - if (-1 == res) janet_panicf("%s: %s", strerror(errno), path); - return janet_wrap_nil(); -} - -#ifndef JANET_NO_UMASK -static Janet os_umask(int32_t argc, Janet *argv) { - janet_fixarity(argc, 1); - int mask = (int) os_getmode(argv, 0); -#ifdef JANET_WINDOWS - int res = _umask(mask); -#else - int res = umask(mask); -#endif - return janet_wrap_integer(janet_perm_to_unix(res)); -} -#endif - -static Janet os_dir(int32_t argc, Janet *argv) { - janet_arity(argc, 1, 2); - const char *dir = janet_getcstring(argv, 0); - JanetArray *paths = (argc == 2) ? janet_getarray(argv, 1) : janet_array(0); -#ifdef JANET_WINDOWS - /* Read directory items with FindFirstFile / FindNextFile / FindClose */ - struct _finddata_t afile; - char pattern[MAX_PATH + 1]; - if (strlen(dir) > (sizeof(pattern) - 3)) - janet_panicf("path too long: %s", dir); - sprintf(pattern, "%s/*", dir); - intptr_t res = _findfirst(pattern, &afile); - if (-1 == res) janet_panicv(janet_cstringv(strerror(errno))); - do { - if (strcmp(".", afile.name) && strcmp("..", afile.name)) { - janet_array_push(paths, janet_cstringv(afile.name)); - } - } while (_findnext(res, &afile) != -1); - _findclose(res); -#else - /* Read directory items with opendir / readdir / closedir */ - struct dirent *dp; - DIR *dfd = opendir(dir); - if (dfd == NULL) janet_panicf("cannot open directory %s", dir); - while ((dp = readdir(dfd)) != NULL) { - if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) { - continue; - } - janet_array_push(paths, janet_cstringv(dp->d_name)); - } - closedir(dfd); -#endif - return janet_wrap_array(paths); -} - -static Janet os_rename(int32_t argc, Janet *argv) { - janet_fixarity(argc, 2); - const char *src = janet_getcstring(argv, 0); - const char *dest = janet_getcstring(argv, 1); - int status = rename(src, dest); - if (status) { - janet_panic(strerror(errno)); - } - return janet_wrap_nil(); -} - -static Janet os_realpath(int32_t argc, Janet *argv) { - janet_fixarity(argc, 1); - const char *src = janet_getcstring(argv, 0); -#ifdef JANET_NO_REALPATH - janet_panic("os/realpath not enabled for this platform"); -#else -#ifdef JANET_WINDOWS - char *dest = _fullpath(NULL, src, _MAX_PATH); -#else - char *dest = realpath(src, NULL); -#endif - if (NULL == dest) janet_panicf("%s: %s", strerror(errno), src); - Janet ret = janet_cstringv(dest); - free(dest); - return ret; -#endif -} - -static Janet os_permission_string(int32_t argc, Janet *argv) { - janet_fixarity(argc, 1); - return os_make_permstring(os_get_unix_mode(argv, 0)); -} - -static Janet os_permission_int(int32_t argc, Janet *argv) { - janet_fixarity(argc, 1); - return janet_wrap_integer(os_get_unix_mode(argv, 0)); -} - -#endif /* JANET_REDUCED_OS */ - -static const JanetReg os_cfuns[] = { - { - "os/exit", os_exit, - JDOC("(os/exit &opt 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" - "Check the current operating system. Returns one of:\n\n" - "\t:windows\n" - "\t:macos\n" - "\t:web - Web assembly (emscripten)\n" - "\t:linux\n" - "\t:freebsd\n" - "\t:openbsd\n" - "\t:netbsd\n" - "\t:posix - A POSIX compatible system (default)\n\n" - "May also return a custom keyword specified at build time.") - }, - { - "os/arch", os_arch, - JDOC("(os/arch)\n\n" - "Check the ISA that janet was compiled for. Returns one of:\n\n" - "\t:x86\n" - "\t:x86-64\n" - "\t:arm\n" - "\t:aarch64\n" - "\t:sparc\n" - "\t:wasm\n" - "\t:unknown\n") - }, -#ifndef JANET_REDUCED_OS - { - "os/environ", os_environ, - JDOC("(os/environ)\n\n" - "Get a copy of the os environment table.") - }, - { - "os/getenv", os_getenv, - JDOC("(os/getenv variable &opt dflt)\n\n" - "Get the string value of an environment variable.") - }, - { - "os/dir", os_dir, - JDOC("(os/dir dir &opt array)\n\n" - "Iterate over files and subdirectories in a directory. Returns an array of paths parts, " - "with only the file name or directory name and no prefix.") - }, - { - "os/stat", os_stat, - JDOC("(os/stat path &opt tab|key)\n\n" - "Gets information about a file or directory. Returns a table If the third argument is a keyword, returns " - " only that information from stat. If the file or directory does not exist, returns nil. The keys are\n\n" - "\t:dev - the device that the file is on\n" - "\t:mode - the type of file, one of :file, :directory, :block, :character, :fifo, :socket, :link, or :other\n" - "\t:int-permissions - A Unix permission integer like 8r744\n" - "\t:permissions - A Unix permission string like \"rwxr--r--\"\n" - "\t:uid - File uid\n" - "\t:gid - File gid\n" - "\t:nlink - number of links to file\n" - "\t:rdev - Real device of file. 0 on windows.\n" - "\t:size - size of file in bytes\n" - "\t:blocks - number of blocks in file. 0 on windows\n" - "\t:blocksize - size of blocks in file. 0 on windows\n" - "\t:accessed - timestamp when file last accessed\n" - "\t:changed - timestamp when file last changed (permissions changed)\n" - "\t:modified - timestamp when file last modified (content changed)\n") - }, - { - "os/lstat", os_lstat, - JDOC("(os/lstat path &opt tab|key)\n\n" - "Like os/stat, but don't follow symlinks.\n") - }, - { - "os/chmod", os_chmod, - JDOC("(os/chmod path mode)\n\n" - "Change file permissions, where mode is a permission string as returned by " - "os/perm-string, or an integer as returned by os/perm-int. " - "When mode is an integer, it is interpreted as a Unix permission value, best specified in octal, like " - "8r666 or 8r400. Windows will not differentiate between user, group, and other permissions, and thus will combine all of these permissions. Returns nil.") - }, - { - "os/touch", os_touch, - JDOC("(os/touch path &opt 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 nil on success, errors on failure.") - }, -#ifndef JANET_NO_UMASK - { - "os/umask", os_umask, - JDOC("(os/umask mask)\n\n" - "Set a new umask, returns the old umask.") - }, -#endif - { - "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. Returns true if the directory was created, false if the directory already exists, and " - "errors otherwise.") - }, - { - "os/rmdir", os_rmdir, - JDOC("(os/rmdir path)\n\n" - "Delete a directory. The directory must be empty to succeed.") - }, - { - "os/rm", os_remove, - JDOC("(os/rm path)\n\n" - "Delete a file. Returns nil.") - }, - { - "os/link", os_link, - JDOC("(os/link oldpath newpath &opt symlink)\n\n" - "Create a link at newpath that points to oldpath and returns nil. " - "Iff symlink is truthy, creates a symlink. " - "Iff symlink is falsey or not provided, " - "creates a hard link. Does not work on Windows.") - }, -#ifndef JANET_NO_SYMLINKS - { - "os/symlink", os_symlink, - JDOC("(os/symlink oldpath newpath)\n\n" - "Create a symlink from oldpath to newpath, returning nil. Same as (os/link oldpath newpath true).") - }, - { - "os/readlink", os_readlink, - JDOC("(os/readlink path)\n\n" - "Read the contents of a symbolic link. Does not work on Windows.\n") - }, -#endif -#ifndef JANET_NO_PROCESSES - { - "os/execute", os_execute, - JDOC("(os/execute args &opts flags env)\n\n" - "Execute a program on the system and pass it string arguments. Flags " - "is a keyword that modifies how the program will execute.\n\n" - "\t:e - enables passing an environment to the program. Without :e, the " - "current environment is inherited.\n" - "\t:p - allows searching the current PATH for the binary to execute. " - "Without this flag, binaries must use absolute paths.\n" - "\t:x - raise error if exit code is non-zero.\n" - "env is a table or struct mapping environment variables to values. It can also " - "contain the keys :in, :out, and :err, which allow redirecting stdio in the subprocess. " - "These arguments should be core/file values. " - "Returns the exit status of the program.") - }, - { - "os/spawn", os_spawn, - JDOC("(os/spawn args &opts flags env)\n\n" - "Execute a program on the system and return a handle to the process. Otherwise, the " - "same arguments as os/execute. Does not wait for the process.") - }, - { - "os/shell", os_shell, - JDOC("(os/shell str)\n\n" - "Pass a command string str directly to the system shell.") - }, -#endif - { - "os/setenv", os_setenv, - JDOC("(os/setenv variable value)\n\n" - "Set an environment variable.") - }, - { - "os/time", os_time, - JDOC("(os/time)\n\n" - "Get the current time expressed as the number of seconds since " - "January 1, 1970, the Unix epoch. Returns a real number.") - }, - { - "os/mktime", os_mktime, - JDOC("(os/mktime date-struct &opt local)\n\n" - "Get the broken down date-struct time expressed as the number " - " of seconds since January 1, 1970, the Unix epoch. " - "Returns a real number. " - "Date is given in UTC unless local is truthy, in which case the " - "date is computed for the local timezone.\n\n" - "Inverse function to os/date.") - }, - { - "os/clock", os_clock, - JDOC("(os/clock)\n\n" - "Return the number of seconds since some fixed point in time. The clock " - "is guaranteed to be non decreasing in real time.") - }, - { - "os/sleep", os_sleep, - JDOC("(os/sleep nsec)\n\n" - "Suspend the program for nsec seconds. 'nsec' can be a real number. Returns " - "nil.") - }, - { - "os/cwd", os_cwd, - JDOC("(os/cwd)\n\n" - "Returns the current working directory.") - }, - { - "os/cryptorand", os_cryptorand, - JDOC("(os/cryptorand n &opt buf)\n\n" - "Get or append n bytes of good quality random data provided by the OS. Returns a new buffer or buf.") - }, - { - "os/date", os_date, - JDOC("(os/date &opt time local)\n\n" - "Returns the given time as a date struct, or the current time if no time is given. " - "Returns a struct with following key values. Note that all numbers are 0-indexed. " - "Date is given in UTC unless local is truthy, in which case the date is formatted for " - "the local timezone.\n\n" - "\t:seconds - number of seconds [0-61]\n" - "\t:minutes - number of minutes [0-59]\n" - "\t:hours - number of hours [0-23]\n" - "\t:month-day - day of month [0-30]\n" - "\t:month - month of year [0, 11]\n" - "\t:year - years since year 0 (e.g. 2019)\n" - "\t:week-day - day of the week [0-6]\n" - "\t:year-day - day of the year [0-365]\n" - "\t:dst - If Day Light Savings is in effect") - }, - { - "os/rename", os_rename, - JDOC("(os/rename oldname newname)\n\n" - "Rename a file on disk to a new path. Returns nil.") - }, - { - "os/realpath", os_realpath, - JDOC("(os/realpath path)\n\n" - "Get the absolute path for a given path, following ../, ./, and symlinks. " - "Returns an absolute path as a string. Will raise an error on Windows.") - }, - { - "os/perm-string", os_permission_string, - JDOC("(os/perm-string int)\n\n" - "Convert a Unix octal permission value from a permission integer as returned by os/stat " - "to a human readable string, that follows the formatting " - "of unix tools like ls. Returns the string as a 9 character string of r, w, x and - characters. Does not " - "include the file/directory/symlink character as rendered by `ls`.") - }, - { - "os/perm-int", os_permission_int, - JDOC("(os/perm-int bytes)\n\n" - "Parse a 9 character permission string and return an integer that can be used by chmod.") - }, - { - "os/proc-wait", os_proc_wait, - JDOC("(os/proc-wait proc)\n\n" - "Block until the subprocess completes. Returns the subprocess return code.") - }, - { - "os/proc-kill", os_proc_kill, - JDOC("(os/proc-kill proc &opt wait)\n\n" - "Kill a subprocess by sending SIGKILL to it on posix systems, or by closing the process " - "handle on windows. If wait is truthy, will wait for the process to finsih and " - "returns the exit code. Otherwise, returns proc.") - }, -#endif - {NULL, NULL, NULL} -}; - -/* Module entry point */ -void janet_lib_os(JanetTable *env) { -#if !defined(JANET_REDUCED_OS) && defined(JANET_WINDOWS) && defined(JANET_THREADS) - /* During start up, the top-most abstract machine (thread) - * in the thread tree sets up the critical section. */ - if (!env_lock_initialized) { - InitializeCriticalSection(&env_lock); - env_lock_initialized = 1; - } -#endif - janet_core_cfuns(env, NULL, os_cfuns); -}