2018-03-29 00:50:20 +00:00
/*
2020-01-12 16:50:37 +00:00
* Copyright ( c ) 2020 Calvin Rose
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
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
2019-05-30 22:40:10 +00:00
/* Free memory from os_execute */
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-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 ;
HANDLE tHandle ;
2020-09-02 01:06:35 +00:00
# else
int pid ;
# endif
int return_code ;
JanetFile * in ;
JanetFile * out ;
JanetFile * err ;
} JanetProc ;
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-09-02 02:36:49 +00:00
static Janet os_proc_wait_impl ( JanetProc * proc ) {
if ( proc - > flags & JANET_PROC_WAITED ) {
janet_panicf ( " cannot wait on process that has already finished " ) ;
2020-09-02 01:06:35 +00:00
}
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 ) ;
if ( ! ( proc - > flags & JANET_PROC_CLOSED ) ) {
proc - > flags | = JANET_PROC_CLOSED ;
CloseHandle ( proc - > pHandle ) ;
CloseHandle ( proc - > tHandle ) ;
}
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-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 ) ;
return os_proc_wait_impl ( proc ) ;
}
static Janet os_proc_kill ( int32_t argc , Janet * argv ) {
janet_arity ( argc , 1 , 2 ) ;
JanetProc * proc = janet_getabstract ( argv , 0 , & ProcAT ) ;
# 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:07:45 +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 ] ) ) {
return os_proc_wait_impl ( proc ) ;
} else {
return argv [ 0 ] ;
}
}
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 } ,
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 " ) ) {
* out = ( NULL = = proc - > out ) ? janet_wrap_nil ( ) : janet_wrap_abstract ( proc - > err ) ;
return 1 ;
}
if ( ( - 1 ! = proc - > return_code ) & & janet_keyeq ( key , " return-code " ) ) {
* out = janet_wrap_integer ( proc - > return_code ) ;
return 1 ;
}
if ( ! janet_checktype ( key , JANET_KEYWORD ) ) return 0 ;
return janet_getmethod ( janet_unwrap_keyword ( key ) , proc_methods , out ) ;
}
static const JanetAbstractType ProcAT = {
" core/process " ,
NULL ,
janet_proc_mark ,
janet_proc_get ,
JANET_ATEND_GET
} ;
2019-05-31 17:43:38 +00:00
static Janet os_execute ( int32_t argc , Janet * argv ) {
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-02 01:06:35 +00:00
flags = janet_getflags ( argv , 1 , " epxa " ) ;
2019-06-12 16:05:01 +00:00
}
2019-05-30 22:40:10 +00:00
/* Get environment */
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 */
JanetFile * new_in = NULL , * new_out = NULL , * new_err = NULL ;
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 " ) ) ;
if ( ! janet_checktype ( maybe_stdin , JANET_NIL ) ) new_in = janet_getjfile ( & maybe_stdin , 0 ) ;
if ( ! janet_checktype ( maybe_stdout , JANET_NIL ) ) new_out = janet_getjfile ( & maybe_stdout , 0 ) ;
if ( ! janet_checktype ( maybe_stderr , JANET_NIL ) ) new_err = janet_getjfile ( & maybe_stderr , 0 ) ;
}
2019-05-31 17:45:39 +00:00
/* Result */
int status = 0 ;
2020-09-02 01:06:35 +00:00
int is_async = janet_flag_at ( flags , 3 ) ;
2019-05-31 17:45:39 +00:00
2019-05-30 22:40:10 +00:00
# ifdef JANET_WINDOWS
2020-09-03 00:07:45 +00:00
HANDLE pHandle , tHandle ;
PROCESS_INFORMATION processInfo ;
STARTUPINFO startupInfo ;
memset ( & processInfo , 0 , sizeof ( processInfo ) ) ;
memset ( & startupInfo , 0 , sizeof ( startupInfo ) ) ;
startupInfo . cb = sizeof ( startupInfo ) ;
startupInfo . dwFlags | = STARTF_USESTDHANDLES ;
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
/* Do IO redirection */
startupInfo . hStdInput = ( HANDLE ) _get_osfhandle ( ( new_in = = NULL ) ? 0 : _fileno ( new_in - > file ) ) ;
startupInfo . hStdOutput = ( HANDLE ) _get_osfhandle ( ( new_out = = NULL ) ? 1 : _fileno ( new_out - > file ) ) ;
startupInfo . hStdError = ( HANDLE ) _get_osfhandle ( ( new_err = = NULL ) ? 2 : _fileno ( new_err - > file ) ) ;
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-03 00:07:45 +00:00
/* TODO - redirection, :p flag */
if ( ! CreateProcess ( janet_flag_at ( flags , 1 ) ? NULL : path , /* NULL? */
( char * ) buf - > data , /* Single CLI argument */
NULL , /* no proc inheritance */
NULL , /* no thread inheritance */
TRUE , /* handle inheritance */
0 , /* flags */
envp , /* pass in environment */
NULL , /* use parents starting directory */
& startupInfo ,
& processInfo ) ) {
janet_panic ( " failed to create process " ) ;
2018-07-07 01:50:59 +00:00
}
2020-09-03 00:07:45 +00:00
pHandle = processInfo . hProcess ;
tHandle = processInfo . hThread ;
2019-05-31 19:02:44 +00:00
2020-09-03 00:07:45 +00:00
os_execute_cleanup ( envp , NULL ) ;
2020-09-02 01:06:35 +00:00
2020-09-03 00:07:45 +00:00
/* Wait and cleanup immedaitely */
if ( ! is_async ) {
DWORD code ;
WaitForSingleObject ( pHandle , INFINITE ) ;
GetExitCodeProcess ( pHandle , & code ) ;
status = ( int ) code ;
CloseHandle ( pHandle ) ;
CloseHandle ( tHandle ) ;
2020-09-02 01:06:35 +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
int use_environ = ! janet_flag_at ( flags , 0 ) ;
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 ) ;
if ( new_in ! = NULL ) {
posix_spawn_file_actions_adddup2 ( & actions , fileno ( new_in - > file ) , 0 ) ;
}
if ( new_out ! = NULL ) {
posix_spawn_file_actions_adddup2 ( & actions , fileno ( new_out - > file ) , 1 ) ;
}
if ( new_err ! = NULL ) {
posix_spawn_file_actions_adddup2 ( & actions , fileno ( new_err - > file ) , 2 ) ;
}
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-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 ) ) ;
2020-09-02 01:06:35 +00:00
} else if ( janet_flag_at ( flags , 3 ) ) {
/* Get process handle */
os_execute_cleanup ( envp , child_argv ) ;
2019-05-30 22:40:10 +00:00
} else {
2020-09-02 01:06:35 +00:00
/* Wait to complete */
2019-05-30 22:40:10 +00:00
waitpid ( pid , & status , 0 ) ;
2020-09-02 01:06:35 +00:00
os_execute_cleanup ( envp , child_argv ) ;
/* Use POSIX shell semantics for interpreting signals */
if ( WIFEXITED ( status ) ) {
status = WEXITSTATUS ( status ) ;
} else if ( WIFSTOPPED ( status ) ) {
status = WSTOPSIG ( status ) + 128 ;
} else {
status = WTERMSIG ( status ) + 128 ;
}
2019-05-30 22:40:10 +00:00
}
2018-07-07 01:50:59 +00:00
# endif
2020-09-02 01:06:35 +00:00
if ( is_async ) {
JanetProc * proc = janet_abstract ( & ProcAT , sizeof ( JanetProc ) ) ;
proc - > return_code = - 1 ;
2020-09-03 00:07:45 +00:00
# ifdef JANET_WINDOWS
proc - > pHandle = pHandle ;
proc - > tHandle = tHandle ;
# else
proc - > pid = pid ;
# endif
2020-09-02 01:06:35 +00:00
proc - > in = new_in ;
proc - > out = new_out ;
proc - > err = new_err ;
2020-09-02 02:36:49 +00:00
proc - > flags = 0 ;
2020-09-02 01:06:35 +00:00
return janet_wrap_abstract ( proc ) ;
} else if ( janet_flag_at ( flags , 2 ) & & status ) {
2020-08-29 15:27:32 +00:00
janet_panicf ( " command failed with non-zero exit code %d " , status ) ;
2020-09-02 01:06:35 +00:00
} else {
return janet_wrap_integer ( status ) ;
2020-08-29 15:27:32 +00:00
}
2019-05-30 22:40:10 +00:00
}
2018-07-07 01:50:59 +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 ) ) ;
}
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 "
" \t :a - Runs the process asynchronously and returns a core/process. \n \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, or a core/process object if the :a flag is given. " )
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-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 ,
JDOC ( " (os/sleep nsec) \n \n "
2019-02-20 01:51:34 +00:00
" Suspend the program for nsec seconds. 'nsec' can be a real number. Returns "
" 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-09-02 01:06:35 +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. " )
} ,
2020-09-02 02:36:49 +00:00
{
" 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. " )
} ,
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 ;
}
# endif
2019-02-08 05:44:30 +00:00
janet_core_cfuns ( env , NULL , os_cfuns ) ;
2018-03-29 00:50:20 +00:00
}