2018-03-29 00:50:20 +00:00
/*
2021-01-12 00:00:31 +00:00
* Copyright ( c ) 2021 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
2020-06-30 09:17:01 +00:00
# ifdef JANET_APPLE
# include <AvailabilityMacros.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>
2019-05-31 17:43:38 +00:00
extern char * * environ ;
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
2018-09-23 01:46:50 +00:00
/* For macos */
# ifdef __MACH__
# include <mach/clock.h>
# include <mach/mach.h>
# endif
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 int env_lock_initialized = 0 ;
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)
2019-01-06 01:09:03 +00:00
static Janet os_which ( int32_t argc , Janet * argv ) {
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 ) ) ;
# 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 " ) ;
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 */
static Janet os_arch ( int32_t argc , Janet * argv ) {
janet_fixarity ( argc , 0 ) ;
( void ) argv ;
/* Check 64-bit vs 32-bit */
# if defined(JANET_ARCH_NAME)
return janet_ckeywordv ( janet_stringify ( JANET_ARCH_NAME ) ) ;
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 " ) ;
# elif (defined(__sparc__))
return janet_ckeywordv ( " sparc " ) ;
2020-06-26 09:11:21 +00:00
# elif (defined(__ppc__))
return janet_ckeywordv ( " ppc " ) ;
2019-08-18 15:00:04 +00:00
# else
return janet_ckeywordv ( " unknown " ) ;
# endif
}
# undef janet_stringify1
# undef janet_stringify
2019-03-29 03:22:58 +00:00
static Janet os_exit ( int32_t argc , Janet * argv ) {
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 ( ) ;
}
2020-05-09 17:00:01 +00:00
# ifndef JANET_REDUCED_OS
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 */
2019-05-30 22:40:10 +00:00
static char * * os_execute_env ( int32_t argc , const Janet * argv ) {
char * * envp = NULL ;
if ( argc > 2 ) {
JanetDictView dict = janet_getdictionary ( argv , 2 ) ;
2020-01-03 04:02:57 +00:00
envp = janet_smalloc ( sizeof ( char * ) * ( ( size_t ) dict . len + 1 ) ) ;
2019-05-30 22:40:10 +00:00
int32_t j = 0 ;
for ( int32_t i = 0 ; i < dict . cap ; i + + ) {
const JanetKV * kv = dict . kvs + i ;
if ( ! janet_checktype ( kv - > key , JANET_STRING ) ) continue ;
if ( ! janet_checktype ( kv - > value , JANET_STRING ) ) continue ;
const uint8_t * keys = janet_unwrap_string ( kv - > key ) ;
const uint8_t * vals = janet_unwrap_string ( kv - > value ) ;
int32_t klen = janet_string_length ( keys ) ;
int32_t vlen = janet_string_length ( vals ) ;
/* Check keys has no embedded 0s or =s. */
int skip = 0 ;
for ( int32_t k = 0 ; k < klen ; k + + ) {
if ( keys [ k ] = = ' \0 ' | | keys [ k ] = = ' = ' ) {
skip = 1 ;
break ;
}
}
if ( skip ) continue ;
2020-01-03 04:02:57 +00:00
char * envitem = janet_smalloc ( ( size_t ) klen + ( size_t ) vlen + 2 ) ;
2019-05-30 22:40:10 +00:00
memcpy ( envitem , keys , klen ) ;
envitem [ klen ] = ' = ' ;
memcpy ( envitem + klen + 1 , vals , vlen ) ;
envitem [ klen + vlen + 1 ] = 0 ;
envp [ j + + ] = envitem ;
}
envp [ j ] = NULL ;
2018-07-07 01:50:59 +00:00
}
2019-05-30 22:40:10 +00:00
return envp ;
}
2018-07-07 01:50:59 +00:00
2020-09-14 01:49:38 +00:00
/* Free memory from os_execute. Not actually needed, but doesn't pressure the GC
in the happy path . */
2019-05-30 22:40:10 +00:00
static void os_execute_cleanup ( char * * envp , const char * * child_argv ) {
2019-05-31 17:43:38 +00:00
# ifdef JANET_WINDOWS
( void ) child_argv ;
# else
2019-06-02 03:38:10 +00:00
janet_sfree ( ( void * ) child_argv ) ;
2019-05-31 17:43:38 +00:00
# endif
2019-05-30 22:40:10 +00:00
if ( NULL ! = envp ) {
char * * envitem = envp ;
while ( * envitem ! = NULL ) {
2019-06-02 03:38:10 +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 ) ;
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
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 ) ;
GetExitCodeProcess ( proc - > pHandle , & args . argi ) ;
return args ;
}
# else /* windows check */
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 ;
pid_t result ;
int status = 0 ;
do {
result = waitpid ( proc - > pid , & status , 0 ) ;
} while ( result = = - 1 & & errno = = EINTR ) ;
2020-11-29 21:36:21 +00:00
/* Use POSIX shell semantics for interpreting signals */
if ( WIFEXITED ( status ) ) {
status = WEXITSTATUS ( status ) ;
} else if ( WIFSTOPPED ( status ) ) {
status = WSTOPSIG ( status ) + 128 ;
} else {
status = WTERMSIG ( status ) + 128 ;
}
2021-01-03 17:21:44 +00:00
args . argi = status ;
return args ;
}
# endif /* End windows check */
/* Callback that is called in main thread when subroutine completes. */
static void janet_proc_wait_cb ( JanetEVGenericMessage args ) {
int status = args . argi ;
JanetProc * proc = ( JanetProc * ) args . argp ;
2020-12-31 22:12:42 +00:00
if ( NULL ! = proc ) {
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 ) ) {
CloseHandle ( proc - > pHandle ) ;
CloseHandle ( proc - > tHandle ) ;
}
# else
if ( ! ( proc - > flags & JANET_PROC_WAITED ) ) {
/* Kill and wait to prevent zombies */
kill ( proc - > pid , SIGKILL ) ;
int status ;
waitpid ( proc - > pid , & status , 0 ) ;
}
# endif
return 0 ;
}
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
}
2020-09-02 02:36:49 +00:00
static Janet os_proc_wait ( int32_t argc , Janet * argv ) {
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
}
static Janet os_proc_kill ( int32_t argc , Janet * argv ) {
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 ;
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 ] ;
}
}
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-12 00:00:31 +00:00
if ( janet_make_pipe ( handles , reverse ) ) 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-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 ;
}
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 ) {
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 ) {
2020-09-04 13:09:05 +00:00
flags = janet_getflags ( argv , 1 , " epx " ) ;
2021-01-11 15:05:26 +00:00
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 ) ;
2019-05-30 22:40:10 +00:00
char * * envp = os_execute_env ( argc , argv ) ;
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 */
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 " ) ) ;
2020-09-13 00:48:12 +00:00
if ( janet_keyeq ( maybe_stdin , " pipe " ) ) {
2020-09-14 01:49:38 +00:00
new_in = make_pipes ( & pipe_in , 1 , & pipe_errflag ) ;
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
}
if ( janet_keyeq ( maybe_stdout , " pipe " ) ) {
2020-09-14 01:49:38 +00:00
new_out = make_pipes ( & pipe_out , 0 , & pipe_errflag ) ;
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
}
2021-01-11 15:05:26 +00:00
if ( janet_keyeq ( maybe_stderr , " pipe " ) ) {
2020-09-14 01:49:38 +00:00
new_err = make_pipes ( & pipe_err , 0 , & pipe_errflag ) ;
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-31 17:45:39 +00:00
/* Result */
int status = 0 ;
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 ) {
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
2019-05-30 22:40:10 +00:00
/* Use _spawn family of functions. */
/* Windows docs say do this before any spawns. */
_flushall ( ) ;
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
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 ) ;
2020-11-15 18:13:30 +00:00
} else if ( new_in ! = JANET_HANDLE_NONE ) {
posix_spawn_file_actions_adddup2 ( & actions , new_in , 0 ) ;
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 ) ;
2020-11-15 18:13:30 +00:00
} else if ( new_out ! = JANET_HANDLE_NONE ) {
posix_spawn_file_actions_adddup2 ( & actions , new_out , 1 ) ;
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 ) ;
2020-11-15 18:13:30 +00:00
} else if ( new_err ! = JANET_HANDLE_NONE ) {
posix_spawn_file_actions_adddup2 ( & actions , new_err , 2 ) ;
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
}
/* Wait for child */
if ( status ) {
os_execute_cleanup ( envp , child_argv ) ;
2019-12-15 02:39:14 +00:00
janet_panicf ( " %p: %s " , argv [ 0 ] , strerror ( errno ) ) ;
2019-05-30 22:40:10 +00:00
} else {
2020-09-02 01:06:35 +00:00
/* Wait to complete */
os_execute_cleanup ( envp , child_argv ) ;
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 ;
if ( new_in ! = JANET_HANDLE_NONE ) {
2021-01-11 15:05:26 +00:00
proc - > in = get_stdio_for_handle ( new_in , orig_in , 1 ) ;
2020-12-31 22:12:42 +00:00
if ( NULL = = proc - > in ) janet_panic ( " failed to construct proc " ) ;
}
if ( new_out ! = JANET_HANDLE_NONE ) {
2021-01-11 15:05:26 +00:00
proc - > out = get_stdio_for_handle ( new_out , orig_out , 0 ) ;
2020-12-31 22:12:42 +00:00
if ( NULL = = proc - > out ) janet_panic ( " failed to construct proc " ) ;
2020-11-29 21:36:21 +00:00
}
2020-12-31 22:12:42 +00:00
if ( new_err ! = JANET_HANDLE_NONE ) {
2021-01-11 15:05:26 +00:00
proc - > err = get_stdio_for_handle ( new_err , orig_err , 0 ) ;
2020-12-31 22:12:42 +00:00
if ( NULL = = proc - > err ) janet_panic ( " failed to construct proc " ) ;
}
proc - > flags = 0 ;
2020-11-29 21:36:21 +00:00
if ( janet_flag_at ( flags , 2 ) ) {
proc - > flags | = JANET_PROC_ERROR_NONZERO ;
}
if ( is_spawn ) {
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
2020-09-04 13:09:05 +00:00
static Janet os_execute ( int32_t argc , Janet * argv ) {
2020-09-04 19:54:58 +00:00
return os_execute_impl ( argc , argv , 0 ) ;
2020-09-04 13:09:05 +00:00
}
static Janet os_spawn ( int32_t argc , Janet * argv ) {
2020-09-04 19:54:58 +00:00
return os_execute_impl ( argc , argv , 1 ) ;
2020-09-04 13:09:05 +00:00
}
2019-01-06 01:09:03 +00:00
static Janet os_shell ( int32_t argc , Janet * argv ) {
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 ;
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 ) ;
2018-03-29 00:50:20 +00:00
}
2020-05-09 17:00:01 +00:00
# endif /* JANET_NO_PROCESSES */
2019-11-28 05:31:01 +00:00
static Janet os_environ ( int32_t argc , Janet * argv ) {
( 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 ) ;
}
2019-01-06 01:09:03 +00:00
static Janet os_getenv ( int32_t argc , Janet * argv ) {
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
}
2019-01-06 01:09:03 +00:00
static Janet os_setenv ( int32_t argc , Janet * argv ) {
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
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
}
2019-01-06 01:09:03 +00:00
static Janet os_time ( int32_t argc , Janet * argv ) {
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
}
2019-01-06 01:09:03 +00:00
static Janet os_clock ( int32_t argc , Janet * argv ) {
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
}
2019-01-06 01:09:03 +00:00
static Janet os_sleep ( int32_t argc , Janet * argv ) {
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
}
2019-01-06 01:09:03 +00:00
static Janet os_cwd ( int32_t argc , Janet * argv ) {
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
}
2019-12-03 22:29:11 +00:00
static Janet os_cryptorand ( int32_t argc , Janet * argv ) {
JanetBuffer * buffer ;
janet_arity ( argc , 1 , 2 ) ;
int32_t offset ;
int32_t n = janet_getinteger ( argv , 0 ) ;
if ( n < 0 ) janet_panic ( " expected positive integer " ) ;
if ( argc = = 2 ) {
buffer = janet_getbuffer ( argv , 1 ) ;
offset = buffer - > count ;
} else {
offset = 0 ;
buffer = janet_buffer ( n ) ;
}
/* We could optimize here by adding setcount_uninit */
janet_buffer_setcount ( buffer , offset + n ) ;
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 ) ;
}
2019-01-20 19:34:33 +00:00
static Janet os_date ( int32_t argc , Janet * argv ) {
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 ;
2019-01-20 19:34:33 +00:00
if ( argc ) {
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
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
}
static Janet os_mktime ( int32_t argc , Janet * argv ) {
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
2019-03-29 03:22:58 +00:00
static Janet os_link ( int32_t argc , Janet * argv ) {
janet_arity ( argc , 2 , 3 ) ;
# ifdef JANET_WINDOWS
( void ) argc ;
( void ) argv ;
janet_panic ( " os/link not supported on Windows " ) ;
return janet_wrap_nil ( ) ;
# else
const char * oldpath = janet_getcstring ( argv , 0 ) ;
const char * newpath = janet_getcstring ( argv , 1 ) ;
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
}
static Janet os_symlink ( int32_t argc , Janet * argv ) {
janet_fixarity ( argc , 2 ) ;
# ifdef JANET_WINDOWS
( void ) argc ;
( void ) argv ;
janet_panic ( " os/symlink not supported on Windows " ) ;
return janet_wrap_nil ( ) ;
# else
const char * oldpath = janet_getcstring ( argv , 0 ) ;
const char * newpath = janet_getcstring ( argv , 1 ) ;
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
2019-03-29 03:22:58 +00:00
static Janet os_mkdir ( int32_t argc , Janet * argv ) {
janet_fixarity ( argc , 1 ) ;
const char * path = janet_getcstring ( argv , 0 ) ;
# ifdef JANET_WINDOWS
int res = _mkdir ( path ) ;
# else
int res = mkdir ( path , S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH ) ;
# endif
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
}
2019-03-30 19:39:24 +00:00
static Janet os_rmdir ( int32_t argc , Janet * argv ) {
janet_fixarity ( argc , 1 ) ;
const char * path = janet_getcstring ( argv , 0 ) ;
# ifdef JANET_WINDOWS
int res = _rmdir ( path ) ;
# else
int res = rmdir ( path ) ;
# endif
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
static Janet os_cd ( int32_t argc , Janet * argv ) {
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
}
static Janet os_touch ( int32_t argc , Janet * argv ) {
janet_arity ( argc , 1 , 3 ) ;
const char * path = janet_getcstring ( argv , 0 ) ;
struct utimbuf timebuf , * bufp ;
if ( argc > = 2 ) {
bufp = & timebuf ;
timebuf . actime = ( time_t ) janet_getnumber ( argv , 1 ) ;
if ( argc > = 3 ) {
timebuf . modtime = ( time_t ) janet_getnumber ( argv , 2 ) ;
} else {
timebuf . modtime = timebuf . actime ;
}
} else {
bufp = NULL ;
}
int res = utime ( path , bufp ) ;
2019-03-30 19:39:24 +00:00
if ( - 1 = = res ) janet_panic ( strerror ( errno ) ) ;
return janet_wrap_nil ( ) ;
}
static Janet os_remove ( int32_t argc , Janet * argv ) {
janet_fixarity ( argc , 1 ) ;
const char * path = janet_getcstring ( argv , 0 ) ;
int status = remove ( path ) ;
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
2020-03-09 16:34:30 +00:00
static Janet os_readlink ( int32_t argc , Janet * argv ) {
janet_fixarity ( argc , 1 ) ;
# ifdef JANET_WINDOWS
( void ) argc ;
( void ) argv ;
janet_panic ( " os/readlink not supported on Windows " ) ;
return janet_wrap_nil ( ) ;
# else
static char buffer [ PATH_MAX ] ;
const char * path = janet_getcstring ( argv , 0 ) ;
ssize_t len = readlink ( path , buffer , sizeof buffer ) ;
if ( len < 0 | | ( size_t ) len > = sizeof buffer )
janet_panicf ( " %s: %s " , strerror ( errno ) , path ) ;
return janet_stringv ( ( const uint8_t * ) buffer , len ) ;
# endif
}
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 ) {
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 ) {
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 ) {
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 ;
int getall = 1 ;
const uint8_t * key ;
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 ) ) {
getall = 0 ;
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
if ( getall ) {
/* Put results in table */
for ( const struct OsStatGetter * sg = os_stat_getters ; sg - > name ! = NULL ; sg + + ) {
janet_table_put ( tab , janet_ckeywordv ( sg - > name ) , sg - > fn ( & st ) ) ;
}
return janet_wrap_table ( tab ) ;
} else {
/* Get one result */
for ( const struct OsStatGetter * sg = os_stat_getters ; sg - > name ! = NULL ; sg + + ) {
if ( janet_cstrcmp ( key , sg - > name ) ) continue ;
return sg - > fn ( & st ) ;
}
janet_panicf ( " unexpected keyword %v " , janet_wrap_keyword ( key ) ) ;
return janet_wrap_nil ( ) ;
}
2019-03-30 16:06:14 +00:00
}
2020-03-09 16:14:45 +00:00
static Janet os_stat ( int32_t argc , Janet * argv ) {
return os_stat_or_lstat ( 0 , argc , argv ) ;
}
static Janet os_lstat ( int32_t argc , Janet * argv ) {
return os_stat_or_lstat ( 1 , argc , argv ) ;
}
2020-03-14 16:56:42 +00:00
static Janet os_chmod ( int32_t argc , Janet * argv ) {
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
2020-04-03 20:12:58 +00:00
static Janet os_umask ( int32_t argc , Janet * argv ) {
janet_fixarity ( argc , 1 ) ;
int mask = ( int ) os_getmode ( argv , 0 ) ;
# ifdef JANET_WINDOWS
int res = _umask ( mask ) ;
# else
int res = umask ( mask ) ;
# endif
return janet_wrap_integer ( janet_perm_to_unix ( res ) ) ;
}
2020-05-11 04:07:54 +00:00
# endif
2020-04-03 20:12:58 +00:00
2019-03-30 16:36:27 +00:00
static Janet os_dir ( int32_t argc , Janet * argv ) {
janet_arity ( argc , 1 , 2 ) ;
const char * dir = janet_getcstring ( argv , 0 ) ;
JanetArray * paths = ( argc = = 2 ) ? janet_getarray ( argv , 1 ) : janet_array ( 0 ) ;
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 ) ;
}
2019-05-29 15:31:19 +00:00
static Janet os_rename ( int32_t argc , Janet * argv ) {
janet_fixarity ( argc , 2 ) ;
const char * src = janet_getcstring ( argv , 0 ) ;
const char * dest = janet_getcstring ( argv , 1 ) ;
int status = rename ( src , dest ) ;
if ( status ) {
janet_panic ( strerror ( errno ) ) ;
}
return janet_wrap_nil ( ) ;
}
2020-03-25 00:45:57 +00:00
static Janet os_realpath ( int32_t argc , Janet * argv ) {
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 ) ;
free ( dest ) ;
return ret ;
2020-06-14 20:49:39 +00:00
# endif
2020-03-25 00:45:57 +00:00
}
2020-04-03 20:02:12 +00:00
static Janet os_permission_string ( int32_t argc , Janet * argv ) {
janet_fixarity ( argc , 1 ) ;
return os_make_permstring ( os_get_unix_mode ( argv , 0 ) ) ;
}
static Janet os_permission_int ( int32_t argc , Janet * argv ) {
janet_fixarity ( argc , 1 ) ;
return janet_wrap_integer ( os_get_unix_mode ( argv , 0 ) ) ;
}
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 ) ;
}
2020-11-15 15:56:19 +00:00
static Janet os_open ( int32_t argc , Janet * argv ) {
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
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 ;
break ;
case ' w ' :
desiredAccess | = GENERIC_WRITE ;
stream_flags | = JANET_STREAM_WRITABLE ;
break ;
case ' c ' :
creatUnix | = OCREAT ;
break ;
case ' e ' :
creatUnix | = OEXCL ;
break ;
case ' t ' :
creatUnix | = OTRUNC ;
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
for ( const uint8_t * c = opt_flags ; * c ; c + + ) {
switch ( * c ) {
default :
break ;
case ' r ' :
open_flags = ( open_flags & O_WRONLY )
? ( ( open_flags & ~ O_WRONLY ) | O_RDWR )
: ( open_flags | O_RDONLY ) ;
stream_flags | = JANET_STREAM_READABLE ;
break ;
case ' w ' :
open_flags = ( open_flags & O_RDONLY )
? ( ( open_flags & ~ O_RDONLY ) | O_RDWR )
: ( open_flags | O_WRONLY ) ;
stream_flags | = JANET_STREAM_WRITABLE ;
break ;
case ' c ' :
open_flags | = O_CREAT ;
break ;
case ' e ' :
open_flags | = O_EXCL ;
break ;
case ' t ' :
open_flags | = O_TRUNC ;
break ;
/* posix only */
case ' x ' :
open_flags | = O_SYNC ;
break ;
case ' C ' :
open_flags | = O_NOCTTY ;
break ;
case ' a ' :
open_flags | = O_APPEND ;
break ;
}
}
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 ) ) ;
}
static Janet os_pipe ( int32_t argc , Janet * argv ) {
( 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 */
2019-01-24 05:15:58 +00:00
static const JanetReg os_cfuns [ ] = {
2019-03-29 03:22:58 +00:00
{
" os/exit " , os_exit ,
2019-06-08 14:30:30 +00:00
JDOC ( " (os/exit &opt x) \n \n "
2019-03-29 03:22:58 +00:00
" 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-01-06 06:49:56 +00:00
{
" os/which " , os_which ,
JDOC ( " (os/which) \n \n "
2019-02-20 01:51:34 +00:00
" Check the current operating system. Returns one of: \n \n "
2019-07-27 16:29:40 +00:00
" \t :windows \n "
" \t :macos \n "
2019-07-27 03:43:54 +00:00
" \t :web - Web assembly (emscripten) \n "
2019-07-27 16:29:40 +00:00
" \t :linux \n "
" \t :freebsd \n "
" \t :openbsd \n "
" \t :netbsd \n "
2020-05-10 21:44:16 +00:00
" \t :posix - A POSIX compatible system (default) \n \n "
" May also return a custom keyword specified at build time. " )
2018-11-16 07:34:50 +00:00
} ,
2019-08-18 15:00:04 +00:00
{
" os/arch " , os_arch ,
JDOC ( " (os/arch) \n \n "
" Check the ISA that janet was compiled for. Returns one of: \n \n "
" \t :x86 \n "
" \t :x86-64 \n "
" \t :arm \n "
" \t :aarch64 \n "
" \t :sparc \n "
2019-08-18 15:08:52 +00:00
" \t :wasm \n "
2019-08-18 15:00:04 +00:00
" \t :unknown \n " )
} ,
2019-03-29 03:22:58 +00:00
# ifndef JANET_REDUCED_OS
2019-12-15 02:39:14 +00:00
{
" os/environ " , os_environ ,
JDOC ( " (os/environ) \n \n "
" Get a copy of the os environment table. " )
} ,
2020-05-09 17:00:01 +00:00
{
" os/getenv " , os_getenv ,
JDOC ( " (os/getenv variable &opt dflt) \n \n "
" Get the string value of an environment variable. " )
} ,
2019-03-30 16:36:27 +00:00
{
" os/dir " , os_dir ,
2019-06-08 14:30:30 +00:00
JDOC ( " (os/dir dir &opt array) \n \n "
2019-03-30 16:36:27 +00:00
" Iterate over files and subdirectories in a directory. Returns an array of paths parts, "
2020-05-10 21:44:16 +00:00
" with only the file name or directory name and no prefix. " )
2019-03-30 16:36:27 +00:00
} ,
2019-03-29 03:22:58 +00:00
{
2019-03-30 16:06:14 +00:00
" os/stat " , os_stat ,
2019-06-08 14:30:30 +00:00
JDOC ( " (os/stat path &opt tab|key) \n \n "
2019-04-01 15:11:15 +00:00
" Gets information about a file or directory. Returns a table If the third argument is a keyword, returns "
2019-04-05 18:45:04 +00:00
" only that information from stat. If the file or directory does not exist, returns nil. The keys are \n \n "
2019-04-01 15:11:15 +00:00
" \t :dev - the device that the file is on \n "
" \t :mode - the type of file, one of :file, :directory, :block, :character, :fifo, :socket, :link, or :other \n "
2020-04-17 00:03:35 +00:00
" \t :int-permissions - A Unix permission integer like 8r744 \n "
2020-04-03 23:28:43 +00:00
" \t :permissions - A Unix permission string like \" rwxr--r-- \" \n "
2019-04-01 15:11:15 +00:00
" \t :uid - File uid \n "
" \t :gid - File gid \n "
" \t :nlink - number of links to file \n "
" \t :rdev - Real device of file. 0 on windows. \n "
" \t :size - size of file in bytes \n "
" \t :blocks - number of blocks in file. 0 on windows \n "
" \t :blocksize - size of blocks in file. 0 on windows \n "
" \t :accessed - timestamp when file last accessed \n "
2020-05-10 21:44:16 +00:00
" \t :changed - timestamp when file last changed (permissions changed) \n "
2019-04-01 15:11:15 +00:00
" \t :modified - timestamp when file last modified (content changed) \n " )
2019-03-30 16:06:14 +00:00
} ,
2020-03-09 16:14:45 +00:00
{
" os/lstat " , os_lstat ,
JDOC ( " (os/lstat path &opt tab|key) \n \n "
" Like os/stat, but don't follow symlinks. \n " )
} ,
2020-03-14 16:56:42 +00:00
{
" os/chmod " , os_chmod ,
JDOC ( " (os/chmod path mode) \n \n "
" Change file permissions, where mode is a permission string as returned by "
2020-04-03 20:23:29 +00:00
" os/perm-string, or an integer as returned by os/perm-int. "
2020-04-03 20:02:12 +00:00
" When mode is an integer, it is interpreted as a Unix permission value, best specified in octal, like "
" 8r666 or 8r400. Windows will not differentiate between user, group, and other permissions, and thus will combine all of these permissions. Returns nil. " )
2020-03-14 16:56:42 +00:00
} ,
2019-03-30 16:06:14 +00:00
{
2019-03-29 03:22:58 +00:00
" os/touch " , os_touch ,
2019-06-08 14:30:30 +00:00
JDOC ( " (os/touch path &opt actime modtime) \n \n "
2019-03-29 03:22:58 +00:00
" Update the access time and modification times for a file. By default, sets "
" times to the current time. " )
} ,
{
" os/cd " , os_cd ,
JDOC ( " (os/cd path) \n \n "
2020-03-13 20:01:48 +00:00
" Change current directory to path. Returns nil on success, errors on failure. " )
2019-03-29 03:22:58 +00:00
} ,
2020-05-11 04:07:54 +00:00
# ifndef JANET_NO_UMASK
2020-04-02 10:33:50 +00:00
{
" os/umask " , os_umask ,
JDOC ( " (os/umask mask) \n \n "
" Set a new umask, returns the old umask. " )
} ,
2020-05-11 04:07:54 +00:00
# endif
2019-03-29 03:22:58 +00:00
{
" os/mkdir " , os_mkdir ,
JDOC ( " (os/mkdir path) \n \n "
" Create a new directory. The path will be relative to the current directory if relative, otherwise "
2020-04-03 20:02:12 +00:00
" it will be an absolute path. Returns true if the directory was created, false if the directory already exists, and "
2020-03-25 00:45:57 +00:00
" errors otherwise. " )
2019-03-29 03:22:58 +00:00
} ,
2019-03-30 19:39:24 +00:00
{
" os/rmdir " , os_rmdir ,
JDOC ( " (os/rmdir path) \n \n "
" Delete a directory. The directory must be empty to succeed. " )
} ,
{
" os/rm " , os_remove ,
JDOC ( " (os/rm path) \n \n "
" Delete a file. Returns nil. " )
} ,
2019-03-29 03:22:58 +00:00
{
" os/link " , os_link ,
2019-06-08 14:30:30 +00:00
JDOC ( " (os/link oldpath newpath &opt symlink) \n \n "
2020-04-03 22:04:05 +00:00
" 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. " )
2020-03-25 00:45:57 +00:00
} ,
2020-05-11 04:07:54 +00:00
# ifndef JANET_NO_SYMLINKS
2020-03-25 00:45:57 +00:00
{
" os/symlink " , os_symlink ,
JDOC ( " (os/symlink oldpath newpath) \n \n "
" Create a symlink from oldpath to newpath, returning nil. Same as (os/link oldpath newpath true). " )
2019-03-29 03:22:58 +00:00
} ,
2020-03-09 16:34:30 +00:00
{
" os/readlink " , os_readlink ,
JDOC ( " (os/readlink path) \n \n "
" Read the contents of a symbolic link. Does not work on Windows. \n " )
} ,
2020-05-11 04:07:54 +00:00
# endif
2020-05-09 17:00:01 +00:00
# ifndef JANET_NO_PROCESSES
2019-01-06 06:49:56 +00:00
{
" os/execute " , os_execute ,
2019-05-30 22:40:10 +00:00
JDOC ( " (os/execute args &opts flags env) \n \n "
" Execute a program on the system and pass it string arguments. Flags "
" is a keyword that modifies how the program will execute. \n \n "
" \t :e - enables passing an environment to the program. Without :e, the "
" current environment is inherited. \n "
" \t :p - allows searching the current PATH for the binary to execute. "
2020-08-29 15:27:32 +00:00
" Without this flag, binaries must use absolute paths. \n "
2020-09-02 02:36:49 +00:00
" \t :x - raise error if exit code is non-zero. \n "
" env is a table or struct mapping environment variables to values. It can also "
" contain the keys :in, :out, and :err, which allow redirecting stdio in the subprocess. "
" These arguments should be core/file values. "
2020-09-13 00:48:12 +00:00
" One can also pass in the :pipe keyword "
" for these arguments to create files that will read (for :err and :out) or write (for :in) "
" to the file descriptor of the subprocess. This is only useful in os/spawn, which takes "
" the same parameters as os/execute, but will return an object that contains references to these "
" files via (return-value :in), (return-value :out), and (return-value :err). "
2020-09-04 13:09:05 +00:00
" Returns the exit status of the program. " )
} ,
2020-09-04 19:54:58 +00:00
{
2020-09-04 13:09:05 +00:00
" os/spawn " , os_spawn ,
JDOC ( " (os/spawn args &opts flags env) \n \n "
" Execute a program on the system and return a handle to the process. Otherwise, the "
2020-09-04 19:54:58 +00:00
" same arguments as os/execute. Does not wait for the process. " )
2018-11-16 07:34:50 +00:00
} ,
2019-01-06 06:49:56 +00:00
{
" os/shell " , os_shell ,
JDOC ( " (os/shell str) \n \n "
2019-02-20 01:51:34 +00:00
" Pass a command string str directly to the system shell. " )
2018-11-16 07:34:50 +00:00
} ,
2020-09-07 21:08:43 +00:00
{
" os/proc-wait " , os_proc_wait ,
JDOC ( " (os/proc-wait proc) \n \n "
" Block until the subprocess completes. Returns the subprocess return code. " )
} ,
{
" os/proc-kill " , os_proc_kill ,
JDOC ( " (os/proc-kill proc &opt wait) \n \n "
" Kill a subprocess by sending SIGKILL to it on posix systems, or by closing the process "
" handle on windows. If wait is truthy, will wait for the process to finsih and "
" returns the exit code. Otherwise, returns proc. " )
} ,
2020-05-09 17:00:01 +00:00
# endif
2019-01-06 06:49:56 +00:00
{
" os/setenv " , os_setenv ,
JDOC ( " (os/setenv variable value) \n \n "
2019-02-20 01:51:34 +00:00
" Set an environment variable. " )
2018-11-16 07:34:50 +00:00
} ,
2019-01-06 06:49:56 +00:00
{
" os/time " , os_time ,
JDOC ( " (os/time) \n \n "
2019-02-20 01:51:34 +00:00
" Get the current time expressed as the number of seconds since "
" January 1, 1970, the Unix epoch. Returns a real number. " )
2018-11-16 07:34:50 +00:00
} ,
2020-03-18 18:52:25 +00:00
{
" os/mktime " , os_mktime ,
JDOC ( " (os/mktime date-struct &opt local) \n \n "
" Get the broken down date-struct time expressed as the number "
" of seconds since January 1, 1970, the Unix epoch. "
" Returns a real number. "
" Date is given in UTC unless local is truthy, in which case the "
" date is computed for the local timezone. \n \n "
" Inverse function to os/date. " )
} ,
2019-01-06 06:49:56 +00:00
{
" os/clock " , os_clock ,
JDOC ( " (os/clock) \n \n "
2019-02-20 01:51:34 +00:00
" Return the number of seconds since some fixed point in time. The clock "
" is guaranteed to be non decreasing in real time. " )
2018-11-16 07:34:50 +00:00
} ,
2019-01-06 06:49:56 +00:00
{
" os/sleep " , os_sleep ,
2020-11-02 15:09:22 +00:00
JDOC ( " (os/sleep n) \n \n "
" Suspend the program for n seconds. 'nsec' can be a real number. Returns "
2019-02-20 01:51:34 +00:00
" nil. " )
2018-11-16 07:34:50 +00:00
} ,
2019-01-06 06:49:56 +00:00
{
" os/cwd " , os_cwd ,
JDOC ( " (os/cwd) \n \n "
2019-02-20 01:51:34 +00:00
" Returns the current working directory. " )
2018-11-16 07:34:50 +00:00
} ,
2019-12-03 22:29:11 +00:00
{
" os/cryptorand " , os_cryptorand ,
JDOC ( " (os/cryptorand n &opt buf) \n \n "
2020-04-03 20:02:12 +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
} ,
2019-01-20 19:34:33 +00:00
{
" os/date " , os_date ,
2019-11-09 22:57:21 +00:00
JDOC ( " (os/date &opt time local) \n \n "
2019-02-20 01:51:34 +00:00
" Returns the given time as a date struct, or the current time if no time is given. "
2019-11-09 22:57:21 +00:00
" Returns a struct with following key values. Note that all numbers are 0-indexed. "
2020-04-03 20:02:12 +00:00
" Date is given in UTC unless local is truthy, in which case the date is formatted for "
2019-11-09 22:57:21 +00:00
" the local timezone. \n \n "
2019-02-20 01:51:34 +00:00
" \t :seconds - number of seconds [0-61] \n "
" \t :minutes - number of minutes [0-59] \n "
2019-04-24 02:43:51 +00:00
" \t :hours - number of hours [0-23] \n "
2019-02-20 01:51:34 +00:00
" \t :month-day - day of month [0-30] \n "
" \t :month - month of year [0, 11] \n "
" \t :year - years since year 0 (e.g. 2019) \n "
" \t :week-day - day of the week [0-6] \n "
" \t :year-day - day of the year [0-365] \n "
" \t :dst - If Day Light Savings is in effect " )
2019-01-20 19:34:33 +00:00
} ,
2019-05-29 15:31:19 +00:00
{
" os/rename " , os_rename ,
JDOC ( " (os/rename oldname newname) \n \n "
" Rename a file on disk to a new path. Returns nil. " )
} ,
2020-03-25 00:45:57 +00:00
{
" os/realpath " , os_realpath ,
JDOC ( " (os/realpath path) \n \n "
2020-04-01 13:54:01 +00:00
" Get the absolute path for a given path, following ../, ./, and symlinks. "
" Returns an absolute path as a string. Will raise an error on Windows. " )
2020-03-25 00:45:57 +00:00
} ,
2020-04-03 20:02:12 +00:00
{
2020-04-03 20:23:29 +00:00
" os/perm-string " , os_permission_string ,
JDOC ( " (os/perm-string int) \n \n "
2020-04-03 20:02:12 +00:00
" Convert a Unix octal permission value from a permission integer as returned by os/stat "
" to a human readable string, that follows the formatting "
" of unix tools like ls. Returns the string as a 9 character string of r, w, x and - characters. Does not "
" include the file/directory/symlink character as rendered by `ls`. " )
} ,
{
" os/perm-int " , os_permission_int ,
JDOC ( " (os/perm-int bytes) \n \n "
" Parse a 9 character permission string and return an integer that can be used by chmod. " )
} ,
2020-11-15 15:56:19 +00:00
# ifdef JANET_EV
{
" os/open " , os_open ,
JDOC ( " (os/open path &opt flags mode) \n \n "
" Create a stream from a file, like the POSIX open system call. Returns a new stream. "
" mode should be a file mode as passed to os/chmod, but only if the create flag is given. "
" The default mode is 8r666. "
" Allowed flags are as follows: \n \n "
2020-12-28 17:00:15 +00:00
" * :r - open this file for reading \n "
" * :w - open this file for writing \n "
" * :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 "
" Posix only flags: \n \n "
" * :a - append to a file (O_APPEND) \n "
" * :x - O_SYNC \n "
" * :C - O_NOCTTY \n \n "
" Windows only flags: \n \n "
" * :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
} ,
{
" os/pipe " , os_pipe ,
JDOC ( " (os/pipe) \n \n "
" Create a readable stream and a writable stream that are connected. Returns a two element "
" tuple where the first element is a readable stream and the second element is the writable "
" stream. " )
} ,
# endif
2019-03-29 03:22:58 +00:00
# endif
2018-11-15 20:45:41 +00:00
{ NULL , NULL , NULL }
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 . */
if ( ! env_lock_initialized ) {
2020-01-12 16:18:03 +00:00
InitializeCriticalSection ( & env_lock ) ;
2020-01-07 01:30:44 +00:00
env_lock_initialized = 1 ;
}
2020-11-29 21:36:21 +00:00
# endif
# ifndef JANET_NO_PROCESSES
2020-01-07 01:30:44 +00:00
# endif
2019-02-08 05:44:30 +00:00
janet_core_cfuns ( env , NULL , os_cfuns ) ;
2018-03-29 00:50:20 +00:00
}