2018-03-29 00:50:20 +00:00
/*
2023-01-07 21:03:35 +00:00
* Copyright ( c ) 2023 Calvin Rose and contributors .
2018-03-29 00:50:20 +00:00
*
* 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 .
*/
2019-01-24 05:15:58 +00:00
# ifndef JANET_AMALG
2019-12-31 00:06:15 +00:00
# include "features.h"
2019-02-19 01:13:35 +00:00
# include <janet.h>
2019-01-24 05:15:58 +00:00
# include "util.h"
2020-09-02 01:06:35 +00:00
# include "gc.h"
2019-01-24 05:15:58 +00:00
# endif
2019-03-29 03:22:58 +00:00
# ifndef JANET_REDUCED_OS
2019-12-15 18:47:12 +00:00
# include <stdlib.h>
2018-05-13 00:31:28 +00:00
# include <time.h>
2019-03-29 03:34:24 +00:00
# include <fcntl.h>
# include <errno.h>
2020-03-09 16:34:30 +00:00
# include <limits.h>
2019-03-29 03:34:24 +00:00
# include <stdio.h>
# include <string.h>
# include <sys/stat.h>
2020-09-02 02:47:08 +00:00
# include <signal.h>
2018-05-13 00:31:28 +00:00
2022-05-28 17:01:23 +00:00
# ifdef JANET_BSD
# include <sys/sysctl.h>
# endif
2022-05-28 17:19:25 +00:00
# ifdef JANET_LINUX
# include <sched.h>
# endif
2018-09-06 02:18:42 +00:00
# ifdef JANET_WINDOWS
2019-03-29 03:34:24 +00:00
# include <windows.h>
2018-05-19 05:09:56 +00:00
# include <direct.h>
2019-03-29 03:34:24 +00:00
# include <sys/utime.h>
# include <io.h>
2019-05-30 23:14:54 +00:00
# include <process.h>
2018-05-13 00:31:28 +00:00
# else
2019-05-30 23:13:13 +00:00
# include <spawn.h>
2019-03-29 03:22:58 +00:00
# include <utime.h>
2018-05-13 00:31:28 +00:00
# include <unistd.h>
2019-03-29 03:22:58 +00:00
# include <dirent.h>
2018-07-07 01:50:59 +00:00
# include <sys/types.h>
# include <sys/wait.h>
2021-04-27 10:54:24 +00:00
# ifdef JANET_APPLE
# include <crt_externs.h>
# define environ (*_NSGetEnviron())
# else
2019-05-31 17:43:38 +00:00
extern char * * environ ;
2021-04-27 10:54:24 +00:00
# endif
2020-01-07 01:30:44 +00:00
# ifdef JANET_THREADS
# include <pthread.h>
# endif
2018-05-13 00:31:28 +00:00
# endif
2018-03-29 00:50:20 +00:00
2020-03-18 18:52:25 +00:00
/* 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
2020-01-07 01:30:44 +00:00
/* 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 CRITICAL_SECTION env_lock ;
static void janet_lock_environ ( void ) {
2020-01-12 16:18:03 +00:00
EnterCriticalSection ( & env_lock ) ;
2020-01-07 01:30:44 +00:00
}
static void janet_unlock_environ ( void ) {
2020-01-12 16:18:03 +00:00
LeaveCriticalSection ( & env_lock ) ;
2020-01-07 01:30:44 +00:00
}
# 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
2019-03-29 03:34:24 +00:00
# endif /* JANET_REDCUED_OS */
2019-03-29 03:22:58 +00:00
/* Core OS functions */
/* Full OS functions */
2019-08-18 15:00:04 +00:00
# define janet_stringify1(x) #x
# define janet_stringify(x) janet_stringify1(x)
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_which ,
" (os/which) " ,
" Check the current operating system. Returns one of: \n \n "
" * :windows \n \n "
2023-01-21 17:50:03 +00:00
" * :mingw \n \n "
2023-01-28 20:00:02 +00:00
" * :cygwin \n \n "
2021-07-26 07:48:04 +00:00
" * :macos \n \n "
" * :web - Web assembly (emscripten) \n \n "
" * :linux \n \n "
" * :freebsd \n \n "
" * :openbsd \n \n "
" * :netbsd \n \n "
2023-03-07 14:45:31 +00:00
" * :dragonfly \n \n "
" * :bsd \n \n "
2021-07-26 07:48:04 +00:00
" * :posix - A POSIX compatible system (default) \n \n "
" May also return a custom keyword specified at build time. " ) {
2019-01-06 01:45:24 +00:00
janet_fixarity ( argc , 0 ) ;
2019-01-06 01:09:03 +00:00
( void ) argv ;
2019-08-18 15:00:04 +00:00
# if defined(JANET_OS_NAME)
return janet_ckeywordv ( janet_stringify ( JANET_OS_NAME ) ) ;
2023-01-21 17:50:03 +00:00
# elif defined(JANET_MINGW)
return janet_ckeywordv ( " mingw " ) ;
2023-01-28 20:00:02 +00:00
# elif defined(JANET_CYGWIN)
return janet_ckeywordv ( " cygwin " ) ;
2019-08-18 15:00:04 +00:00
# elif defined(JANET_WINDOWS)
2019-02-20 01:51:34 +00:00
return janet_ckeywordv ( " windows " ) ;
2019-12-15 21:41:58 +00:00
# elif defined(JANET_APPLE)
2019-02-20 01:51:34 +00:00
return janet_ckeywordv ( " macos " ) ;
# elif defined(__EMSCRIPTEN__)
return janet_ckeywordv ( " web " ) ;
2019-12-15 21:41:58 +00:00
# elif defined(JANET_LINUX)
2019-07-27 03:43:54 +00:00
return janet_ckeywordv ( " linux " ) ;
2019-07-27 16:36:48 +00:00
# elif defined(__FreeBSD__)
2019-07-27 16:29:40 +00:00
return janet_ckeywordv ( " freebsd " ) ;
2019-07-27 16:36:48 +00:00
# elif defined(__NetBSD__)
2019-07-27 16:29:40 +00:00
return janet_ckeywordv ( " netbsd " ) ;
2019-07-27 16:36:48 +00:00
# elif defined(__OpenBSD__)
2019-07-27 16:29:40 +00:00
return janet_ckeywordv ( " openbsd " ) ;
2023-03-07 14:45:31 +00:00
# elif defined(__DragonFly__)
return janet_ckeywordv ( " dragonfly " ) ;
2019-12-15 21:41:58 +00:00
# elif defined(JANET_BSD)
return janet_ckeywordv ( " bsd " ) ;
2019-02-20 01:51:34 +00:00
# else
return janet_ckeywordv ( " posix " ) ;
# endif
2018-08-07 04:54:47 +00:00
}
2019-08-18 15:00:04 +00:00
/* Detect the ISA we are compiled for */
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_arch ,
" (os/arch) " ,
" Check the ISA that janet was compiled for. Returns one of: \n \n "
" * :x86 \n \n "
2021-09-05 17:32:33 +00:00
" * :x64 \n \n "
2021-07-26 07:48:04 +00:00
" * :arm \n \n "
" * :aarch64 \n \n "
2023-01-27 17:23:57 +00:00
" * :riscv32 \n \n "
" * :riscv64 \n \n "
2021-07-26 07:48:04 +00:00
" * :sparc \n \n "
" * :wasm \n \n "
" * :unknown \n " ) {
2019-08-18 15:00:04 +00:00
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 ) ) ;
2019-08-18 15:08:52 +00:00
# elif defined(__EMSCRIPTEN__)
return janet_ckeywordv ( " wasm " ) ;
2019-08-18 15:00:04 +00:00
# elif (defined(__x86_64__) || defined(_M_X64))
2019-12-19 18:18:46 +00:00
return janet_ckeywordv ( " x64 " ) ;
2019-08-18 15:00:04 +00:00
# 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 " ) ;
2023-01-27 17:23:57 +00:00
# elif (defined(__riscv) && (__riscv_xlen == 64))
return janet_ckeywordv ( " riscv64 " ) ;
# elif (defined(__riscv) && (__riscv_xlen == 32))
return janet_ckeywordv ( " riscv32 " ) ;
2019-08-18 15:00:04 +00:00
# elif (defined(__sparc__))
return janet_ckeywordv ( " sparc " ) ;
2020-06-26 09:11:21 +00:00
# elif (defined(__ppc__))
return janet_ckeywordv ( " ppc " ) ;
2022-01-21 22:08:57 +00:00
# elif (defined(__ppc64__) || defined(_ARCH_PPC64) || defined(_M_PPC))
return janet_ckeywordv ( " ppc64 " ) ;
2019-08-18 15:00:04 +00:00
# else
return janet_ckeywordv ( " unknown " ) ;
# endif
}
2023-01-28 20:00:02 +00:00
/* Detect the compiler used to build the interpreter */
JANET_CORE_FN ( os_compiler ,
" (os/compiler) " ,
" Get the compiler used to compile the interpreter. Returns one of: \n \n "
" * :gcc \n \n "
" * :clang \n \n "
" * :msvc \n \n "
" * :unknown \n \n " ) {
janet_fixarity ( argc , 0 ) ;
( void ) argv ;
# if defined(_MSC_VER)
return janet_ckeywordv ( " msvc " ) ;
# elif defined(__clang__)
return janet_ckeywordv ( " clang " ) ;
# elif defined(__GNUC__)
return janet_ckeywordv ( " gcc " ) ;
# else
return janet_ckeywordv ( " unknown " ) ;
# endif
}
2019-08-18 15:00:04 +00:00
# undef janet_stringify1
# undef janet_stringify
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_exit ,
" (os/exit &opt x) " ,
" 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. " ) {
2019-03-29 03:22:58 +00:00
janet_arity ( argc , 0 , 1 ) ;
2020-01-10 23:25:10 +00:00
int status ;
2019-03-29 03:22:58 +00:00
if ( argc = = 0 ) {
2020-01-10 23:25:10 +00:00
status = EXIT_SUCCESS ;
2019-03-29 03:22:58 +00:00
} else if ( janet_checkint ( argv [ 0 ] ) ) {
2020-01-10 23:25:10 +00:00
status = janet_unwrap_integer ( argv [ 0 ] ) ;
2019-03-29 03:22:58 +00:00
} else {
2020-01-10 23:25:10 +00:00
status = EXIT_FAILURE ;
2019-03-29 03:22:58 +00:00
}
2020-01-10 23:25:10 +00:00
janet_deinit ( ) ;
exit ( status ) ;
2019-03-29 03:22:58 +00:00
return janet_wrap_nil ( ) ;
}
2022-12-16 04:16:43 +00:00
# ifndef JANET_REDUCED_OS
2022-05-28 17:01:23 +00:00
JANET_CORE_FN ( os_cpu_count ,
" (os/cpu-count &opt dflt) " ,
" Get an approximate number of CPUs available on for this process to use. If "
" unable to get an approximation, will return a default value dflt. " ) {
janet_arity ( argc , 0 , 1 ) ;
Janet dflt = argc > 0 ? argv [ 0 ] : janet_wrap_nil ( ) ;
# ifdef JANET_WINDOWS
( void ) dflt ;
SYSTEM_INFO info ;
GetSystemInfo ( & info ) ;
return janet_wrap_integer ( info . dwNumberOfProcessors ) ;
# elif defined(JANET_LINUX)
( void ) dflt ;
2022-05-28 17:19:25 +00:00
cpu_set_t cs ;
CPU_ZERO ( & cs ) ;
sched_getaffinity ( 0 , sizeof ( cs ) , & cs ) ;
int count = CPU_COUNT ( & cs ) ;
return janet_wrap_integer ( count ) ;
2022-05-28 17:01:23 +00:00
# elif defined(JANET_BSD) && defined(HW_NCPUONLINE)
( void ) dflt ;
const int name [ 2 ] = { CTL_HW , HW_NCPUONLINE } ;
int result = 0 ;
size_t len = sizeof ( int ) ;
2022-05-28 17:23:28 +00:00
if ( - 1 = = sysctl ( name , 2 , & result , & len , NULL , 0 ) ) {
2022-05-28 17:01:23 +00:00
return dflt ;
}
return janet_wrap_integer ( result ) ;
# elif defined(JANET_BSD) && defined(HW_NCPU)
( void ) dflt ;
const int name [ 2 ] = { CTL_HW , HW_NCPU } ;
int result = 0 ;
size_t len = sizeof ( int ) ;
2022-05-28 17:21:44 +00:00
if ( - 1 = = sysctl ( name , 2 , & result , & len , NULL , 0 ) ) {
2022-05-28 17:01:23 +00:00
return dflt ;
}
return janet_wrap_integer ( result ) ;
# else
return dflt ;
# endif
}
2019-03-29 03:22:58 +00:00
2020-05-09 17:00:01 +00:00
# ifndef JANET_NO_PROCESSES
2020-11-29 21:36:21 +00:00
2019-06-12 16:05:01 +00:00
/* Get env for os_execute */
2021-02-14 17:04:59 +00:00
# ifdef JANET_WINDOWS
typedef char * EnvBlock ;
# else
typedef char * * EnvBlock ;
# endif
/* Get env for os_execute */
static EnvBlock os_execute_env ( int32_t argc , const Janet * argv ) {
if ( argc < = 2 ) return NULL ;
JanetDictView dict = janet_getdictionary ( argv , 2 ) ;
# ifdef JANET_WINDOWS
JanetBuffer * temp = janet_buffer ( 10 ) ;
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 ) ;
janet_buffer_push_bytes ( temp , keys , janet_string_length ( keys ) ) ;
janet_buffer_push_u8 ( temp , ' = ' ) ;
janet_buffer_push_bytes ( temp , vals , janet_string_length ( vals ) ) ;
janet_buffer_push_u8 ( temp , ' \0 ' ) ;
}
janet_buffer_push_u8 ( temp , ' \0 ' ) ;
char * ret = janet_smalloc ( temp - > count ) ;
memcpy ( ret , temp - > data , temp - > count ) ;
return ret ;
# else
char * * 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 ;
2019-05-30 22:40:10 +00:00
}
}
2021-02-14 17:04:59 +00:00
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 ;
2018-07-07 01:50:59 +00:00
}
2021-02-14 17:04:59 +00:00
envp [ j ] = NULL ;
2019-05-30 22:40:10 +00:00
return envp ;
2021-02-14 17:04:59 +00:00
# endif
2019-05-30 22:40:10 +00:00
}
2018-07-07 01:50:59 +00:00
2021-02-14 17:04:59 +00:00
static void os_execute_cleanup ( EnvBlock envp , const char * * child_argv ) {
2019-05-31 17:43:38 +00:00
# ifdef JANET_WINDOWS
( void ) child_argv ;
2021-02-15 16:27:19 +00:00
if ( NULL ! = envp ) janet_sfree ( envp ) ;
2019-05-31 17:43:38 +00:00
# else
2019-06-02 03:38:10 +00:00
janet_sfree ( ( void * ) child_argv ) ;
2019-05-30 22:40:10 +00:00
if ( NULL ! = envp ) {
char * * envitem = envp ;
while ( * envitem ! = NULL ) {
2021-02-15 16:27:19 +00:00
janet_sfree ( * envitem ) ;
2019-05-30 22:40:10 +00:00
envitem + + ;
}
2018-07-07 01:50:59 +00:00
}
2019-06-02 03:38:10 +00:00
janet_sfree ( envp ) ;
2021-02-14 17:04:59 +00:00
# endif
2018-07-07 01:50:59 +00:00
}
2019-05-30 22:40:10 +00:00
2019-05-31 17:43:38 +00:00
# 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 ) ;
2019-05-30 22:40:10 +00:00
2019-05-31 17:43:38 +00:00
/* 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 , ' " ' ) ;
}
2018-07-07 01:50:59 +00:00
}
2019-05-31 17:43:38 +00:00
janet_buffer_push_u8 ( b , 0 ) ;
return b ;
}
# endif
2020-09-02 01:06:35 +00:00
/* Process type for when running a subprocess and not immediately waiting */
static const JanetAbstractType ProcAT ;
2020-09-02 02:36:49 +00:00
# define JANET_PROC_CLOSED 1
# define JANET_PROC_WAITED 2
2020-11-29 21:36:21 +00:00
# define JANET_PROC_WAITING 4
# define JANET_PROC_ERROR_NONZERO 8
2021-01-16 21:11:07 +00:00
# define JANET_PROC_OWNS_STDIN 16
# define JANET_PROC_OWNS_STDOUT 32
# define JANET_PROC_OWNS_STDERR 64
2021-07-24 16:55:04 +00:00
# define JANET_PROC_ALLOW_ZOMBIE 128
2020-09-02 01:06:35 +00:00
typedef struct {
2020-09-02 02:36:49 +00:00
int flags ;
2020-09-02 01:06:35 +00:00
# ifdef JANET_WINDOWS
2020-09-03 00:07:45 +00:00
HANDLE pHandle ;
2020-09-03 00:12:27 +00:00
HANDLE tHandle ;
2020-09-02 01:06:35 +00:00
# else
2020-12-31 17:22:18 +00:00
pid_t pid ;
2020-09-02 01:06:35 +00:00
# endif
int return_code ;
2020-11-15 18:13:30 +00:00
# ifdef JANET_EV
JanetStream * in ;
JanetStream * out ;
JanetStream * err ;
# else
2020-09-02 01:06:35 +00:00
JanetFile * in ;
JanetFile * out ;
JanetFile * err ;
2020-11-15 18:13:30 +00:00
# endif
2020-09-02 01:06:35 +00:00
} JanetProc ;
2020-11-29 21:36:21 +00:00
# ifdef JANET_EV
2021-01-03 17:21:44 +00:00
# ifdef JANET_WINDOWS
static JanetEVGenericMessage janet_proc_wait_subr ( JanetEVGenericMessage args ) {
JanetProc * proc = ( JanetProc * ) args . argp ;
WaitForSingleObject ( proc - > pHandle , INFINITE ) ;
2023-01-21 17:50:03 +00:00
DWORD exitcode = 0 ;
GetExitCodeProcess ( proc - > pHandle , & exitcode ) ;
args . tag = ( int32_t ) exitcode ;
2021-01-03 17:21:44 +00:00
return args ;
}
# else /* windows check */
2021-11-19 02:30:06 +00:00
static int proc_get_status ( JanetProc * proc ) {
/* Use POSIX shell semantics for interpreting signals */
int status = 0 ;
pid_t result ;
do {
result = waitpid ( proc - > pid , & status , 0 ) ;
} while ( result = = - 1 & & errno = = EINTR ) ;
if ( WIFEXITED ( status ) ) {
status = WEXITSTATUS ( status ) ;
} else if ( WIFSTOPPED ( status ) ) {
status = WSTOPSIG ( status ) + 128 ;
} else {
status = WTERMSIG ( status ) + 128 ;
}
return status ;
}
2020-12-31 22:12:42 +00:00
/* Function that is called in separate thread to wait on a pid */
static JanetEVGenericMessage janet_proc_wait_subr ( JanetEVGenericMessage args ) {
JanetProc * proc = ( JanetProc * ) args . argp ;
2021-11-19 02:30:06 +00:00
args . tag = proc_get_status ( proc ) ;
2021-01-03 17:21:44 +00:00
return args ;
}
# endif /* End windows check */
/* Callback that is called in main thread when subroutine completes. */
static void janet_proc_wait_cb ( JanetEVGenericMessage args ) {
2021-07-25 01:30:36 +00:00
janet_ev_dec_refcount ( ) ;
2021-01-03 17:21:44 +00:00
JanetProc * proc = ( JanetProc * ) args . argp ;
2020-12-31 22:12:42 +00:00
if ( NULL ! = proc ) {
2021-11-19 02:30:06 +00:00
int status = args . tag ;
2020-12-31 22:12:42 +00:00
proc - > return_code = ( int32_t ) status ;
proc - > flags | = JANET_PROC_WAITED ;
proc - > flags & = ~ JANET_PROC_WAITING ;
janet_gcunroot ( janet_wrap_abstract ( proc ) ) ;
janet_gcunroot ( janet_wrap_fiber ( args . fiber ) ) ;
if ( ( status ! = 0 ) & & ( proc - > flags & JANET_PROC_ERROR_NONZERO ) ) {
JanetString s = janet_formatc ( " command failed with non-zero exit code %d " , status ) ;
janet_cancel ( args . fiber , janet_wrap_string ( s ) ) ;
} else {
janet_schedule ( args . fiber , janet_wrap_integer ( status ) ) ;
}
2020-11-29 21:36:21 +00:00
}
}
2020-12-31 22:12:42 +00:00
2021-01-03 17:21:44 +00:00
# endif /* End ev check */
2020-11-29 21:36:21 +00:00
2020-09-04 19:54:58 +00:00
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 ) ) {
2021-07-24 16:55:04 +00:00
if ( ! ( proc - > flags & JANET_PROC_ALLOW_ZOMBIE ) ) {
TerminateProcess ( proc - > pHandle , 1 ) ;
}
2020-09-04 19:54:58 +00:00
CloseHandle ( proc - > pHandle ) ;
CloseHandle ( proc - > tHandle ) ;
}
# else
2021-07-24 16:55:04 +00:00
if ( ! ( proc - > flags & ( JANET_PROC_WAITED | JANET_PROC_ALLOW_ZOMBIE ) ) ) {
2020-09-04 19:54:58 +00:00
/* Kill and wait to prevent zombies */
kill ( proc - > pid , SIGKILL ) ;
int status ;
2021-11-27 15:05:43 +00:00
if ( ! ( proc - > flags & JANET_PROC_WAITING ) ) {
waitpid ( proc - > pid , & status , 0 ) ;
}
2020-09-04 19:54:58 +00:00
}
# endif
return 0 ;
}
2020-09-02 01:06:35 +00:00
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 ;
}
2020-11-29 21:36:21 +00:00
# ifdef JANET_EV
2021-01-03 17:47:29 +00:00
static JANET_NO_RETURN void
# else
static Janet
2020-11-29 21:36:21 +00:00
# endif
2021-01-03 17:47:29 +00:00
os_proc_wait_impl ( JanetProc * proc ) {
2020-11-29 21:36:21 +00:00
if ( proc - > flags & ( JANET_PROC_WAITED | JANET_PROC_WAITING ) ) {
janet_panicf ( " cannot wait twice on a process " ) ;
2020-09-02 01:06:35 +00:00
}
2020-11-29 21:36:21 +00:00
# ifdef JANET_EV
2020-12-31 22:12:42 +00:00
/* Event loop implementation - threaded call */
2020-11-29 21:36:21 +00:00
proc - > flags | = JANET_PROC_WAITING ;
2020-12-31 22:12:42 +00:00
JanetEVGenericMessage targs ;
memset ( & targs , 0 , sizeof ( targs ) ) ;
targs . argp = proc ;
targs . fiber = janet_root_fiber ( ) ;
janet_gcroot ( janet_wrap_abstract ( proc ) ) ;
janet_gcroot ( janet_wrap_fiber ( targs . fiber ) ) ;
janet_ev_threaded_call ( janet_proc_wait_subr , targs , janet_proc_wait_cb ) ;
2020-11-29 21:36:21 +00:00
janet_await ( ) ;
# else
/* Non evented implementation */
2020-09-02 02:36:49 +00:00
proc - > flags | = JANET_PROC_WAITED ;
2020-09-02 01:06:35 +00:00
int status = 0 ;
2020-09-02 02:36:49 +00:00
# ifdef JANET_WINDOWS
2020-09-03 00:07:45 +00:00
WaitForSingleObject ( proc - > pHandle , INFINITE ) ;
GetExitCodeProcess ( proc - > pHandle , & status ) ;
2020-09-03 00:12:27 +00:00
if ( ! ( proc - > flags & JANET_PROC_CLOSED ) ) {
proc - > flags | = JANET_PROC_CLOSED ;
CloseHandle ( proc - > pHandle ) ;
CloseHandle ( proc - > tHandle ) ;
2020-09-03 00:07:45 +00:00
}
2020-09-02 02:36:49 +00:00
# else
2020-09-02 01:06:35 +00:00
waitpid ( proc - > pid , & status , 0 ) ;
2020-09-02 02:36:49 +00:00
# endif
2020-09-02 01:06:35 +00:00
proc - > return_code = ( int32_t ) status ;
return janet_wrap_integer ( proc - > return_code ) ;
2020-11-29 21:36:21 +00:00
# endif
2020-09-02 01:06:35 +00:00
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_proc_wait ,
" (os/proc-wait proc) " ,
" Block until the subprocess completes. Returns the subprocess return code. " ) {
2020-09-02 02:36:49 +00:00
janet_fixarity ( argc , 1 ) ;
JanetProc * proc = janet_getabstract ( argv , 0 , & ProcAT ) ;
2021-01-03 17:54:31 +00:00
# ifdef JANET_EV
os_proc_wait_impl ( proc ) ;
return janet_wrap_nil ( ) ;
# else
2020-09-02 02:36:49 +00:00
return os_proc_wait_impl ( proc ) ;
2021-01-03 17:54:31 +00:00
# endif
2020-09-02 02:36:49 +00:00
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_proc_kill ,
" (os/proc-kill proc &opt wait) " ,
" Kill a subprocess by sending SIGKILL to it on posix systems, or by closing the process "
2022-04-18 00:42:32 +00:00
" handle on windows. If `wait` is truthy, will wait for the process to finish and "
" returns the exit code. Otherwise, returns `proc`. " ) {
2020-09-02 02:36:49 +00:00
janet_arity ( argc , 1 , 2 ) ;
JanetProc * proc = janet_getabstract ( argv , 0 , & ProcAT ) ;
2020-09-04 19:54:58 +00:00
if ( proc - > flags & JANET_PROC_WAITED ) {
janet_panicf ( " cannot kill process that has already finished " ) ;
}
2020-09-02 02:36:49 +00:00
# ifdef JANET_WINDOWS
if ( proc - > flags & JANET_PROC_CLOSED ) {
janet_panicf ( " cannot close process handle that is already closed " ) ;
}
proc - > flags | = JANET_PROC_CLOSED ;
2021-07-11 09:21:55 +00:00
TerminateProcess ( proc - > pHandle , 1 ) ;
2020-09-03 00:12:27 +00:00
CloseHandle ( proc - > pHandle ) ;
CloseHandle ( proc - > tHandle ) ;
2020-09-02 02:36:49 +00:00
# else
int status = kill ( proc - > pid , SIGKILL ) ;
if ( status ) {
janet_panic ( strerror ( errno ) ) ;
}
2020-09-03 00:07:45 +00:00
# endif
2020-09-02 02:36:49 +00:00
/* After killing process we wait on it. */
if ( argc > 1 & & janet_truthy ( argv [ 1 ] ) ) {
2021-01-03 17:54:31 +00:00
# ifdef JANET_EV
os_proc_wait_impl ( proc ) ;
return janet_wrap_nil ( ) ;
# else
2020-09-02 02:36:49 +00:00
return os_proc_wait_impl ( proc ) ;
2021-01-03 17:54:31 +00:00
# endif
2020-09-02 02:36:49 +00:00
} else {
return argv [ 0 ] ;
}
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_proc_close ,
" (os/proc-close proc) " ,
" Wait on a process if it has not been waited on, and close pipes created by `os/spawn` "
" if they have not been closed. Returns nil. " ) {
2021-01-16 21:11:07 +00:00
janet_fixarity ( argc , 1 ) ;
JanetProc * proc = janet_getabstract ( argv , 0 , & ProcAT ) ;
# ifdef JANET_EV
if ( proc - > flags & JANET_PROC_OWNS_STDIN ) janet_stream_close ( proc - > in ) ;
if ( proc - > flags & JANET_PROC_OWNS_STDOUT ) janet_stream_close ( proc - > out ) ;
if ( proc - > flags & JANET_PROC_OWNS_STDERR ) janet_stream_close ( proc - > err ) ;
# else
if ( proc - > flags & JANET_PROC_OWNS_STDIN ) janet_file_close ( proc - > in ) ;
if ( proc - > flags & JANET_PROC_OWNS_STDOUT ) janet_file_close ( proc - > out ) ;
if ( proc - > flags & JANET_PROC_OWNS_STDERR ) janet_file_close ( proc - > err ) ;
# endif
proc - > flags & = ~ ( JANET_PROC_OWNS_STDIN | JANET_PROC_OWNS_STDOUT | JANET_PROC_OWNS_STDERR ) ;
if ( proc - > flags & ( JANET_PROC_WAITED | JANET_PROC_WAITING ) ) {
return janet_wrap_nil ( ) ;
}
# ifdef JANET_EV
os_proc_wait_impl ( proc ) ;
return janet_wrap_nil ( ) ;
# else
return os_proc_wait_impl ( proc ) ;
# endif
}
2020-09-14 01:49:38 +00:00
static void swap_handles ( JanetHandle * handles ) {
JanetHandle temp = handles [ 0 ] ;
handles [ 0 ] = handles [ 1 ] ;
handles [ 1 ] = temp ;
}
static void close_handle ( JanetHandle handle ) {
# ifdef JANET_WINDOWS
CloseHandle ( handle ) ;
# else
close ( handle ) ;
# endif
}
/* Create piped file for os/execute and os/spawn. Need to be careful that we mark
the error flag if we can ' t create pipe and don ' t leak handles . * handle will be cleaned
up by the calling function . If everything goes well , * handle is owned by the calling function ,
2021-01-11 17:10:23 +00:00
( if it is set ) and the returned handle owns the other end of the pipe , which will be closed
2020-09-14 01:49:38 +00:00
on GC or fclose . */
2020-11-15 18:13:30 +00:00
static JanetHandle make_pipes ( JanetHandle * handle , int reverse , int * errflag ) {
2020-09-13 00:48:12 +00:00
JanetHandle handles [ 2 ] ;
2021-01-11 17:10:23 +00:00
# ifdef JANET_EV
/* non-blocking pipes */
2021-01-16 21:11:07 +00:00
if ( janet_make_pipe ( handles , reverse ? 2 : 1 ) ) goto error ;
2021-01-11 17:10:23 +00:00
if ( reverse ) swap_handles ( handles ) ;
# ifdef JANET_WINDOWS
if ( ! SetHandleInformation ( handles [ 0 ] , HANDLE_FLAG_INHERIT , 0 ) ) goto error ;
# endif
* handle = handles [ 1 ] ;
return handles [ 0 ] ;
# else
/* Normal blocking pipes */
2020-09-13 00:48:12 +00:00
# ifdef JANET_WINDOWS
2020-09-14 01:49:38 +00:00
SECURITY_ATTRIBUTES saAttr ;
memset ( & saAttr , 0 , sizeof ( saAttr ) ) ;
saAttr . nLength = sizeof ( saAttr ) ;
saAttr . bInheritHandle = TRUE ;
2020-11-15 18:13:30 +00:00
if ( ! CreatePipe ( handles , handles + 1 , & saAttr , 0 ) ) goto error ;
2020-09-14 01:49:38 +00:00
if ( reverse ) swap_handles ( handles ) ;
/* Don't inherit the side of the pipe owned by this process */
2020-11-15 18:13:30 +00:00
if ( ! SetHandleInformation ( handles [ 0 ] , HANDLE_FLAG_INHERIT , 0 ) ) goto error ;
2020-09-13 00:56:48 +00:00
* handle = handles [ 1 ] ;
2020-11-15 18:13:30 +00:00
return handles [ 0 ] ;
2020-09-13 00:48:12 +00:00
# else
2020-11-15 18:13:30 +00:00
if ( pipe ( handles ) ) goto error ;
2020-09-14 01:49:38 +00:00
if ( reverse ) swap_handles ( handles ) ;
2020-09-13 00:48:12 +00:00
* handle = handles [ 1 ] ;
2020-11-15 18:13:30 +00:00
return handles [ 0 ] ;
2020-09-13 00:48:12 +00:00
# endif
2021-01-11 17:10:23 +00:00
# endif
2020-11-15 18:13:30 +00:00
error :
* errflag = 1 ;
return JANET_HANDLE_NONE ;
2020-09-13 00:48:12 +00:00
}
2020-09-02 01:06:35 +00:00
static const JanetMethod proc_methods [ ] = {
{ " wait " , os_proc_wait } ,
2020-09-02 02:36:49 +00:00
{ " kill " , os_proc_kill } ,
2021-01-16 21:11:07 +00:00
{ " close " , os_proc_close } ,
2021-01-12 05:14:07 +00:00
/* dud methods for janet_proc_next */
{ " in " , NULL } ,
{ " out " , NULL } ,
{ " err " , NULL } ,
2020-09-02 01:06:35 +00:00
{ 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 " ) ) {
2020-12-31 22:19:41 +00:00
* out = ( NULL = = proc - > err ) ? janet_wrap_nil ( ) : janet_wrap_abstract ( proc - > err ) ;
2020-09-02 01:06:35 +00:00
return 1 ;
}
2021-11-19 02:06:29 +00:00
# ifndef JANET_WINDOWS
2021-11-12 10:43:36 +00:00
if ( janet_keyeq ( key , " pid " ) ) {
* out = janet_wrap_number ( proc - > pid ) ;
return 1 ;
}
2021-11-19 02:06:29 +00:00
# endif
2020-09-02 01:06:35 +00:00
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 ) ;
}
2021-01-12 05:14:07 +00:00
static Janet janet_proc_next ( void * p , Janet key ) {
( void ) p ;
return janet_nextmethod ( proc_methods , key ) ;
}
2020-09-02 01:06:35 +00:00
static const JanetAbstractType ProcAT = {
" core/process " ,
2020-09-04 19:54:58 +00:00
janet_proc_gc ,
2020-09-02 01:06:35 +00:00
janet_proc_mark ,
janet_proc_get ,
2021-01-12 05:14:07 +00:00
NULL , /* put */
NULL , /* marshal */
NULL , /* unmarshal */
NULL , /* tostring */
NULL , /* compare */
NULL , /* hash */
janet_proc_next ,
JANET_ATEND_NEXT
2020-09-02 01:06:35 +00:00
} ;
2020-11-15 18:13:30 +00:00
static JanetHandle janet_getjstream ( Janet * argv , int32_t n , void * * orig ) {
# ifdef JANET_EV
2021-01-11 15:05:26 +00:00
JanetStream * stream = janet_checkabstract ( argv [ n ] , & janet_stream_type ) ;
2020-11-15 18:13:30 +00:00
if ( stream ! = NULL ) {
if ( stream - > flags & JANET_STREAM_CLOSED )
janet_panic ( " stream is closed " ) ;
* orig = stream ;
return stream - > handle ;
}
# endif
2021-01-11 15:05:26 +00:00
JanetFile * f = janet_checkabstract ( argv [ n ] , & janet_file_type ) ;
2020-11-15 18:13:30 +00:00
if ( f ! = NULL ) {
if ( f - > flags & JANET_FILE_CLOSED ) {
janet_panic ( " file is closed " ) ;
}
* orig = f ;
# ifdef JANET_WINDOWS
return ( HANDLE ) _get_osfhandle ( _fileno ( f - > file ) ) ;
# else
return fileno ( f - > file ) ;
# endif
}
janet_panicf ( " expected file|stream, got %v " , argv [ n ] ) ;
}
# ifdef JANET_EV
static JanetStream * get_stdio_for_handle ( JanetHandle handle , void * orig , int iswrite ) {
if ( orig = = NULL ) {
return janet_stream ( handle , iswrite ? JANET_STREAM_WRITABLE : JANET_STREAM_READABLE , NULL ) ;
} else if ( janet_abstract_type ( orig ) = = & janet_file_type ) {
JanetFile * jf = ( JanetFile * ) orig ;
uint32_t flags = 0 ;
if ( jf - > flags & JANET_FILE_WRITE ) {
flags | = JANET_STREAM_WRITABLE ;
}
if ( jf - > flags & JANET_FILE_READ ) {
flags | = JANET_STREAM_READABLE ;
}
/* duplicate handle when converting file to stream */
# ifdef JANET_WINDOWS
HANDLE prochandle = GetCurrentProcess ( ) ;
2020-11-16 01:55:58 +00:00
HANDLE newHandle = INVALID_HANDLE_VALUE ;
2020-11-15 18:13:30 +00:00
if ( ! DuplicateHandle ( prochandle , handle , prochandle , & newHandle , 0 , FALSE , DUPLICATE_SAME_ACCESS ) ) {
return NULL ;
}
# else
int newHandle = dup ( handle ) ;
if ( newHandle < 0 ) {
return NULL ;
}
# endif
2020-11-16 01:55:58 +00:00
return janet_stream ( newHandle , flags , NULL ) ;
2020-11-15 18:13:30 +00:00
} else {
return orig ;
}
}
# else
static JanetFile * get_stdio_for_handle ( JanetHandle handle , void * orig , int iswrite ) {
if ( NULL ! = orig ) return ( JanetFile * ) orig ;
# ifdef JANET_WINDOWS
int fd = _open_osfhandle ( ( intptr_t ) handle , iswrite ? _O_WRONLY : _O_RDONLY ) ;
if ( - 1 = = fd ) return NULL ;
FILE * f = _fdopen ( fd , iswrite ? " w " : " r " ) ;
if ( NULL = = f ) {
_close ( fd ) ;
return NULL ;
}
# else
FILE * f = fdopen ( handle , iswrite ? " w " : " r " ) ;
if ( NULL = = f ) return NULL ;
# endif
return janet_makejfile ( f , iswrite ? JANET_FILE_WRITE : JANET_FILE_READ ) ;
}
# endif
2020-11-29 21:36:21 +00:00
static Janet os_execute_impl ( int32_t argc , Janet * argv , int is_spawn ) {
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_SUBPROCESS ) ;
2019-05-31 17:43:38 +00:00
janet_arity ( argc , 1 , 3 ) ;
2019-05-30 22:40:10 +00:00
/* Get flags */
2019-06-12 16:05:01 +00:00
uint64_t flags = 0 ;
if ( argc > 1 ) {
2021-07-24 16:55:04 +00:00
flags = janet_getflags ( argv , 1 , " epxd " ) ;
2019-06-12 16:05:01 +00:00
}
2019-05-30 22:40:10 +00:00
/* Get environment */
2020-09-14 01:49:38 +00:00
int use_environ = ! janet_flag_at ( flags , 0 ) ;
2021-02-14 17:04:59 +00:00
EnvBlock envp = os_execute_env ( argc , argv ) ;
2019-05-30 22:40:10 +00:00
2019-05-31 17:43:38 +00:00
/* Get arguments */
JanetView exargs = janet_getindexed ( argv , 0 ) ;
if ( exargs . len < 1 ) {
janet_panic ( " expected at least 1 command line argument " ) ;
}
2019-05-30 22:40:10 +00:00
2020-09-02 02:47:08 +00:00
/* Optional stdio redirections */
2020-11-15 18:13:30 +00:00
JanetAbstract orig_in = NULL , orig_out = NULL , orig_err = NULL ;
JanetHandle new_in = JANET_HANDLE_NONE , new_out = JANET_HANDLE_NONE , new_err = JANET_HANDLE_NONE ;
2020-09-13 00:48:12 +00:00
JanetHandle pipe_in = JANET_HANDLE_NONE , pipe_out = JANET_HANDLE_NONE , pipe_err = JANET_HANDLE_NONE ;
2020-09-14 01:49:38 +00:00
int pipe_errflag = 0 ; /* Track errors setting up pipes */
2021-07-24 16:55:04 +00:00
int pipe_owner_flags = ( is_spawn & & ( flags & 0x8 ) ) ? JANET_PROC_ALLOW_ZOMBIE : 0 ;
2020-09-02 02:47:08 +00:00
2020-09-02 01:06:35 +00:00
/* 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 " ) ) ;
2022-04-28 01:12:28 +00:00
if ( is_spawn & & janet_keyeq ( maybe_stdin , " pipe " ) ) {
2020-09-14 01:49:38 +00:00
new_in = make_pipes ( & pipe_in , 1 , & pipe_errflag ) ;
2021-01-16 21:11:07 +00:00
pipe_owner_flags | = JANET_PROC_OWNS_STDIN ;
2020-09-13 00:48:12 +00:00
} else if ( ! janet_checktype ( maybe_stdin , JANET_NIL ) ) {
2020-11-15 18:13:30 +00:00
new_in = janet_getjstream ( & maybe_stdin , 0 , & orig_in ) ;
2020-09-13 00:48:12 +00:00
}
2022-04-28 01:12:28 +00:00
if ( is_spawn & & janet_keyeq ( maybe_stdout , " pipe " ) ) {
2020-09-14 01:49:38 +00:00
new_out = make_pipes ( & pipe_out , 0 , & pipe_errflag ) ;
2021-01-16 21:11:07 +00:00
pipe_owner_flags | = JANET_PROC_OWNS_STDOUT ;
2020-09-13 00:48:12 +00:00
} else if ( ! janet_checktype ( maybe_stdout , JANET_NIL ) ) {
2020-11-15 18:13:30 +00:00
new_out = janet_getjstream ( & maybe_stdout , 0 , & orig_out ) ;
2020-09-13 00:48:12 +00:00
}
2022-04-28 01:12:28 +00:00
if ( is_spawn & & janet_keyeq ( maybe_stderr , " pipe " ) ) {
2020-09-14 01:49:38 +00:00
new_err = make_pipes ( & pipe_err , 0 , & pipe_errflag ) ;
2021-01-16 21:11:07 +00:00
pipe_owner_flags | = JANET_PROC_OWNS_STDERR ;
2020-09-13 00:48:12 +00:00
} else if ( ! janet_checktype ( maybe_stderr , JANET_NIL ) ) {
2020-11-15 18:13:30 +00:00
new_err = janet_getjstream ( & maybe_stderr , 0 , & orig_err ) ;
2020-09-13 00:48:12 +00:00
}
2020-09-02 01:06:35 +00:00
}
2020-09-14 01:49:38 +00:00
/* Clean up if any of the pipes have any issues */
if ( pipe_errflag ) {
if ( pipe_in ! = JANET_HANDLE_NONE ) close_handle ( pipe_in ) ;
if ( pipe_out ! = JANET_HANDLE_NONE ) close_handle ( pipe_out ) ;
if ( pipe_err ! = JANET_HANDLE_NONE ) close_handle ( pipe_err ) ;
janet_panic ( " failed to create pipes " ) ;
}
2019-05-30 22:40:10 +00:00
# ifdef JANET_WINDOWS
2020-09-03 00:07:45 +00:00
HANDLE pHandle , tHandle ;
2020-09-14 01:49:38 +00:00
SECURITY_ATTRIBUTES saAttr ;
2020-09-03 00:12:27 +00:00
PROCESS_INFORMATION processInfo ;
STARTUPINFO startupInfo ;
2020-09-14 01:49:38 +00:00
memset ( & saAttr , 0 , sizeof ( saAttr ) ) ;
2020-09-03 00:12:27 +00:00
memset ( & processInfo , 0 , sizeof ( processInfo ) ) ;
memset ( & startupInfo , 0 , sizeof ( startupInfo ) ) ;
startupInfo . cb = sizeof ( startupInfo ) ;
startupInfo . dwFlags | = STARTF_USESTDHANDLES ;
2020-09-14 01:49:38 +00:00
saAttr . nLength = sizeof ( saAttr ) ;
2020-09-02 01:06:35 +00:00
2019-05-31 17:43:38 +00:00
JanetBuffer * buf = os_exec_escape ( exargs ) ;
2019-09-22 17:19:02 +00:00
if ( buf - > count > 8191 ) {
2021-01-16 21:11:07 +00:00
if ( pipe_in ! = JANET_HANDLE_NONE ) CloseHandle ( pipe_in ) ;
if ( pipe_out ! = JANET_HANDLE_NONE ) CloseHandle ( pipe_out ) ;
if ( pipe_err ! = JANET_HANDLE_NONE ) CloseHandle ( pipe_err ) ;
2020-05-19 22:36:58 +00:00
janet_panic ( " command line string too long (max 8191 characters) " ) ;
2019-05-31 17:43:38 +00:00
}
const char * path = ( const char * ) janet_unwrap_string ( exargs . items [ 0 ] ) ;
2020-09-03 00:07:45 +00:00
2020-09-03 00:12:27 +00:00
/* Do IO redirection */
2020-09-13 00:48:12 +00:00
if ( pipe_in ! = JANET_HANDLE_NONE ) {
startupInfo . hStdInput = pipe_in ;
2020-11-15 18:13:30 +00:00
} else if ( new_in ! = JANET_HANDLE_NONE ) {
startupInfo . hStdInput = new_in ;
2020-09-14 01:49:38 +00:00
} else {
startupInfo . hStdInput = ( HANDLE ) _get_osfhandle ( 0 ) ;
2020-09-13 00:48:12 +00:00
}
2020-09-14 01:49:38 +00:00
2020-09-13 00:48:12 +00:00
if ( pipe_out ! = JANET_HANDLE_NONE ) {
2020-09-14 01:49:38 +00:00
startupInfo . hStdOutput = pipe_out ;
2020-11-15 18:13:30 +00:00
} else if ( new_out ! = JANET_HANDLE_NONE ) {
startupInfo . hStdOutput = new_out ;
2020-09-14 01:49:38 +00:00
} else {
startupInfo . hStdOutput = ( HANDLE ) _get_osfhandle ( 1 ) ;
2020-09-13 00:48:12 +00:00
}
if ( pipe_err ! = JANET_HANDLE_NONE ) {
2020-09-14 01:49:38 +00:00
startupInfo . hStdError = pipe_err ;
2020-09-13 00:48:12 +00:00
} else if ( new_err ! = NULL ) {
2020-11-15 18:13:30 +00:00
startupInfo . hStdError = new_err ;
2020-09-14 01:49:38 +00:00
} else {
startupInfo . hStdError = ( HANDLE ) _get_osfhandle ( 2 ) ;
2020-09-13 00:48:12 +00:00
}
2019-05-31 17:43:38 +00:00
2020-09-14 01:49:38 +00:00
int cp_failed = 0 ;
if ( ! CreateProcess ( janet_flag_at ( flags , 1 ) ? NULL : path ,
2020-09-03 00:12:27 +00:00
( char * ) buf - > data , /* Single CLI argument */
2020-09-14 01:49:38 +00:00
& saAttr , /* no proc inheritance */
& saAttr , /* no thread inheritance */
2020-09-03 00:12:27 +00:00
TRUE , /* handle inheritance */
0 , /* flags */
2020-09-14 01:49:38 +00:00
use_environ ? NULL : envp , /* pass in environment */
2020-09-03 00:12:27 +00:00
NULL , /* use parents starting directory */
& startupInfo ,
2020-09-14 01:49:38 +00:00
& processInfo ) ) {
cp_failed = 1 ;
2018-07-07 01:50:59 +00:00
}
2020-09-03 00:12:27 +00:00
2020-09-13 00:48:12 +00:00
if ( pipe_in ! = JANET_HANDLE_NONE ) CloseHandle ( pipe_in ) ;
if ( pipe_out ! = JANET_HANDLE_NONE ) CloseHandle ( pipe_out ) ;
if ( pipe_err ! = JANET_HANDLE_NONE ) CloseHandle ( pipe_err ) ;
2020-09-14 01:49:38 +00:00
os_execute_cleanup ( envp , NULL ) ;
if ( cp_failed ) {
janet_panic ( " failed to create process " ) ;
}
2020-09-03 00:12:27 +00:00
pHandle = processInfo . hProcess ;
tHandle = processInfo . hThread ;
2019-05-31 19:02:44 +00:00
2019-05-30 22:40:10 +00:00
# else
2023-01-21 16:37:34 +00:00
/* Result */
int status = 0 ;
2020-01-03 04:02:57 +00:00
const char * * child_argv = janet_smalloc ( sizeof ( char * ) * ( ( size_t ) exargs . len + 1 ) ) ;
2019-06-02 03:38:10 +00:00
for ( int32_t i = 0 ; i < exargs . len ; i + + )
2019-05-31 17:43:38 +00:00
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 ;
2019-05-30 22:40:10 +00:00
/* Use posix_spawn to spawn new process */
2020-05-05 04:03:13 +00:00
if ( use_environ ) {
janet_lock_environ ( ) ;
}
2020-09-02 01:06:35 +00:00
/* Posix spawn setup */
posix_spawn_file_actions_t actions ;
posix_spawn_file_actions_init ( & actions ) ;
2020-09-13 00:48:12 +00:00
if ( pipe_in ! = JANET_HANDLE_NONE ) {
posix_spawn_file_actions_adddup2 ( & actions , pipe_in , 0 ) ;
2021-11-27 00:44:11 +00:00
posix_spawn_file_actions_addclose ( & actions , pipe_in ) ;
2020-11-15 18:13:30 +00:00
} else if ( new_in ! = JANET_HANDLE_NONE ) {
posix_spawn_file_actions_adddup2 ( & actions , new_in , 0 ) ;
2021-11-27 00:44:11 +00:00
posix_spawn_file_actions_addclose ( & actions , new_in ) ;
2020-09-02 01:06:35 +00:00
}
2020-09-13 00:48:12 +00:00
if ( pipe_out ! = JANET_HANDLE_NONE ) {
posix_spawn_file_actions_adddup2 ( & actions , pipe_out , 1 ) ;
2021-11-27 00:44:11 +00:00
posix_spawn_file_actions_addclose ( & actions , pipe_out ) ;
2020-11-15 18:13:30 +00:00
} else if ( new_out ! = JANET_HANDLE_NONE ) {
posix_spawn_file_actions_adddup2 ( & actions , new_out , 1 ) ;
2021-11-27 00:44:11 +00:00
posix_spawn_file_actions_addclose ( & actions , new_out ) ;
2020-09-02 01:06:35 +00:00
}
2020-09-13 00:48:12 +00:00
if ( pipe_err ! = JANET_HANDLE_NONE ) {
posix_spawn_file_actions_adddup2 ( & actions , pipe_err , 2 ) ;
2021-11-27 00:44:11 +00:00
posix_spawn_file_actions_addclose ( & actions , pipe_err ) ;
2020-11-15 18:13:30 +00:00
} else if ( new_err ! = JANET_HANDLE_NONE ) {
posix_spawn_file_actions_adddup2 ( & actions , new_err , 2 ) ;
2021-11-27 00:44:11 +00:00
posix_spawn_file_actions_addclose ( & actions , new_err ) ;
2020-09-02 01:06:35 +00:00
}
2019-05-30 22:40:10 +00:00
pid_t pid ;
2019-06-12 16:05:01 +00:00
if ( janet_flag_at ( flags , 1 ) ) {
2019-05-30 22:40:10 +00:00
status = posix_spawnp ( & pid ,
2020-09-02 01:06:35 +00:00
child_argv [ 0 ] , & actions , NULL , cargv ,
2020-05-05 04:03:13 +00:00
use_environ ? environ : envp ) ;
2019-05-30 22:40:10 +00:00
} else {
status = posix_spawn ( & pid ,
2020-09-02 01:06:35 +00:00
child_argv [ 0 ] , & actions , NULL , cargv ,
2020-05-05 04:03:13 +00:00
use_environ ? environ : envp ) ;
}
2020-09-02 01:06:35 +00:00
posix_spawn_file_actions_destroy ( & actions ) ;
2020-09-13 00:48:12 +00:00
if ( pipe_in ! = JANET_HANDLE_NONE ) close ( pipe_in ) ;
if ( pipe_out ! = JANET_HANDLE_NONE ) close ( pipe_out ) ;
if ( pipe_err ! = JANET_HANDLE_NONE ) close ( pipe_err ) ;
2020-05-05 04:03:13 +00:00
if ( use_environ ) {
janet_unlock_environ ( ) ;
2019-05-30 22:40:10 +00:00
}
2021-01-16 13:18:07 +00:00
os_execute_cleanup ( envp , child_argv ) ;
2019-05-30 22:40:10 +00:00
if ( status ) {
2019-12-15 02:39:14 +00:00
janet_panicf ( " %p: %s " , argv [ 0 ] , strerror ( errno ) ) ;
2019-05-30 22:40:10 +00:00
}
2018-07-07 01:50:59 +00:00
# endif
2020-11-29 21:36:21 +00:00
JanetProc * proc = janet_abstract ( & ProcAT , sizeof ( JanetProc ) ) ;
proc - > return_code = - 1 ;
2020-09-03 00:07:45 +00:00
# ifdef JANET_WINDOWS
2020-11-29 21:36:21 +00:00
proc - > pHandle = pHandle ;
proc - > tHandle = tHandle ;
2020-09-03 00:07:45 +00:00
# else
2020-11-29 21:36:21 +00:00
proc - > pid = pid ;
# endif
2020-12-31 22:12:42 +00:00
proc - > in = NULL ;
proc - > out = NULL ;
proc - > err = NULL ;
2021-01-16 21:11:07 +00:00
proc - > flags = pipe_owner_flags ;
2020-11-29 21:36:21 +00:00
if ( janet_flag_at ( flags , 2 ) ) {
proc - > flags | = JANET_PROC_ERROR_NONZERO ;
}
if ( is_spawn ) {
2021-01-16 13:18:07 +00:00
/* Only set up pointers to stdin, stdout, and stderr if os/spawn. */
if ( new_in ! = JANET_HANDLE_NONE ) {
proc - > in = get_stdio_for_handle ( new_in , orig_in , 1 ) ;
if ( NULL = = proc - > in ) janet_panic ( " failed to construct proc " ) ;
}
if ( new_out ! = JANET_HANDLE_NONE ) {
proc - > out = get_stdio_for_handle ( new_out , orig_out , 0 ) ;
if ( NULL = = proc - > out ) janet_panic ( " failed to construct proc " ) ;
}
if ( new_err ! = JANET_HANDLE_NONE ) {
proc - > err = get_stdio_for_handle ( new_err , orig_err , 0 ) ;
if ( NULL = = proc - > err ) janet_panic ( " failed to construct proc " ) ;
}
2020-09-02 01:06:35 +00:00
return janet_wrap_abstract ( proc ) ;
} else {
2020-11-29 21:36:21 +00:00
# ifdef JANET_EV
os_proc_wait_impl ( proc ) ;
# else
return os_proc_wait_impl ( proc ) ;
# endif
2020-08-29 15:27:32 +00:00
}
2019-05-30 22:40:10 +00:00
}
2018-07-07 01:50:59 +00:00
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_execute ,
" (os/execute args &opt flags env) " ,
" Execute a program on the system and pass it string arguments. `flags` "
" is a keyword that modifies how the program will execute. \n "
" * :e - enables passing an environment to the program. Without :e, the "
" current environment is inherited. \n "
" * :p - allows searching the current PATH for the binary to execute. "
" Without this flag, binaries must use absolute paths. \n "
" * :x - raise error if exit code is non-zero. \n "
" * :d - Don't try and terminate the process on garbage collection (allow spawning zombies). \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. " ) {
2020-09-04 19:54:58 +00:00
return os_execute_impl ( argc , argv , 0 ) ;
2020-09-04 13:09:05 +00:00
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_spawn ,
" (os/spawn args &opt flags env) " ,
2022-04-18 00:42:32 +00:00
" Execute a program on the system and return a handle to the process. Otherwise, takes the "
" same arguments as `os/execute`. Does not wait for the process. "
2022-04-28 01:12:28 +00:00
" For each of the :in, :out, and :err keys to the `env` argument, one "
2022-08-02 19:58:32 +00:00
" can also pass in the keyword `:pipe` "
" to get streams for standard IO of the subprocess that can be read from and written to. "
2022-04-28 01:12:28 +00:00
" The returned value `proc` has the fields :in, :out, :err, :return-code, and "
" the additional field :pid on unix-like platforms. Use `(os/proc-wait proc)` to rejoin the "
2022-04-30 03:57:37 +00:00
" subprocess or `(os/proc-kill proc)`. " ) {
2020-09-04 19:54:58 +00:00
return os_execute_impl ( argc , argv , 1 ) ;
2020-09-04 13:09:05 +00:00
}
2021-01-18 22:44:22 +00:00
# ifdef JANET_EV
/* Runs in a separate thread */
static JanetEVGenericMessage os_shell_subr ( JanetEVGenericMessage args ) {
int stat = system ( ( const char * ) args . argp ) ;
2021-03-23 10:00:48 +00:00
janet_free ( args . argp ) ;
2021-01-18 22:44:22 +00:00
if ( args . argi ) {
args . tag = JANET_EV_TCTAG_INTEGER ;
} else {
args . tag = JANET_EV_TCTAG_BOOLEAN ;
}
args . argi = stat ;
return args ;
}
# endif
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_shell ,
" (os/shell str) " ,
" Pass a command string str directly to the system shell. " ) {
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_SUBPROCESS ) ;
2019-01-06 01:09:03 +00:00
janet_arity ( argc , 0 , 1 ) ;
const char * cmd = argc
2019-03-29 03:22:58 +00:00
? janet_getcstring ( argv , 0 )
2019-02-20 01:51:34 +00:00
: NULL ;
2021-01-18 22:44:22 +00:00
# ifdef JANET_EV
janet_ev_threaded_await ( os_shell_subr , 0 , argc , cmd ? strdup ( cmd ) : NULL ) ;
# else
2018-03-29 00:50:20 +00:00
int stat = system ( cmd ) ;
2019-01-06 01:09:03 +00:00
return argc
2019-02-20 01:51:34 +00:00
? janet_wrap_integer ( stat )
: janet_wrap_boolean ( stat ) ;
2021-01-18 22:44:22 +00:00
# endif
2018-03-29 00:50:20 +00:00
}
2020-05-09 17:00:01 +00:00
# endif /* JANET_NO_PROCESSES */
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_environ ,
" (os/environ) " ,
2022-04-18 00:42:32 +00:00
" Get a copy of the OS environment table. " ) {
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_ENV ) ;
2019-11-28 05:31:01 +00:00
( void ) argv ;
janet_fixarity ( argc , 0 ) ;
2019-12-02 15:06:58 +00:00
int32_t nenv = 0 ;
2020-01-07 01:30:44 +00:00
janet_lock_environ ( ) ;
2019-11-28 05:31:01 +00:00
char * * env = environ ;
while ( * env + + )
nenv + = 1 ;
JanetTable * t = janet_table ( nenv ) ;
2019-12-02 15:06:58 +00:00
for ( int32_t i = 0 ; i < nenv ; i + + ) {
2019-11-28 05:31:01 +00:00
char * e = environ [ i ] ;
char * eq = strchr ( e , ' = ' ) ;
2020-01-07 01:30:44 +00:00
if ( ! eq ) {
janet_unlock_environ ( ) ;
janet_panic ( " no '=' in environ " ) ;
}
2019-11-28 05:31:01 +00:00
char * v = eq + 1 ;
2019-12-02 15:06:58 +00:00
int32_t full_len = ( int32_t ) strlen ( e ) ;
int32_t val_len = ( int32_t ) strlen ( v ) ;
2019-11-28 05:31:01 +00:00
janet_table_put (
t ,
2019-11-29 04:26:11 +00:00
janet_stringv ( ( const uint8_t * ) e , full_len - val_len - 1 ) ,
janet_stringv ( ( const uint8_t * ) v , val_len )
2019-11-28 05:31:01 +00:00
) ;
}
2020-01-07 01:30:44 +00:00
janet_unlock_environ ( ) ;
2019-11-28 05:31:01 +00:00
return janet_wrap_table ( t ) ;
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_getenv ,
" (os/getenv variable &opt dflt) " ,
" Get the string value of an environment variable. " ) {
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_ENV ) ;
2020-01-06 22:20:55 +00:00
janet_arity ( argc , 1 , 2 ) ;
2019-03-29 03:22:58 +00:00
const char * cstr = janet_getcstring ( argv , 0 ) ;
2018-03-29 00:50:20 +00:00
const char * res = getenv ( cstr ) ;
2020-01-07 01:30:44 +00:00
janet_lock_environ ( ) ;
Janet ret = res
? janet_cstringv ( res )
: argc = = 2
? argv [ 1 ]
: janet_wrap_nil ( ) ;
janet_unlock_environ ( ) ;
return ret ;
2018-03-29 00:50:20 +00:00
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_setenv ,
" (os/setenv variable value) " ,
" Set an environment variable. " ) {
2018-09-06 02:18:42 +00:00
# ifdef JANET_WINDOWS
2018-06-08 19:58:23 +00:00
# define SETENV(K,V) _putenv_s(K, V)
# define UNSETENV(K) _putenv_s(K, "")
2018-03-29 00:50:20 +00:00
# else
2018-06-08 19:58:23 +00:00
# define SETENV(K,V) setenv(K, V, 1)
# define UNSETENV(K) unsetenv(K)
# endif
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_ENV ) ;
2019-01-06 01:09:03 +00:00
janet_arity ( argc , 1 , 2 ) ;
2019-03-29 03:22:58 +00:00
const char * ks = janet_getcstring ( argv , 0 ) ;
2020-01-07 01:30:44 +00:00
const char * vs = janet_optcstring ( argv , argc , 1 , NULL ) ;
janet_lock_environ ( ) ;
if ( NULL = = vs ) {
2018-06-08 19:58:23 +00:00
UNSETENV ( ks ) ;
2018-03-29 00:50:20 +00:00
} else {
2020-01-07 01:30:44 +00:00
SETENV ( ks , vs ) ;
2018-05-13 00:31:28 +00:00
}
2020-01-07 01:30:44 +00:00
janet_unlock_environ ( ) ;
2019-01-06 01:09:03 +00:00
return janet_wrap_nil ( ) ;
2018-05-13 00:31:28 +00:00
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_time ,
" (os/time) " ,
2021-07-31 16:02:48 +00:00
" Get the current time expressed as the number of whole seconds since "
2021-07-26 07:48:04 +00:00
" January 1, 1970, the Unix epoch. Returns a real number. " ) {
2019-01-06 01:45:24 +00:00
janet_fixarity ( argc , 0 ) ;
2019-01-06 01:09:03 +00:00
( void ) argv ;
2018-09-23 01:46:50 +00:00
double dtime = ( double ) ( time ( NULL ) ) ;
2019-01-06 01:09:03 +00:00
return janet_wrap_number ( dtime ) ;
2018-09-23 01:46:50 +00:00
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_clock ,
" (os/clock) " ,
2021-07-31 16:02:48 +00:00
" Return the number of whole + fractional seconds since some fixed point in time. The clock "
2022-04-18 00:42:32 +00:00
" is guaranteed to be non-decreasing in real time. " ) {
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_HRTIME ) ;
2019-01-06 01:45:24 +00:00
janet_fixarity ( argc , 0 ) ;
2019-01-06 01:09:03 +00:00
( void ) argv ;
2018-07-08 23:27:11 +00:00
struct timespec tv ;
2020-07-03 14:54:58 +00:00
if ( janet_gettime ( & tv ) ) janet_panic ( " could not get time " ) ;
2018-07-08 23:27:11 +00:00
double dtime = tv . tv_sec + ( tv . tv_nsec / 1E9 ) ;
2019-01-06 01:09:03 +00:00
return janet_wrap_number ( dtime ) ;
2018-05-13 00:31:28 +00:00
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_sleep ,
" (os/sleep n) " ,
2022-04-18 00:42:32 +00:00
" Suspend the program for `n` seconds. `n` can be a real number. Returns "
2021-07-26 07:48:04 +00:00
" nil. " ) {
2019-01-06 01:45:24 +00:00
janet_fixarity ( argc , 1 ) ;
2019-01-06 01:09:03 +00:00
double delay = janet_getnumber ( argv , 0 ) ;
if ( delay < 0 ) janet_panic ( " invalid argument to sleep " ) ;
2018-09-06 02:18:42 +00:00
# ifdef JANET_WINDOWS
2019-02-20 01:51:34 +00:00
Sleep ( ( DWORD ) ( delay * 1000 ) ) ;
2018-05-13 00:31:28 +00:00
# else
2019-12-03 22:29:11 +00:00
int rc ;
2018-07-07 01:50:59 +00:00
struct timespec ts ;
ts . tv_sec = ( time_t ) delay ;
ts . tv_nsec = ( delay < = UINT32_MAX )
2019-02-20 01:51:34 +00:00
? ( long ) ( ( delay - ( ( uint32_t ) delay ) ) * 1000000000 )
: 0 ;
2019-12-03 22:29:11 +00:00
RETRY_EINTR ( rc , nanosleep ( & ts , & ts ) ) ;
2018-05-13 00:31:28 +00:00
# endif
2019-01-06 01:09:03 +00:00
return janet_wrap_nil ( ) ;
2018-03-29 00:50:20 +00:00
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_cwd ,
" (os/cwd) " ,
" Returns the current working directory. " ) {
2019-01-06 01:45:24 +00:00
janet_fixarity ( argc , 0 ) ;
2019-01-06 01:09:03 +00:00
( void ) argv ;
2018-05-19 05:09:56 +00:00
char buf [ FILENAME_MAX ] ;
char * ptr ;
2018-09-06 02:18:42 +00:00
# ifdef JANET_WINDOWS
2018-05-19 05:09:56 +00:00
ptr = _getcwd ( buf , FILENAME_MAX ) ;
# else
ptr = getcwd ( buf , FILENAME_MAX ) ;
# endif
2019-01-06 01:09:03 +00:00
if ( NULL = = ptr ) janet_panic ( " could not get current directory " ) ;
return janet_cstringv ( ptr ) ;
2018-05-19 05:09:56 +00:00
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_cryptorand ,
" (os/cryptorand n &opt buf) " ,
2022-04-18 00:42:32 +00:00
" Get or append `n` bytes of good quality random data provided by the OS. Returns a new buffer or `buf`. " ) {
2019-12-03 22:29:11 +00:00
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 ) ;
2020-08-04 00:11:05 +00:00
if ( janet_cryptorand ( buffer - > data + offset , n ) ! = 0 )
janet_panic ( " unable to get sufficient random data " ) ;
2019-12-03 22:29:11 +00:00
return janet_wrap_buffer ( buffer ) ;
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_date ,
" (os/date &opt time local) " ,
" Returns the given time as a date struct, or the current time if `time` is not 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 "
" * :seconds - number of seconds [0-61] \n \n "
" * :minutes - number of minutes [0-59] \n \n "
" * :hours - number of hours [0-23] \n \n "
" * :month-day - day of month [0-30] \n \n "
" * :month - month of year [0, 11] \n \n "
" * :year - years since year 0 (e.g. 2019) \n \n "
" * :week-day - day of the week [0-6] \n \n "
" * :year-day - day of the year [0-365] \n \n "
" * :dst - if Day Light Savings is in effect " ) {
2019-11-09 22:57:21 +00:00
janet_arity ( argc , 0 , 2 ) ;
2019-01-20 19:34:33 +00:00
( void ) argv ;
time_t t ;
2019-10-11 03:59:43 +00:00
struct tm t_infos ;
2019-11-09 22:57:21 +00:00
struct tm * t_info = NULL ;
2022-10-10 20:24:28 +00:00
if ( argc & & ! janet_checktype ( argv [ 0 ] , JANET_NIL ) ) {
2019-11-09 22:57:21 +00:00
int64_t integer = janet_getinteger64 ( argv , 0 ) ;
t = ( time_t ) integer ;
2019-01-20 19:34:33 +00:00
} else {
time ( & t ) ;
}
2020-03-18 18:49:23 +00:00
if ( argc > = 2 & & janet_truthy ( argv [ 1 ] ) ) {
2019-11-09 22:57:21 +00:00
/* local time */
# ifdef JANET_WINDOWS
2022-06-19 13:49:25 +00:00
_tzset ( ) ;
2019-11-09 22:57:21 +00:00
localtime_s ( & t_infos , & t ) ;
t_info = & t_infos ;
# else
tzset ( ) ;
t_info = localtime_r ( & t , & t_infos ) ;
# endif
} else {
/* utc time */
2019-10-11 03:59:43 +00:00
# ifdef JANET_WINDOWS
2019-11-09 22:57:21 +00:00
gmtime_s ( & t_infos , & t ) ;
t_info = & t_infos ;
2019-10-11 03:59:43 +00:00
# else
2019-11-09 22:57:21 +00:00
t_info = gmtime_r ( & t , & t_infos ) ;
2019-10-11 03:59:43 +00:00
# endif
2019-11-09 22:57:21 +00:00
}
2019-01-20 19:34:33 +00:00
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 ) ) ;
2019-01-20 21:49:39 +00:00
janet_struct_put ( st , janet_ckeywordv ( " month-day " ) , janet_wrap_number ( t_info - > tm_mday - 1 ) ) ;
2019-01-20 19:34:33 +00:00
janet_struct_put ( st , janet_ckeywordv ( " month " ) , janet_wrap_number ( t_info - > tm_mon ) ) ;
2019-01-20 21:49:39 +00:00
janet_struct_put ( st , janet_ckeywordv ( " year " ) , janet_wrap_number ( t_info - > tm_year + 1900 ) ) ;
2019-01-20 19:34:33 +00:00
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 ) ) ;
}
2020-03-19 02:23:35 +00:00
static int entry_getdst ( Janet env_entry ) {
2020-03-19 03:22:03 +00:00
Janet v ;
2020-03-19 02:23:35 +00:00
if ( janet_checktype ( env_entry , JANET_TABLE ) ) {
JanetTable * entry = janet_unwrap_table ( env_entry ) ;
2020-03-19 03:22:03 +00:00
v = janet_table_get ( entry , janet_ckeywordv ( " dst " ) ) ;
2020-03-19 02:23:35 +00:00
} else if ( janet_checktype ( env_entry , JANET_STRUCT ) ) {
const JanetKV * entry = janet_unwrap_struct ( env_entry ) ;
2020-03-19 03:22:03 +00:00
v = janet_struct_get ( entry , janet_ckeywordv ( " dst " ) ) ;
2020-03-19 02:23:35 +00:00
} else {
2020-03-19 03:22:03 +00:00
v = janet_wrap_nil ( ) ;
}
if ( janet_checktype ( v , JANET_NIL ) ) {
return - 1 ;
} else {
return janet_truthy ( v ) ;
2020-03-19 02:23:35 +00:00
}
}
2020-03-19 02:15:50 +00:00
# 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 ) {
2020-03-18 18:52:25 +00:00
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 ;
}
2020-03-19 02:15:50 +00:00
# ifdef JANET_WINDOWS
if ( ! janet_checkint ( i ) ) {
janet_panicf ( " bad slot #%s, expected 32 bit signed integer, got %v " ,
field , i ) ;
}
# else
2020-03-18 18:52:25 +00:00
if ( ! janet_checkint64 ( i ) ) {
2020-03-19 02:15:50 +00:00
janet_panicf ( " bad slot #%s, expected 64 bit signed integer, got %v " ,
field , i ) ;
2020-03-18 18:52:25 +00:00
}
2020-03-19 02:15:50 +00:00
# endif
2020-03-18 18:52:25 +00:00
2020-03-19 02:15:50 +00:00
return ( timeint_t ) janet_unwrap_number ( i ) ;
2020-03-18 18:52:25 +00:00
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_mktime ,
" (os/mktime date-struct &opt local) " ,
" Get the broken down date-struct time expressed as the number "
2022-04-18 00:42:32 +00:00
" of seconds since January 1, 1970, the Unix epoch. "
2021-07-26 07:48:04 +00:00
" Returns a real number. "
2022-04-18 00:42:32 +00:00
" Date is given in UTC unless `local` is truthy, in which case the "
2021-07-26 07:48:04 +00:00
" date is computed for the local timezone. \n \n "
" Inverse function to os/date. " ) {
2020-03-18 18:52:25 +00:00
janet_arity ( argc , 1 , 2 ) ;
time_t t ;
2020-04-04 18:34:16 +00:00
struct tm t_info ;
/* Use memset instead of = {0} to silence paranoid warning in macos */
memset ( & t_info , 0 , sizeof ( t_info ) ) ;
2020-03-18 18:52:25 +00:00
if ( ! janet_checktype ( argv [ 0 ] , JANET_TABLE ) & &
2020-03-19 02:15:50 +00:00
! janet_checktype ( argv [ 0 ] , JANET_STRUCT ) )
janet_panic_type ( argv [ 0 ] , 0 , JANET_TFLAG_DICTIONARY ) ;
2020-03-18 18:52:25 +00:00
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 ;
2020-03-19 02:23:35 +00:00
t_info . tm_isdst = entry_getdst ( argv [ 0 ] ) ;
2020-03-18 18:52:25 +00:00
if ( argc > = 2 & & janet_truthy ( argv [ 1 ] ) ) {
/* local time */
t = mktime ( & t_info ) ;
} else {
/* utc time */
2020-05-09 17:00:01 +00:00
# ifdef JANET_NO_UTC_MKTIME
janet_panic ( " os/mktime UTC not supported on this platform " ) ;
2020-03-19 02:15:50 +00:00
return janet_wrap_nil ( ) ;
2020-03-18 18:52:25 +00:00
# else
t = timegm ( & t_info ) ;
# endif
}
2020-03-19 02:15:50 +00:00
if ( t = = ( time_t ) - 1 ) {
2020-03-18 18:52:25 +00:00
janet_panicf ( " %s " , strerror ( errno ) ) ;
}
return janet_wrap_number ( ( double ) t ) ;
}
2020-05-09 17:00:01 +00:00
# ifdef JANET_NO_SYMLINKS
# define j_symlink link
# else
# define j_symlink symlink
# endif
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_link ,
" (os/link oldpath newpath &opt symlink) " ,
" 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. " ) {
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_FS_WRITE ) ;
2019-03-29 03:22:58 +00:00
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 ) ;
2020-05-09 17:00:01 +00:00
int res = ( ( argc = = 3 & & janet_truthy ( argv [ 2 ] ) ) ? j_symlink : link ) ( oldpath , newpath ) ;
2019-12-12 09:20:20 +00:00
if ( - 1 = = res ) janet_panicf ( " %s: %s -> %s " , strerror ( errno ) , oldpath , newpath ) ;
2020-03-25 00:45:57 +00:00
return janet_wrap_nil ( ) ;
# endif
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_symlink ,
" (os/symlink oldpath newpath) " ,
2022-04-18 00:42:32 +00:00
" Create a symlink from oldpath to newpath, returning nil. Same as `(os/link oldpath newpath true)`. " ) {
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_FS_WRITE ) ;
2020-03-25 00:45:57 +00:00
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 ) ;
2020-05-09 17:00:01 +00:00
int res = j_symlink ( oldpath , newpath ) ;
2020-03-25 00:45:57 +00:00
if ( - 1 = = res ) janet_panicf ( " %s: %s -> %s " , strerror ( errno ) , oldpath , newpath ) ;
return janet_wrap_nil ( ) ;
2019-03-29 03:22:58 +00:00
# endif
}
2020-05-09 17:00:01 +00:00
# undef j_symlink
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_mkdir ,
" (os/mkdir path) " ,
" 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. " ) {
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_FS_WRITE ) ;
2019-03-29 03:22:58 +00:00
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
2020-03-25 00:45:57 +00:00
if ( res = = 0 ) return janet_wrap_true ( ) ;
if ( errno = = EEXIST ) return janet_wrap_false ( ) ;
janet_panicf ( " %s: %s " , strerror ( errno ) , path ) ;
2019-03-29 03:22:58 +00:00
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_rmdir ,
" (os/rmdir path) " ,
" Delete a directory. The directory must be empty to succeed. " ) {
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_FS_WRITE ) ;
2019-03-30 19:39:24 +00:00
janet_fixarity ( argc , 1 ) ;
const char * path = janet_getcstring ( argv , 0 ) ;
# ifdef JANET_WINDOWS
int res = _rmdir ( path ) ;
# else
int res = rmdir ( path ) ;
# endif
2019-12-12 09:20:20 +00:00
if ( - 1 = = res ) janet_panicf ( " %s: %s " , strerror ( errno ) , path ) ;
2019-03-30 19:39:24 +00:00
return janet_wrap_nil ( ) ;
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_cd ,
" (os/cd path) " ,
" Change current directory to path. Returns nil on success, errors on failure. " ) {
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_FS_READ ) ;
2019-03-29 03:22:58 +00:00
janet_fixarity ( argc , 1 ) ;
const char * path = janet_getcstring ( argv , 0 ) ;
2019-03-29 03:34:24 +00:00
# ifdef JANET_WINDOWS
int res = _chdir ( path ) ;
# else
2019-03-29 03:22:58 +00:00
int res = chdir ( path ) ;
2019-03-29 03:34:24 +00:00
# endif
2019-12-12 09:20:20 +00:00
if ( - 1 = = res ) janet_panicf ( " %s: %s " , strerror ( errno ) , path ) ;
2019-03-30 19:39:24 +00:00
return janet_wrap_nil ( ) ;
2019-03-29 03:22:58 +00:00
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_touch ,
" (os/touch path &opt actime modtime) " ,
" Update the access time and modification times for a file. By default, sets "
" times to the current time. " ) {
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_FS_WRITE ) ;
2019-03-29 03:22:58 +00:00
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 ) ;
2019-03-30 19:39:24 +00:00
if ( - 1 = = res ) janet_panic ( strerror ( errno ) ) ;
return janet_wrap_nil ( ) ;
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_remove ,
" (os/rm path) " ,
" Delete a file. Returns nil. " ) {
2019-03-30 19:39:24 +00:00
janet_fixarity ( argc , 1 ) ;
const char * path = janet_getcstring ( argv , 0 ) ;
int status = remove ( path ) ;
2019-12-12 09:20:20 +00:00
if ( - 1 = = status ) janet_panicf ( " %s: %s " , strerror ( errno ) , path ) ;
2019-03-30 19:39:24 +00:00
return janet_wrap_nil ( ) ;
2019-03-29 03:22:58 +00:00
}
2020-05-11 04:07:54 +00:00
# ifndef JANET_NO_SYMLINKS
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_readlink ,
" (os/readlink path) " ,
" Read the contents of a symbolic link. Does not work on Windows. \n " ) {
2020-03-09 16:34:30 +00:00
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
}
2020-05-11 04:07:54 +00:00
# endif
2020-03-09 16:34:30 +00:00
2019-03-30 16:06:14 +00:00
# ifdef JANET_WINDOWS
2020-04-03 20:02:12 +00:00
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 ;
2019-03-30 16:06:14 +00:00
}
2020-04-03 20:02:12 +00:00
static unsigned short janet_perm_from_unix ( int32_t x ) {
2020-03-14 16:56:42 +00:00
unsigned short m = 0 ;
2020-04-03 20:02:12 +00:00
if ( x & 111 ) m | = S_IEXEC ;
if ( x & 222 ) m | = S_IWRITE ;
if ( x & 444 ) m | = S_IREAD ;
2020-03-14 16:56:42 +00:00
return m ;
}
2019-03-30 16:06:14 +00:00
static const uint8_t * janet_decode_mode ( unsigned short m ) {
const char * str = " other " ;
2019-03-30 17:46:52 +00:00
if ( m & _S_IFREG ) str = " file " ;
else if ( m & _S_IFDIR ) str = " directory " ;
else if ( m & _S_IFCHR ) str = " character " ;
2019-03-30 16:06:14 +00:00
return janet_ckeyword ( str ) ;
}
2020-04-03 20:02:12 +00:00
static int32_t janet_decode_permissions ( jmode_t mode ) {
return ( int32_t ) ( mode & ( S_IEXEC | S_IWRITE | S_IREAD ) ) ;
}
2019-03-30 16:06:14 +00:00
# else
2020-04-03 20:02:12 +00:00
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 ;
2020-03-14 16:56:42 +00:00
}
2019-03-30 16:06:14 +00:00
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 ) ;
}
2020-04-03 20:02:12 +00:00
static int32_t janet_decode_permissions ( jmode_t mode ) {
return ( int32_t ) ( mode & 0777 ) ;
}
2020-03-19 02:37:55 +00:00
# endif
2020-04-03 20:02:12 +00:00
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 ) ) ;
}
2019-04-01 15:11:15 +00:00
/* Getters */
2020-03-19 02:37:55 +00:00
static Janet os_stat_dev ( jstat_t * st ) {
2019-04-01 15:21:45 +00:00
return janet_wrap_number ( st - > st_dev ) ;
}
2020-03-19 02:37:55 +00:00
static Janet os_stat_inode ( jstat_t * st ) {
2019-04-01 15:21:45 +00:00
return janet_wrap_number ( st - > st_ino ) ;
}
2020-03-19 02:37:55 +00:00
static Janet os_stat_mode ( jstat_t * st ) {
2019-04-01 15:21:45 +00:00
return janet_wrap_keyword ( janet_decode_mode ( st - > st_mode ) ) ;
}
2020-04-03 23:28:43 +00:00
static Janet os_stat_int_permissions ( jstat_t * st ) {
return janet_wrap_integer ( janet_perm_to_unix ( janet_decode_permissions ( st - > st_mode ) ) ) ;
}
2020-03-19 02:37:55 +00:00
static Janet os_stat_permissions ( jstat_t * st ) {
2020-04-03 20:02:12 +00:00
return os_make_permstring ( janet_perm_to_unix ( janet_decode_permissions ( st - > st_mode ) ) ) ;
2019-04-01 15:21:45 +00:00
}
2020-03-19 02:37:55 +00:00
static Janet os_stat_uid ( jstat_t * st ) {
2019-04-01 15:21:45 +00:00
return janet_wrap_number ( st - > st_uid ) ;
}
2020-03-19 02:37:55 +00:00
static Janet os_stat_gid ( jstat_t * st ) {
2019-04-01 15:21:45 +00:00
return janet_wrap_number ( st - > st_gid ) ;
}
2020-03-19 02:37:55 +00:00
static Janet os_stat_nlink ( jstat_t * st ) {
2019-04-01 15:21:45 +00:00
return janet_wrap_number ( st - > st_nlink ) ;
}
2020-03-19 02:37:55 +00:00
static Janet os_stat_rdev ( jstat_t * st ) {
2019-04-01 15:21:45 +00:00
return janet_wrap_number ( st - > st_rdev ) ;
}
2020-03-19 02:37:55 +00:00
static Janet os_stat_size ( jstat_t * st ) {
2019-04-01 15:21:45 +00:00
return janet_wrap_number ( st - > st_size ) ;
}
2020-03-19 02:37:55 +00:00
static Janet os_stat_accessed ( jstat_t * st ) {
2019-04-01 15:21:45 +00:00
return janet_wrap_number ( ( double ) st - > st_atime ) ;
}
2020-03-19 02:37:55 +00:00
static Janet os_stat_modified ( jstat_t * st ) {
2019-04-01 15:21:45 +00:00
return janet_wrap_number ( ( double ) st - > st_mtime ) ;
}
2020-03-19 02:37:55 +00:00
static Janet os_stat_changed ( jstat_t * st ) {
2019-04-01 15:21:45 +00:00
return janet_wrap_number ( ( double ) st - > st_ctime ) ;
}
2019-04-01 15:11:15 +00:00
# ifdef JANET_WINDOWS
2020-03-19 02:37:55 +00:00
static Janet os_stat_blocks ( jstat_t * st ) {
2023-01-21 16:37:34 +00:00
( void ) st ;
2019-04-01 15:21:45 +00:00
return janet_wrap_number ( 0 ) ;
}
2020-03-19 02:37:55 +00:00
static Janet os_stat_blocksize ( jstat_t * st ) {
2023-01-21 16:37:34 +00:00
( void ) st ;
2019-04-01 15:21:45 +00:00
return janet_wrap_number ( 0 ) ;
}
2019-04-01 15:11:15 +00:00
# else
2020-03-19 02:37:55 +00:00
static Janet os_stat_blocks ( jstat_t * st ) {
2019-04-01 15:21:45 +00:00
return janet_wrap_number ( st - > st_blocks ) ;
}
2020-03-19 02:37:55 +00:00
static Janet os_stat_blocksize ( jstat_t * st ) {
2019-04-01 15:21:45 +00:00
return janet_wrap_number ( st - > st_blksize ) ;
}
2019-04-01 15:11:15 +00:00
# endif
struct OsStatGetter {
const char * name ;
2020-03-19 02:37:55 +00:00
Janet ( * fn ) ( jstat_t * st ) ;
2019-04-01 15:11:15 +00:00
} ;
static const struct OsStatGetter os_stat_getters [ ] = {
{ " dev " , os_stat_dev } ,
{ " inode " , os_stat_inode } ,
{ " mode " , os_stat_mode } ,
2020-04-17 00:03:35 +00:00
{ " int-permissions " , os_stat_int_permissions } ,
2019-04-01 15:11:15 +00:00
{ " 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 }
} ;
2020-03-09 16:14:45 +00:00
static Janet os_stat_or_lstat ( int do_lstat , int32_t argc , Janet * argv ) {
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_FS_READ ) ;
2019-03-30 16:06:14 +00:00
janet_arity ( argc , 1 , 2 ) ;
const char * path = janet_getcstring ( argv , 0 ) ;
2019-04-01 15:11:15 +00:00
JanetTable * tab = NULL ;
2022-11-05 21:38:52 +00:00
const uint8_t * key = NULL ;
2019-03-30 16:06:14 +00:00
if ( argc = = 2 ) {
2019-04-01 15:11:15 +00:00
if ( janet_checktype ( argv [ 1 ] , JANET_KEYWORD ) ) {
key = janet_getkeyword ( argv , 1 ) ;
} else {
tab = janet_gettable ( argv , 1 ) ;
}
2019-03-30 16:06:14 +00:00
} else {
tab = janet_table ( 0 ) ;
}
2019-04-01 15:11:15 +00:00
2019-03-30 16:06:14 +00:00
/* Build result */
2020-03-19 02:37:55 +00:00
jstat_t st ;
2020-03-14 16:56:42 +00:00
# ifdef JANET_WINDOWS
2020-03-09 16:14:45 +00:00
( void ) do_lstat ;
2020-03-14 16:56:42 +00:00
int res = _stat ( path , & st ) ;
# else
2020-03-09 16:14:45 +00:00
int res ;
if ( do_lstat ) {
res = lstat ( path , & st ) ;
} else {
res = stat ( path , & st ) ;
}
2020-03-14 16:56:42 +00:00
# endif
2019-03-30 16:06:14 +00:00
if ( - 1 = = res ) {
2019-04-05 18:45:45 +00:00
return janet_wrap_nil ( ) ;
2019-03-30 16:06:14 +00:00
}
2019-04-01 15:11:15 +00:00
2022-11-05 21:38:52 +00:00
if ( NULL = = key ) {
2019-04-01 15:11:15 +00:00
/* 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 ( ) ;
}
2019-03-30 16:06:14 +00:00
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_stat ,
" (os/stat path &opt tab|key) " ,
" Gets information about a file or directory. Returns a table if the second argument is a keyword, returns "
2023-01-19 03:41:06 +00:00
" only that information from stat. If the file or directory does not exist, returns nil. The keys are: \n \n "
2021-07-26 07:48:04 +00:00
" * :dev - the device that the file is on \n \n "
" * :mode - the type of file, one of :file, :directory, :block, :character, :fifo, :socket, :link, or :other \n \n "
" * :int-permissions - A Unix permission integer like 8r744 \n \n "
" * :permissions - A Unix permission string like \" rwxr--r-- \" \n \n "
" * :uid - File uid \n \n "
" * :gid - File gid \n \n "
" * :nlink - number of links to file \n \n "
2022-04-18 00:42:32 +00:00
" * :rdev - Real device of file. 0 on Windows \n \n "
2021-07-26 07:48:04 +00:00
" * :size - size of file in bytes \n \n "
2022-04-18 00:42:32 +00:00
" * :blocks - number of blocks in file. 0 on Windows \n \n "
" * :blocksize - size of blocks in file. 0 on Windows \n \n "
2021-07-26 07:48:04 +00:00
" * :accessed - timestamp when file last accessed \n \n "
" * :changed - timestamp when file last changed (permissions changed) \n \n "
" * :modified - timestamp when file last modified (content changed) \n " ) {
2020-03-09 16:14:45 +00:00
return os_stat_or_lstat ( 0 , argc , argv ) ;
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_lstat ,
" (os/lstat path &opt tab|key) " ,
" Like os/stat, but don't follow symlinks. \n " ) {
2020-03-09 16:14:45 +00:00
return os_stat_or_lstat ( 1 , argc , argv ) ;
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_chmod ,
" (os/chmod path mode) " ,
2022-04-18 00:42:32 +00:00
" 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 "
2021-07-26 07:48:04 +00:00
" 8r666 or 8r400. Windows will not differentiate between user, group, and other permissions, and thus will combine all of these permissions. Returns nil. " ) {
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_FS_WRITE ) ;
2020-03-14 16:56:42 +00:00
janet_fixarity ( argc , 2 ) ;
const char * path = janet_getcstring ( argv , 0 ) ;
# ifdef JANET_WINDOWS
2020-04-03 20:02:12 +00:00
int res = _chmod ( path , os_getmode ( argv , 1 ) ) ;
2020-03-14 16:56:42 +00:00
# else
2020-04-03 20:02:12 +00:00
int res = chmod ( path , os_getmode ( argv , 1 ) ) ;
2020-03-14 16:56:42 +00:00
# endif
if ( - 1 = = res ) janet_panicf ( " %s: %s " , strerror ( errno ) , path ) ;
return janet_wrap_nil ( ) ;
}
2020-05-11 04:07:54 +00:00
# ifndef JANET_NO_UMASK
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_umask ,
" (os/umask mask) " ,
" Set a new umask, returns the old umask. " ) {
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_FS_WRITE ) ;
2020-04-03 20:12:58 +00:00
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 ) ) ;
}
2020-05-11 04:07:54 +00:00
# endif
2020-04-03 20:12:58 +00:00
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_dir ,
" (os/dir dir &opt array) " ,
" 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. " ) {
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_FS_READ ) ;
2019-03-30 16:36:27 +00:00
janet_arity ( argc , 1 , 2 ) ;
const char * dir = janet_getcstring ( argv , 0 ) ;
JanetArray * paths = ( argc = = 2 ) ? janet_getarray ( argv , 1 ) : janet_array ( 0 ) ;
2019-03-30 17:01:57 +00:00
# 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 ) ;
2019-03-31 18:14:25 +00:00
sprintf ( pattern , " %s/* " , dir ) ;
2019-03-30 17:01:57 +00:00
intptr_t res = _findfirst ( pattern , & afile ) ;
2019-03-30 17:46:52 +00:00
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 ) ;
2019-03-30 17:01:57 +00:00
_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 ) ;
2019-03-30 16:36:27 +00:00
while ( ( dp = readdir ( dfd ) ) ! = NULL ) {
if ( ! strcmp ( dp - > d_name , " . " ) | | ! strcmp ( dp - > d_name , " .. " ) ) {
continue ;
}
janet_array_push ( paths , janet_cstringv ( dp - > d_name ) ) ;
}
2019-03-30 17:01:57 +00:00
closedir ( dfd ) ;
# endif
2019-03-30 16:36:27 +00:00
return janet_wrap_array ( paths ) ;
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_rename ,
" (os/rename oldname newname) " ,
" Rename a file on disk to a new path. Returns nil. " ) {
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_FS_WRITE ) ;
2019-05-29 15:31:19 +00:00
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 ( ) ;
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_realpath ,
" (os/realpath path) " ,
" Get the absolute path for a given path, following ../, ./, and symlinks. "
2023-01-23 03:55:04 +00:00
" Returns an absolute path as a string. " ) {
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_FS_READ ) ;
2020-03-25 00:45:57 +00:00
janet_fixarity ( argc , 1 ) ;
const char * src = janet_getcstring ( argv , 0 ) ;
2020-06-14 20:49:39 +00:00
# ifdef JANET_NO_REALPATH
janet_panic ( " os/realpath not enabled for this platform " ) ;
# else
2020-06-02 09:05:41 +00:00
# ifdef JANET_WINDOWS
char * dest = _fullpath ( NULL , src , _MAX_PATH ) ;
# else
2020-03-25 00:45:57 +00:00
char * dest = realpath ( src , NULL ) ;
2020-06-02 09:05:41 +00:00
# endif
2020-03-25 00:45:57 +00:00
if ( NULL = = dest ) janet_panicf ( " %s: %s " , strerror ( errno ) , src ) ;
Janet ret = janet_cstringv ( dest ) ;
2021-03-23 10:00:48 +00:00
janet_free ( dest ) ;
2020-03-25 00:45:57 +00:00
return ret ;
2020-06-14 20:49:39 +00:00
# endif
2020-03-25 00:45:57 +00:00
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_permission_string ,
" (os/perm-string int) " ,
2022-04-18 00:42:32 +00:00
" Convert a Unix octal permission value from a permission integer as returned by `os/stat` "
2021-07-26 07:48:04 +00:00
" to a human readable string, that follows the formatting "
2022-04-18 00:42:32 +00:00
" of Unix tools like `ls`. Returns the string as a 9-character string of r, w, x and - characters. Does not "
2021-07-26 07:48:04 +00:00
" include the file/directory/symlink character as rendered by `ls`. " ) {
2020-04-03 20:02:12 +00:00
janet_fixarity ( argc , 1 ) ;
return os_make_permstring ( os_get_unix_mode ( argv , 0 ) ) ;
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_permission_int ,
" (os/perm-int bytes) " ,
2022-04-18 00:42:32 +00:00
" Parse a 9-character permission string and return an integer that can be used by chmod. " ) {
2020-04-03 20:02:12 +00:00
janet_fixarity ( argc , 1 ) ;
return janet_wrap_integer ( os_get_unix_mode ( argv , 0 ) ) ;
}
2020-11-15 15:56:19 +00:00
# ifdef JANET_EV
2020-11-15 18:13:30 +00:00
/*
* Define a few functions on streams the require JANET_EV to be defined .
*/
static jmode_t os_optmode ( int32_t argc , const Janet * argv , int32_t n , int32_t dflt ) {
if ( argc > n ) return os_getmode ( argv , n ) ;
return janet_perm_from_unix ( dflt ) ;
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_open ,
" (os/open path &opt flags mode) " ,
" Create a stream from a file, like the POSIX open system call. Returns a new stream. "
2022-04-18 00:42:32 +00:00
" `mode` should be a file mode as passed to `os/chmod`, but only if the create flag is given. "
2021-07-26 07:48:04 +00:00
" The default mode is 8r666. "
" Allowed flags are as follows: \n \n "
" * :r - open this file for reading \n "
" * :w - open this file for writing \n "
2022-08-02 19:58:32 +00:00
" * :c - create a new file (O \\ _CREATE) \n "
" * :e - fail if the file exists (O \\ _EXCL) \n "
" * :t - shorten an existing file to length 0 (O \\ _TRUNC) \n \n "
2022-04-18 00:42:32 +00:00
" Posix-only flags: \n \n "
2022-08-02 19:58:32 +00:00
" * :a - append to a file (O \\ _APPEND) \n "
" * :x - O \\ _SYNC \n "
" * :C - O \\ _NOCTTY \n \n "
2022-04-18 00:42:32 +00:00
" Windows-only flags: \n \n "
2022-08-02 19:58:32 +00:00
" * :R - share reads (FILE \\ _SHARE \\ _READ) \n "
" * :W - share writes (FILE \\ _SHARE \\ _WRITE) \n "
" * :D - share deletes (FILE \\ _SHARE \\ _DELETE) \n "
" * :H - FILE \\ _ATTRIBUTE \\ _HIDDEN \n "
" * :O - FILE \\ _ATTRIBUTE \\ _READONLY \n "
" * :F - FILE \\ _ATTRIBUTE \\ _OFFLINE \n "
" * :T - FILE \\ _ATTRIBUTE \\ _TEMPORARY \n "
" * :d - FILE \\ _FLAG \\ _DELETE \\ _ON \\ _CLOSE \n "
" * :b - FILE \\ _FLAG \\ _NO \\ _BUFFERING \n " ) {
2020-11-15 15:56:19 +00:00
janet_arity ( argc , 1 , 3 ) ;
const char * path = janet_getcstring ( argv , 0 ) ;
const uint8_t * opt_flags = janet_optkeyword ( argv , argc , 1 , ( const uint8_t * ) " r " ) ;
jmode_t mode = os_optmode ( argc , argv , 2 , 0666 ) ;
uint32_t stream_flags = 0 ;
JanetHandle fd ;
# ifdef JANET_WINDOWS
2023-01-21 16:37:34 +00:00
( void ) mode ;
2020-11-15 15:56:19 +00:00
DWORD desiredAccess = 0 ;
DWORD shareMode = 0 ;
DWORD creationDisp = 0 ;
DWORD flagsAndAttributes = FILE_FLAG_OVERLAPPED ;
/* We map unix-like open flags to the creationDisp parameter */
int creatUnix = 0 ;
# define OCREAT 1
# define OEXCL 2
# define OTRUNC 4
for ( const uint8_t * c = opt_flags ; * c ; c + + ) {
switch ( * c ) {
default :
break ;
case ' r ' :
desiredAccess | = GENERIC_READ ;
stream_flags | = JANET_STREAM_READABLE ;
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_FS_READ ) ;
2020-11-15 15:56:19 +00:00
break ;
case ' w ' :
desiredAccess | = GENERIC_WRITE ;
stream_flags | = JANET_STREAM_WRITABLE ;
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_FS_WRITE ) ;
2020-11-15 15:56:19 +00:00
break ;
case ' c ' :
creatUnix | = OCREAT ;
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_FS_WRITE ) ;
2020-11-15 15:56:19 +00:00
break ;
case ' e ' :
creatUnix | = OEXCL ;
break ;
case ' t ' :
creatUnix | = OTRUNC ;
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_FS_WRITE ) ;
2020-11-15 15:56:19 +00:00
break ;
/* Windows only flags */
case ' D ' :
shareMode | = FILE_SHARE_DELETE ;
break ;
case ' R ' :
shareMode | = FILE_SHARE_READ ;
break ;
case ' W ' :
shareMode | = FILE_SHARE_WRITE ;
break ;
case ' H ' :
flagsAndAttributes | = FILE_ATTRIBUTE_HIDDEN ;
break ;
case ' O ' :
flagsAndAttributes | = FILE_ATTRIBUTE_READONLY ;
break ;
case ' F ' :
flagsAndAttributes | = FILE_ATTRIBUTE_OFFLINE ;
break ;
case ' T ' :
flagsAndAttributes | = FILE_ATTRIBUTE_TEMPORARY ;
break ;
case ' d ' :
flagsAndAttributes | = FILE_FLAG_DELETE_ON_CLOSE ;
break ;
case ' b ' :
flagsAndAttributes | = FILE_FLAG_NO_BUFFERING ;
break ;
/* we could potentially add more here -
* https : //docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
*/
}
}
switch ( creatUnix ) {
default :
janet_panic ( " invalid creation flags " ) ;
case 0 :
creationDisp = OPEN_EXISTING ;
break ;
case OCREAT :
creationDisp = OPEN_ALWAYS ;
break ;
case OCREAT + OEXCL :
creationDisp = CREATE_NEW ;
break ;
case OCREAT + OTRUNC :
creationDisp = CREATE_ALWAYS ;
break ;
case OTRUNC :
creationDisp = TRUNCATE_EXISTING ;
break ;
}
fd = CreateFileA ( path , desiredAccess , shareMode , NULL , creationDisp , flagsAndAttributes , NULL ) ;
if ( fd = = INVALID_HANDLE_VALUE ) janet_panicv ( janet_ev_lasterr ( ) ) ;
# else
int open_flags = O_NONBLOCK ;
# ifdef JANET_LINUX
open_flags | = O_CLOEXEC ;
# endif
2022-10-25 00:39:41 +00:00
int read_flag = 0 ;
int write_flag = 0 ;
2020-11-15 15:56:19 +00:00
for ( const uint8_t * c = opt_flags ; * c ; c + + ) {
switch ( * c ) {
default :
break ;
case ' r ' :
2022-10-25 00:39:41 +00:00
read_flag = 1 ;
2020-11-15 15:56:19 +00:00
stream_flags | = JANET_STREAM_READABLE ;
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_FS_READ ) ;
2020-11-15 15:56:19 +00:00
break ;
case ' w ' :
2022-10-25 00:39:41 +00:00
write_flag = 1 ;
2020-11-15 15:56:19 +00:00
stream_flags | = JANET_STREAM_WRITABLE ;
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_FS_WRITE ) ;
2020-11-15 15:56:19 +00:00
break ;
case ' c ' :
open_flags | = O_CREAT ;
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_FS_WRITE ) ;
2020-11-15 15:56:19 +00:00
break ;
case ' e ' :
open_flags | = O_EXCL ;
break ;
case ' t ' :
open_flags | = O_TRUNC ;
2023-02-06 14:41:04 +00:00
janet_sandbox_assert ( JANET_SANDBOX_FS_WRITE ) ;
2020-11-15 15:56:19 +00:00
break ;
/* posix only */
case ' x ' :
open_flags | = O_SYNC ;
break ;
case ' C ' :
open_flags | = O_NOCTTY ;
break ;
case ' a ' :
open_flags | = O_APPEND ;
break ;
}
}
2022-10-25 00:39:41 +00:00
/* If both read and write, fix up to O_RDWR */
if ( read_flag & & ! write_flag ) {
open_flags | = O_RDONLY ;
} else if ( write_flag & & ! read_flag ) {
open_flags | = O_WRONLY ;
} else {
open_flags = O_RDWR ;
}
2020-11-15 15:56:19 +00:00
do {
fd = open ( path , open_flags , mode ) ;
} while ( fd = = - 1 & & errno = = EINTR ) ;
if ( fd = = - 1 ) janet_panicv ( janet_ev_lasterr ( ) ) ;
# endif
return janet_wrap_abstract ( janet_stream ( fd , stream_flags , NULL ) ) ;
}
2021-07-26 07:48:04 +00:00
JANET_CORE_FN ( os_pipe ,
" (os/pipe) " ,
2022-04-18 00:42:32 +00:00
" Create a readable stream and a writable stream that are connected. Returns a two-element "
2021-07-26 07:48:04 +00:00
" tuple where the first element is a readable stream and the second element is the writable "
" stream. " ) {
2020-11-15 15:56:19 +00:00
( void ) argv ;
janet_fixarity ( argc , 0 ) ;
2020-11-18 16:53:36 +00:00
JanetHandle fds [ 2 ] ;
2021-01-12 00:00:31 +00:00
if ( janet_make_pipe ( fds , 0 ) ) janet_panicv ( janet_ev_lasterr ( ) ) ;
2020-11-15 15:56:19 +00:00
JanetStream * reader = janet_stream ( fds [ 0 ] , JANET_STREAM_READABLE , NULL ) ;
JanetStream * writer = janet_stream ( fds [ 1 ] , JANET_STREAM_WRITABLE , NULL ) ;
Janet tup [ 2 ] = { janet_wrap_abstract ( reader ) , janet_wrap_abstract ( writer ) } ;
return janet_wrap_tuple ( janet_tuple_n ( tup , 2 ) ) ;
}
# endif
2019-03-29 03:22:58 +00:00
# endif /* JANET_REDUCED_OS */
2018-03-29 00:50:20 +00:00
/* Module entry point */
2019-01-06 01:09:03 +00:00
void janet_lib_os ( JanetTable * env ) {
2020-01-07 01:30:44 +00:00
# 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 . */
2021-08-09 02:06:53 +00:00
static volatile long env_lock_initializing = 0 ;
static volatile long env_lock_initialized = 0 ;
2021-08-15 20:25:07 +00:00
if ( ! InterlockedExchange ( & env_lock_initializing , 1 ) ) {
InitializeCriticalSection ( & env_lock ) ;
InterlockedOr ( & env_lock_initialized , 1 ) ;
2021-08-09 02:06:53 +00:00
} else {
2021-08-15 20:25:07 +00:00
while ( ! InterlockedOr ( & env_lock_initialized , 0 ) ) {
Sleep ( 0 ) ;
}
2020-01-07 01:30:44 +00:00
}
2021-08-09 02:06:53 +00:00
2020-11-29 21:36:21 +00:00
# endif
# ifndef JANET_NO_PROCESSES
2020-01-07 01:30:44 +00:00
# endif
2021-07-26 07:48:04 +00:00
JanetRegExt os_cfuns [ ] = {
JANET_CORE_REG ( " os/exit " , os_exit ) ,
JANET_CORE_REG ( " os/which " , os_which ) ,
JANET_CORE_REG ( " os/arch " , os_arch ) ,
2023-01-28 20:00:02 +00:00
JANET_CORE_REG ( " os/compiler " , os_compiler ) ,
2021-07-26 07:48:04 +00:00
# ifndef JANET_REDUCED_OS
2023-02-06 14:41:04 +00:00
/* misc (un-sandboxed) */
JANET_CORE_REG ( " os/cpu-count " , os_cpu_count ) ,
JANET_CORE_REG ( " os/cwd " , os_cwd ) ,
JANET_CORE_REG ( " os/cryptorand " , os_cryptorand ) ,
JANET_CORE_REG ( " os/perm-string " , os_permission_string ) ,
JANET_CORE_REG ( " os/perm-int " , os_permission_int ) ,
JANET_CORE_REG ( " os/mktime " , os_mktime ) ,
JANET_CORE_REG ( " os/time " , os_time ) , /* not high resolution */
JANET_CORE_REG ( " os/date " , os_date ) , /* not high resolution */
JANET_CORE_REG ( " os/sleep " , os_sleep ) ,
/* env functions */
2021-07-26 07:48:04 +00:00
JANET_CORE_REG ( " os/environ " , os_environ ) ,
JANET_CORE_REG ( " os/getenv " , os_getenv ) ,
2023-02-06 14:41:04 +00:00
JANET_CORE_REG ( " os/setenv " , os_setenv ) ,
/* fs read */
2021-07-26 07:48:04 +00:00
JANET_CORE_REG ( " os/dir " , os_dir ) ,
JANET_CORE_REG ( " os/stat " , os_stat ) ,
JANET_CORE_REG ( " os/lstat " , os_lstat ) ,
JANET_CORE_REG ( " os/chmod " , os_chmod ) ,
JANET_CORE_REG ( " os/touch " , os_touch ) ,
2023-02-06 14:41:04 +00:00
JANET_CORE_REG ( " os/realpath " , os_realpath ) ,
2021-07-26 07:48:04 +00:00
JANET_CORE_REG ( " os/cd " , os_cd ) ,
# ifndef JANET_NO_UMASK
JANET_CORE_REG ( " os/umask " , os_umask ) ,
# endif
2023-02-06 14:41:04 +00:00
# ifndef JANET_NO_SYMLINKS
JANET_CORE_REG ( " os/readlink " , os_readlink ) ,
# endif
/* fs write */
2021-07-26 07:48:04 +00:00
JANET_CORE_REG ( " os/mkdir " , os_mkdir ) ,
JANET_CORE_REG ( " os/rmdir " , os_rmdir ) ,
JANET_CORE_REG ( " os/rm " , os_remove ) ,
JANET_CORE_REG ( " os/link " , os_link ) ,
2023-02-06 14:41:04 +00:00
JANET_CORE_REG ( " os/rename " , os_rename ) ,
2021-07-26 07:48:04 +00:00
# ifndef JANET_NO_SYMLINKS
JANET_CORE_REG ( " os/symlink " , os_symlink ) ,
# endif
2023-02-06 14:41:04 +00:00
/* processes */
2021-07-26 07:48:04 +00:00
# ifndef JANET_NO_PROCESSES
JANET_CORE_REG ( " os/execute " , os_execute ) ,
JANET_CORE_REG ( " os/spawn " , os_spawn ) ,
JANET_CORE_REG ( " os/shell " , os_shell ) ,
2023-02-06 14:41:04 +00:00
/* no need to sandbox process management if you can't create processes
* ( allows for limited functionality if use exposes C - functions to create specific processes ) */
2021-07-26 07:48:04 +00:00
JANET_CORE_REG ( " os/proc-wait " , os_proc_wait ) ,
JANET_CORE_REG ( " os/proc-kill " , os_proc_kill ) ,
JANET_CORE_REG ( " os/proc-close " , os_proc_close ) ,
# endif
2023-02-06 14:41:04 +00:00
/* high resolution timers */
2021-07-26 07:48:04 +00:00
JANET_CORE_REG ( " os/clock " , os_clock ) ,
2023-02-06 14:41:04 +00:00
2021-07-26 07:48:04 +00:00
# ifdef JANET_EV
2023-02-06 14:41:04 +00:00
JANET_CORE_REG ( " os/open " , os_open ) , /* fs read and write */
2021-07-26 07:48:04 +00:00
JANET_CORE_REG ( " os/pipe " , os_pipe ) ,
# endif
# endif
JANET_REG_END
} ;
janet_core_cfuns_ext ( env , NULL , os_cfuns ) ;
2018-03-29 00:50:20 +00:00
}