2019-05-24 18:27:18 -04:00
const std = @import ( " std.zig " ) ;
2021-10-04 23:47:27 -07:00
const builtin = @import ( " builtin " ) ;
2019-05-25 13:07:44 -04:00
const os = std . os ;
2019-05-26 23:35:26 -04:00
const fs = std . fs ;
2019-05-24 18:27:18 -04:00
const BufMap = std . BufMap ;
const mem = std . mem ;
2019-05-26 13:17:34 -04:00
const math = std . math ;
2019-05-24 18:27:18 -04:00
const Allocator = mem . Allocator ;
const assert = std . debug . assert ;
const testing = std . testing ;
2020-12-26 13:50:26 -07:00
const child_process = @import ( " child_process.zig " ) ;
2019-05-24 18:27:18 -04:00
2019-05-25 13:07:44 -04:00
pub const abort = os . abort ;
pub const exit = os . exit ;
2019-05-26 13:17:34 -04:00
pub const changeCurDir = os . chdir ;
pub const changeCurDirC = os . chdirC ;
2019-05-24 18:27:18 -04:00
2019-05-26 23:35:26 -04:00
/// The result is a slice of `out_buffer`, from index `0`.
2020-03-27 14:28:48 -05:00
pub fn getCwd ( out_buffer : [ ] u8 ) ! [ ] u8 {
2019-05-26 23:35:26 -04:00
return os . getcwd ( out_buffer ) ;
}
/// Caller must free the returned memory.
2021-10-29 00:37:25 +01:00
pub fn getCwdAlloc ( allocator : Allocator ) ! [ ] u8 {
2020-03-28 00:12:40 -05:00
// The use of MAX_PATH_BYTES here is just a heuristic: most paths will fit
// in stack_buf, avoiding an extra allocation in the common case.
var stack_buf : [ fs . MAX_PATH_BYTES ] u8 = undefined ;
var heap_buf : ? [ ] u8 = null ;
defer if ( heap_buf ) | buf | allocator . free ( buf ) ;
var current_buf : [ ] u8 = & stack_buf ;
while ( true ) {
if ( os . getcwd ( current_buf ) ) | slice | {
2020-07-04 13:44:28 +02:00
return allocator . dupe ( u8 , slice ) ;
2020-05-29 16:39:47 -04:00
} else | err | switch ( err ) {
2020-03-28 00:12:40 -05:00
error . NameTooLong = > {
// The path is too long to fit in stack_buf. Allocate geometrically
// increasing buffers until we find one that works
const new_capacity = current_buf . len * 2 ;
if ( heap_buf ) | buf | allocator . free ( buf ) ;
current_buf = try allocator . alloc ( u8 , new_capacity ) ;
heap_buf = current_buf ;
} ,
2020-05-29 16:39:47 -04:00
else = > | e | return e ,
2020-03-28 00:12:40 -05:00
}
}
2019-05-26 23:35:26 -04:00
}
test " getCwdAlloc " {
Add/fix missing WASI functionality to pass libstd tests
This rather large commit adds/fixes missing WASI functionality
in `libstd` needed to pass the `libstd` tests. As such, now by
default tests targeting `wasm32-wasi` target are enabled in
`test/tests.zig` module. However, they can be disabled by passing
the `-Dskip-wasi=true` flag when invoking the `zig build test`
command. When the flag is set to `false`, i.e., when WASI tests are
included, `wasmtime` with `--dir=.` is used as the default testing
command.
Since the majority of `libstd` tests were relying on `fs.cwd()`
call to get current working directory handle wrapped in `Dir`
struct, in order to make the tests WASI-friendly, `fs.cwd()`
call was replaced with `testing.getTestDir()` function which
resolved to either `fs.cwd()` for non-WASI targets, or tries to
fetch the preopen list from the WASI runtime and extract a
preopen for '.' path.
The summary of changes introduced by this commit:
* implement `Dir.makeDir` and `Dir.openDir` targeting WASI
* implement `Dir.deleteFile` and `Dir.deleteDir` targeting WASI
* fix `os.close` and map errors in `unlinkat`
* move WASI-specific `mkdirat` and `unlinkat` from `std.fs.wasi`
to `std.os` module
* implement `lseek_{SET, CUR, END}` targeting WASI
* implement `futimens` targeting WASI
* implement `ftruncate` targeting WASI
* implement `readv`, `writev`, `pread{v}`, `pwrite{v}` targeting WASI
* make sure ANSI escape codes are _not_ used in stderr or stdout
in WASI, as WASI always sanitizes stderr, and sanitizes stdout if
fd is a TTY
* fix specifying WASI rights when opening/creating files/dirs
* tweak `AtomicFile` to be WASI-compatible
* implement `os.renameatWasi` for WASI-compliant `os.renameat` function
* implement sleep() targeting WASI
* fix `process.getEnvMap` targeting WASI
2020-05-05 17:23:49 +02:00
if ( builtin . os . tag == . wasi ) return error . SkipZigTest ;
2020-01-31 19:06:50 -06:00
const cwd = try getCwdAlloc ( testing . allocator ) ;
testing . allocator . free ( cwd ) ;
2019-05-26 23:35:26 -04:00
}
2020-02-22 15:59:13 -05:00
/// Caller owns resulting `BufMap`.
2021-10-29 00:37:25 +01:00
pub fn getEnvMap ( allocator : Allocator ) ! BufMap {
2019-05-24 18:27:18 -04:00
var result = BufMap . init ( allocator ) ;
errdefer result . deinit ( ) ;
2020-02-25 01:52:27 -05:00
if ( builtin . os . tag == . windows ) {
2020-02-22 17:49:52 -05:00
const ptr = os . windows . peb ( ) . ProcessParameters . Environment ;
2019-05-24 18:27:18 -04:00
var i : usize = 0 ;
2020-02-22 17:35:36 -05:00
while ( ptr [ i ] != 0 ) {
2019-05-24 18:27:18 -04:00
const key_start = i ;
while ( ptr [ i ] != 0 and ptr [ i ] != '=' ) : ( i += 1 ) { }
const key_w = ptr [ key_start .. i ] ;
const key = try std . unicode . utf16leToUtf8Alloc ( allocator , key_w ) ;
errdefer allocator . free ( key ) ;
if ( ptr [ i ] == '=' ) i += 1 ;
const value_start = i ;
while ( ptr [ i ] != 0 ) : ( i += 1 ) { }
const value_w = ptr [ value_start .. i ] ;
const value = try std . unicode . utf16leToUtf8Alloc ( allocator , value_w ) ;
errdefer allocator . free ( value ) ;
i += 1 ; // skip over null byte
2021-06-03 15:39:26 -05:00
try result . putMove ( key , value ) ;
2019-05-24 18:27:18 -04:00
}
2020-02-22 17:35:36 -05:00
return result ;
2021-07-27 08:59:34 +09:00
} else if ( builtin . os . tag == . wasi and ! builtin . link_libc ) {
2019-05-24 18:27:18 -04:00
var environ_count : usize = undefined ;
var environ_buf_size : usize = undefined ;
2019-05-25 13:07:44 -04:00
const environ_sizes_get_ret = os . wasi . environ_sizes_get ( & environ_count , & environ_buf_size ) ;
2021-08-23 17:06:56 -07:00
if ( environ_sizes_get_ret != . SUCCESS ) {
2019-05-25 13:07:44 -04:00
return os . unexpectedErrno ( environ_sizes_get_ret ) ;
2019-05-24 18:27:18 -04:00
}
Add/fix missing WASI functionality to pass libstd tests
This rather large commit adds/fixes missing WASI functionality
in `libstd` needed to pass the `libstd` tests. As such, now by
default tests targeting `wasm32-wasi` target are enabled in
`test/tests.zig` module. However, they can be disabled by passing
the `-Dskip-wasi=true` flag when invoking the `zig build test`
command. When the flag is set to `false`, i.e., when WASI tests are
included, `wasmtime` with `--dir=.` is used as the default testing
command.
Since the majority of `libstd` tests were relying on `fs.cwd()`
call to get current working directory handle wrapped in `Dir`
struct, in order to make the tests WASI-friendly, `fs.cwd()`
call was replaced with `testing.getTestDir()` function which
resolved to either `fs.cwd()` for non-WASI targets, or tries to
fetch the preopen list from the WASI runtime and extract a
preopen for '.' path.
The summary of changes introduced by this commit:
* implement `Dir.makeDir` and `Dir.openDir` targeting WASI
* implement `Dir.deleteFile` and `Dir.deleteDir` targeting WASI
* fix `os.close` and map errors in `unlinkat`
* move WASI-specific `mkdirat` and `unlinkat` from `std.fs.wasi`
to `std.os` module
* implement `lseek_{SET, CUR, END}` targeting WASI
* implement `futimens` targeting WASI
* implement `ftruncate` targeting WASI
* implement `readv`, `writev`, `pread{v}`, `pwrite{v}` targeting WASI
* make sure ANSI escape codes are _not_ used in stderr or stdout
in WASI, as WASI always sanitizes stderr, and sanitizes stdout if
fd is a TTY
* fix specifying WASI rights when opening/creating files/dirs
* tweak `AtomicFile` to be WASI-compatible
* implement `os.renameatWasi` for WASI-compliant `os.renameat` function
* implement sleep() targeting WASI
* fix `process.getEnvMap` targeting WASI
2020-05-05 17:23:49 +02:00
var environ = try allocator . alloc ( [ * : 0 ] u8 , environ_count ) ;
2019-05-24 18:27:18 -04:00
defer allocator . free ( environ ) ;
2020-01-29 21:46:06 -06:00
var environ_buf = try allocator . alloc ( u8 , environ_buf_size ) ;
2019-05-24 18:27:18 -04:00
defer allocator . free ( environ_buf ) ;
2019-05-25 13:07:44 -04:00
const environ_get_ret = os . wasi . environ_get ( environ . ptr , environ_buf . ptr ) ;
2021-08-23 17:06:56 -07:00
if ( environ_get_ret != . SUCCESS ) {
2019-05-25 13:07:44 -04:00
return os . unexpectedErrno ( environ_get_ret ) ;
2019-05-24 18:27:18 -04:00
}
for ( environ ) | env | {
2021-11-30 00:13:07 -07:00
const pair = mem . sliceTo ( env , 0 ) ;
2021-08-06 02:01:47 -07:00
var parts = mem . split ( u8 , pair , " = " ) ;
Add/fix missing WASI functionality to pass libstd tests
This rather large commit adds/fixes missing WASI functionality
in `libstd` needed to pass the `libstd` tests. As such, now by
default tests targeting `wasm32-wasi` target are enabled in
`test/tests.zig` module. However, they can be disabled by passing
the `-Dskip-wasi=true` flag when invoking the `zig build test`
command. When the flag is set to `false`, i.e., when WASI tests are
included, `wasmtime` with `--dir=.` is used as the default testing
command.
Since the majority of `libstd` tests were relying on `fs.cwd()`
call to get current working directory handle wrapped in `Dir`
struct, in order to make the tests WASI-friendly, `fs.cwd()`
call was replaced with `testing.getTestDir()` function which
resolved to either `fs.cwd()` for non-WASI targets, or tries to
fetch the preopen list from the WASI runtime and extract a
preopen for '.' path.
The summary of changes introduced by this commit:
* implement `Dir.makeDir` and `Dir.openDir` targeting WASI
* implement `Dir.deleteFile` and `Dir.deleteDir` targeting WASI
* fix `os.close` and map errors in `unlinkat`
* move WASI-specific `mkdirat` and `unlinkat` from `std.fs.wasi`
to `std.os` module
* implement `lseek_{SET, CUR, END}` targeting WASI
* implement `futimens` targeting WASI
* implement `ftruncate` targeting WASI
* implement `readv`, `writev`, `pread{v}`, `pwrite{v}` targeting WASI
* make sure ANSI escape codes are _not_ used in stderr or stdout
in WASI, as WASI always sanitizes stderr, and sanitizes stdout if
fd is a TTY
* fix specifying WASI rights when opening/creating files/dirs
* tweak `AtomicFile` to be WASI-compatible
* implement `os.renameatWasi` for WASI-compliant `os.renameat` function
* implement sleep() targeting WASI
* fix `process.getEnvMap` targeting WASI
2020-05-05 17:23:49 +02:00
const key = parts . next ( ) .? ;
const value = parts . next ( ) .? ;
2021-06-03 15:39:26 -05:00
try result . put ( key , value ) ;
2019-05-24 18:27:18 -04:00
}
return result ;
2020-02-22 15:59:13 -05:00
} else if ( builtin . link_libc ) {
var ptr = std . c . environ ;
while ( ptr .* ) | line | : ( ptr += 1 ) {
var line_i : usize = 0 ;
while ( line [ line_i ] != 0 and line [ line_i ] != '=' ) : ( line_i += 1 ) { }
const key = line [ 0 .. line_i ] ;
var end_i : usize = line_i ;
while ( line [ end_i ] != 0 ) : ( end_i += 1 ) { }
const value = line [ line_i + 1 .. end_i ] ;
2021-06-03 15:39:26 -05:00
try result . put ( key , value ) ;
2020-02-22 15:59:13 -05:00
}
return result ;
2019-05-24 18:27:18 -04:00
} else {
2020-02-22 15:59:13 -05:00
for ( os . environ ) | line | {
2019-05-24 18:27:18 -04:00
var line_i : usize = 0 ;
2020-02-22 15:59:13 -05:00
while ( line [ line_i ] != 0 and line [ line_i ] != '=' ) : ( line_i += 1 ) { }
const key = line [ 0 .. line_i ] ;
2019-05-24 18:27:18 -04:00
var end_i : usize = line_i ;
2020-02-22 15:59:13 -05:00
while ( line [ end_i ] != 0 ) : ( end_i += 1 ) { }
const value = line [ line_i + 1 .. end_i ] ;
2019-05-24 18:27:18 -04:00
2021-06-03 15:39:26 -05:00
try result . put ( key , value ) ;
2019-05-24 18:27:18 -04:00
}
return result ;
}
}
test " os.getEnvMap " {
2020-01-29 21:22:01 -06:00
var env = try getEnvMap ( std . testing . allocator ) ;
2019-05-24 18:27:18 -04:00
defer env . deinit ( ) ;
}
pub const GetEnvVarOwnedError = error {
OutOfMemory ,
EnvironmentVariableNotFound ,
/// See https://github.com/ziglang/zig/issues/1774
InvalidUtf8 ,
} ;
/// Caller must free returned memory.
2021-10-29 00:37:25 +01:00
pub fn getEnvVarOwned ( allocator : mem . Allocator , key : [ ] const u8 ) GetEnvVarOwnedError ! [ ] u8 {
2020-02-25 01:52:27 -05:00
if ( builtin . os . tag == . windows ) {
2020-02-22 17:35:36 -05:00
const result_w = blk : {
const key_w = try std . unicode . utf8ToUtf16LeWithNull ( allocator , key ) ;
defer allocator . free ( key_w ) ;
break : blk std . os . getenvW ( key_w ) orelse return error . EnvironmentVariableNotFound ;
} ;
return std . unicode . utf16leToUtf8Alloc ( allocator , result_w ) catch | err | switch ( err ) {
error . DanglingSurrogateHalf = > return error . InvalidUtf8 ,
error . ExpectedSecondSurrogateHalf = > return error . InvalidUtf8 ,
error . UnexpectedSecondSurrogateHalf = > return error . InvalidUtf8 ,
else = > | e | return e ,
} ;
2019-05-24 18:27:18 -04:00
} else {
2019-05-26 13:17:34 -04:00
const result = os . getenv ( key ) orelse return error . EnvironmentVariableNotFound ;
2020-07-04 13:44:28 +02:00
return allocator . dupe ( u8 , result ) ;
2019-05-24 18:27:18 -04:00
}
}
2021-06-21 13:47:38 -05:00
pub fn hasEnvVarConstant ( comptime key : [ ] const u8 ) bool {
if ( builtin . os . tag == . windows ) {
const key_w = comptime std . unicode . utf8ToUtf16LeStringLiteral ( key ) ;
return std . os . getenvW ( key_w ) != null ;
} else {
return os . getenv ( key ) != null ;
}
}
2021-10-29 00:37:25 +01:00
pub fn hasEnvVar ( allocator : Allocator , key : [ ] const u8 ) error { OutOfMemory } ! bool {
2021-06-21 13:47:38 -05:00
if ( builtin . os . tag == . windows ) {
var stack_alloc = std . heap . stackFallback ( 256 * @sizeOf ( u16 ) , allocator ) ;
2021-10-29 00:37:25 +01:00
const key_w = try std . unicode . utf8ToUtf16LeWithNull ( stack_alloc . get ( ) , key ) ;
2021-06-21 13:47:38 -05:00
defer stack_alloc . allocator . free ( key_w ) ;
return std . os . getenvW ( key_w ) != null ;
} else {
return os . getenv ( key ) != null ;
}
}
2019-05-24 18:27:18 -04:00
test " os.getEnvVarOwned " {
2020-01-29 21:22:01 -06:00
var ga = std . testing . allocator ;
2021-05-04 20:47:26 +03:00
try testing . expectError ( error . EnvironmentVariableNotFound , getEnvVarOwned ( ga , " BADENV " ) ) ;
2019-05-24 18:27:18 -04:00
}
pub const ArgIteratorPosix = struct {
index : usize ,
count : usize ,
2022-01-30 11:27:52 -08:00
pub const InitError = error { } ;
2019-05-24 18:27:18 -04:00
pub fn init ( ) ArgIteratorPosix {
return ArgIteratorPosix {
. index = 0 ,
2019-05-26 13:17:34 -04:00
. count = os . argv . len ,
2019-05-24 18:27:18 -04:00
} ;
}
2020-10-22 17:52:48 -04:00
pub fn next ( self : * ArgIteratorPosix ) ? [ : 0 ] const u8 {
2019-05-24 18:27:18 -04:00
if ( self . index == self . count ) return null ;
2019-05-26 13:17:34 -04:00
const s = os . argv [ self . index ] ;
2019-05-24 18:27:18 -04:00
self . index += 1 ;
2021-11-30 00:13:07 -07:00
return mem . sliceTo ( s , 0 ) ;
2019-05-24 18:27:18 -04:00
}
pub fn skip ( self : * ArgIteratorPosix ) bool {
if ( self . index == self . count ) return false ;
self . index += 1 ;
return true ;
}
} ;
2020-05-20 19:42:15 +02:00
pub const ArgIteratorWasi = struct {
2021-10-29 00:37:25 +01:00
allocator : mem . Allocator ,
2020-05-20 19:42:15 +02:00
index : usize ,
2020-10-22 17:52:48 -04:00
args : [ ] [ : 0 ] u8 ,
2020-05-20 19:42:15 +02:00
pub const InitError = error { OutOfMemory } || os . UnexpectedError ;
/// You must call deinit to free the internal buffer of the
/// iterator after you are done.
2021-10-29 00:37:25 +01:00
pub fn init ( allocator : mem . Allocator ) InitError ! ArgIteratorWasi {
2020-05-20 19:42:15 +02:00
const fetched_args = try ArgIteratorWasi . internalInit ( allocator ) ;
return ArgIteratorWasi {
. allocator = allocator ,
. index = 0 ,
. args = fetched_args ,
} ;
}
2021-10-29 00:37:25 +01:00
fn internalInit ( allocator : mem . Allocator ) InitError ! [ ] [ : 0 ] u8 {
2020-05-20 19:42:15 +02:00
const w = os . wasi ;
var count : usize = undefined ;
var buf_size : usize = undefined ;
switch ( w . args_sizes_get ( & count , & buf_size ) ) {
2021-08-23 17:06:56 -07:00
. SUCCESS = > { } ,
2020-05-20 19:42:15 +02:00
else = > | err | return os . unexpectedErrno ( err ) ,
}
var argv = try allocator . alloc ( [ * : 0 ] u8 , count ) ;
defer allocator . free ( argv ) ;
var argv_buf = try allocator . alloc ( u8 , buf_size ) ;
switch ( w . args_get ( argv . ptr , argv_buf . ptr ) ) {
2021-08-23 17:06:56 -07:00
. SUCCESS = > { } ,
2020-05-20 19:42:15 +02:00
else = > | err | return os . unexpectedErrno ( err ) ,
}
2020-10-22 17:52:48 -04:00
var result_args = try allocator . alloc ( [ : 0 ] u8 , count ) ;
2020-05-20 19:42:15 +02:00
var i : usize = 0 ;
while ( i < count ) : ( i += 1 ) {
2021-11-30 00:13:07 -07:00
result_args [ i ] = mem . sliceTo ( argv [ i ] , 0 ) ;
2020-05-20 19:42:15 +02:00
}
return result_args ;
}
2020-10-22 17:52:48 -04:00
pub fn next ( self : * ArgIteratorWasi ) ? [ : 0 ] const u8 {
2020-05-20 19:42:15 +02:00
if ( self . index == self . args . len ) return null ;
const arg = self . args [ self . index ] ;
self . index += 1 ;
return arg ;
}
pub fn skip ( self : * ArgIteratorWasi ) bool {
if ( self . index == self . args . len ) return false ;
self . index += 1 ;
return true ;
}
/// Call to free the internal buffer of the iterator.
pub fn deinit ( self : * ArgIteratorWasi ) void {
const last_item = self . args [ self . args . len - 1 ] ;
const last_byte_addr = @ptrToInt ( last_item . ptr ) + last_item . len + 1 ; // null terminated
const first_item_ptr = self . args [ 0 ] . ptr ;
const len = last_byte_addr - @ptrToInt ( first_item_ptr ) ;
self . allocator . free ( first_item_ptr [ 0 .. len ] ) ;
self . allocator . free ( self . args ) ;
}
} ;
2022-01-30 11:27:52 -08:00
/// Optional parameters for `ArgIteratorGeneral`
pub const ArgIteratorGeneralOptions = struct {
comments_supported : bool = false ,
} ;
2019-05-24 18:27:18 -04:00
2022-01-30 11:27:52 -08:00
/// A general Iterator to parse a string into a set of arguments
pub fn ArgIteratorGeneral ( comptime options : ArgIteratorGeneralOptions ) type {
return struct {
allocator : Allocator ,
index : usize = 0 ,
cmd_line : [ ] const u8 ,
/// Should the cmd_line field be free'd (using the allocator) on deinit()?
free_cmd_line_on_deinit : bool ,
/// buffer MUST be long enough to hold the cmd_line plus a null terminator.
/// buffer will we free'd (using the allocator) on deinit()
buffer : [ ] u8 ,
start : usize = 0 ,
end : usize = 0 ,
pub const Self = @This ( ) ;
pub const InitError = error { OutOfMemory } ;
pub const InitUtf16leError = error { OutOfMemory , InvalidCmdLine } ;
/// cmd_line_utf8 MUST remain valid and constant while using this instance
pub fn init ( allocator : Allocator , cmd_line_utf8 : [ ] const u8 ) InitError ! Self {
var buffer = try allocator . alloc ( u8 , cmd_line_utf8 . len + 1 ) ;
errdefer allocator . free ( buffer ) ;
return Self {
. allocator = allocator ,
. cmd_line = cmd_line_utf8 ,
. free_cmd_line_on_deinit = false ,
. buffer = buffer ,
} ;
}
2019-05-24 18:27:18 -04:00
2022-01-30 11:27:52 -08:00
/// cmd_line_utf8 will be free'd (with the allocator) on deinit()
pub fn initTakeOwnership ( allocator : Allocator , cmd_line_utf8 : [ ] const u8 ) InitError ! Self {
var buffer = try allocator . alloc ( u8 , cmd_line_utf8 . len + 1 ) ;
errdefer allocator . free ( buffer ) ;
return Self {
. allocator = allocator ,
. cmd_line = cmd_line_utf8 ,
. free_cmd_line_on_deinit = true ,
. buffer = buffer ,
} ;
}
2019-05-24 18:27:18 -04:00
2022-01-30 11:27:52 -08:00
/// cmd_line_utf16le MUST be encoded UTF16-LE, and is converted to UTF-8 in an internal buffer
pub fn initUtf16le ( allocator : Allocator , cmd_line_utf16le : [ * : 0 ] const u16 ) InitUtf16leError ! Self {
var utf16le_slice = mem . sliceTo ( cmd_line_utf16le , 0 ) ;
var cmd_line = std . unicode . utf16leToUtf8Alloc ( allocator , utf16le_slice ) catch | err | switch ( err ) {
error . ExpectedSecondSurrogateHalf ,
error . DanglingSurrogateHalf ,
error . UnexpectedSecondSurrogateHalf ,
= > return error . InvalidCmdLine ,
error . OutOfMemory = > return error . OutOfMemory ,
} ;
errdefer allocator . free ( cmd_line ) ;
var buffer = try allocator . alloc ( u8 , cmd_line . len + 1 ) ;
errdefer allocator . free ( buffer ) ;
return Self {
. allocator = allocator ,
. cmd_line = cmd_line ,
. free_cmd_line_on_deinit = true ,
. buffer = buffer ,
} ;
}
2020-11-30 10:47:01 -08:00
2022-01-30 11:27:52 -08:00
// Skips over whitespace in the cmd_line.
// Returns false if the terminating sentinel is reached, true otherwise.
// Also skips over comments (if supported).
fn skipWhitespace ( self : * Self ) bool {
while ( true ) : ( self . index += 1 ) {
const character = if ( self . index != self . cmd_line . len ) self . cmd_line [ self . index ] else 0 ;
switch ( character ) {
0 = > return false ,
' ' , '\t' , '\r' , '\n' = > continue ,
'#' = > {
if ( options . comments_supported ) {
while ( true ) : ( self . index += 1 ) {
switch ( self . cmd_line [ self . index ] ) {
'\n' = > break ,
0 = > return false ,
else = > continue ,
}
}
continue ;
} else {
break ;
}
} ,
else = > break ,
}
2019-05-24 18:27:18 -04:00
}
2022-01-30 11:27:52 -08:00
return true ;
2019-05-24 18:27:18 -04:00
}
2022-01-30 11:27:52 -08:00
pub fn skip ( self : * Self ) bool {
if ( ! self . skipWhitespace ( ) ) {
return false ;
}
2019-05-24 18:27:18 -04:00
2022-01-30 11:27:52 -08:00
var backslash_count : usize = 0 ;
var in_quote = false ;
while ( true ) : ( self . index += 1 ) {
const character = if ( self . index != self . cmd_line . len ) self . cmd_line [ self . index ] else 0 ;
switch ( character ) {
0 = > return true ,
'"' = > {
const quote_is_real = backslash_count % 2 == 0 ;
if ( quote_is_real ) {
in_quote = ! in_quote ;
}
} ,
'\\' = > {
backslash_count += 1 ;
} ,
' ' , '\t' , '\r' , '\n' = > {
if ( ! in_quote ) {
return true ;
}
backslash_count = 0 ;
} ,
else = > {
backslash_count = 0 ;
continue ;
} ,
}
2019-05-24 18:27:18 -04:00
}
}
2022-01-30 11:27:52 -08:00
/// Returns a slice of the internal buffer that contains the next argument.
/// Returns null when it reaches the end.
pub fn next ( self : * Self ) ? [ : 0 ] const u8 {
if ( ! self . skipWhitespace ( ) ) {
return null ;
}
var backslash_count : usize = 0 ;
var in_quote = false ;
while ( true ) : ( self . index += 1 ) {
const character = if ( self . index != self . cmd_line . len ) self . cmd_line [ self . index ] else 0 ;
switch ( character ) {
0 = > {
self . emitBackslashes ( backslash_count ) ;
self . buffer [ self . end ] = 0 ;
var token = self . buffer [ self . start .. self . end : 0 ] ;
self . end += 1 ;
self . start = self . end ;
return token ;
} ,
'"' = > {
const quote_is_real = backslash_count % 2 == 0 ;
self . emitBackslashes ( backslash_count / 2 ) ;
backslash_count = 0 ;
if ( quote_is_real ) {
in_quote = ! in_quote ;
} else {
self . emitCharacter ( '"' ) ;
}
} ,
'\\' = > {
backslash_count += 1 ;
} ,
' ' , '\t' , '\r' , '\n' = > {
self . emitBackslashes ( backslash_count ) ;
backslash_count = 0 ;
if ( in_quote ) {
self . emitCharacter ( character ) ;
} else {
self . buffer [ self . end ] = 0 ;
var token = self . buffer [ self . start .. self . end : 0 ] ;
self . end += 1 ;
self . start = self . end ;
return token ;
}
} ,
else = > {
self . emitBackslashes ( backslash_count ) ;
backslash_count = 0 ;
self . emitCharacter ( character ) ;
} ,
}
2019-05-24 18:27:18 -04:00
}
}
2022-01-30 11:27:52 -08:00
fn emitBackslashes ( self : * Self , emit_count : usize ) void {
var i : usize = 0 ;
while ( i < emit_count ) : ( i += 1 ) {
self . emitCharacter ( '\\' ) ;
2019-05-24 18:27:18 -04:00
}
}
2022-01-30 11:27:52 -08:00
fn emitCharacter ( self : * Self , char : u8 ) void {
self . buffer [ self . end ] = char ;
self . end += 1 ;
}
2020-11-30 10:47:01 -08:00
2022-01-30 11:27:52 -08:00
/// Call to free the internal buffer of the iterator.
pub fn deinit ( self : * Self ) void {
self . allocator . free ( self . buffer ) ;
if ( self . free_cmd_line_on_deinit ) {
self . allocator . free ( self . cmd_line ) ;
}
2019-05-24 18:27:18 -04:00
}
2022-01-30 11:27:52 -08:00
} ;
}
2019-05-24 18:27:18 -04:00
2022-01-30 11:27:52 -08:00
/// Cross-platform command line argument iterator.
2019-05-24 18:27:18 -04:00
pub const ArgIterator = struct {
2020-05-20 19:42:15 +02:00
const InnerType = switch ( builtin . os . tag ) {
2022-01-30 11:27:52 -08:00
. windows = > ArgIteratorGeneral ( . { . comments_supported = false } ) ,
2021-07-27 08:59:34 +09:00
. wasi = > if ( builtin . link_libc ) ArgIteratorPosix else ArgIteratorWasi ,
2020-05-20 19:42:15 +02:00
else = > ArgIteratorPosix ,
} ;
2019-05-24 18:27:18 -04:00
inner : InnerType ,
2022-01-30 11:27:52 -08:00
/// Initialize the args iterator. Consider using initWithAllocator() instead
/// for cross-platform compatibility.
2019-05-24 18:27:18 -04:00
pub fn init ( ) ArgIterator {
2020-02-25 01:52:27 -05:00
if ( builtin . os . tag == . wasi ) {
2020-05-29 08:40:32 +02:00
@compileError ( " In WASI, use initWithAllocator instead. " ) ;
2019-05-24 18:27:18 -04:00
}
2022-01-30 11:27:52 -08:00
if ( builtin . os . tag == . windows ) {
@compileError ( " In Windows, use initWithAllocator instead. " ) ;
}
2019-05-24 18:27:18 -04:00
return ArgIterator { . inner = InnerType . init ( ) } ;
}
2022-01-30 11:27:52 -08:00
pub const InitError = switch ( builtin . os . tag ) {
. windows = > InnerType . InitUtf16leError ,
else = > InnerType . InitError ,
} ;
2020-05-20 19:42:15 +02:00
2020-05-29 08:40:32 +02:00
/// You must deinitialize iterator's internal buffers by calling `deinit` when done.
2021-10-29 00:37:25 +01:00
pub fn initWithAllocator ( allocator : mem . Allocator ) InitError ! ArgIterator {
2021-07-27 08:59:34 +09:00
if ( builtin . os . tag == . wasi and ! builtin . link_libc ) {
2020-05-29 08:40:32 +02:00
return ArgIterator { . inner = try InnerType . init ( allocator ) } ;
}
2020-02-25 01:52:27 -05:00
if ( builtin . os . tag == . windows ) {
2022-01-30 11:27:52 -08:00
const cmd_line_w = os . windows . kernel32 . GetCommandLineW ( ) ;
return ArgIterator { . inner = try InnerType . initUtf16le ( allocator , cmd_line_w ) } ;
2019-05-24 18:27:18 -04:00
}
2022-01-30 11:27:52 -08:00
return ArgIterator { . inner = InnerType . init ( ) } ;
2019-05-24 18:27:18 -04:00
}
2022-01-30 11:27:52 -08:00
/// Get the next argument. Returns 'null' if we are at the end.
/// Returned slice is pointing to the iterator's internal buffer.
pub fn next ( self : * ArgIterator ) ? ( [ : 0 ] const u8 ) {
2020-05-20 19:42:15 +02:00
return self . inner . next ( ) ;
}
2019-05-24 18:27:18 -04:00
/// Parse past 1 argument without capturing it.
/// Returns `true` if skipped an arg, `false` if we are at the end.
pub fn skip ( self : * ArgIterator ) bool {
return self . inner . skip ( ) ;
}
2020-05-20 19:42:15 +02:00
2020-05-29 08:40:32 +02:00
/// Call this to free the iterator's internal buffer if the iterator
/// was created with `initWithAllocator` function.
pub fn deinit ( self : * ArgIterator ) void {
2022-01-30 11:27:52 -08:00
// Unless we're targeting WASI or Windows, this is a no-op.
2021-07-27 08:59:34 +09:00
if ( builtin . os . tag == . wasi and ! builtin . link_libc ) {
2020-05-29 08:40:32 +02:00
self . inner . deinit ( ) ;
}
2022-01-30 11:27:52 -08:00
if ( builtin . os . tag == . windows ) {
self . inner . deinit ( ) ;
}
2020-05-20 19:42:15 +02:00
}
2019-05-24 18:27:18 -04:00
} ;
2022-01-30 11:27:52 -08:00
/// Use argsWithAllocator() for cross-platform code
2019-05-24 18:27:18 -04:00
pub fn args ( ) ArgIterator {
return ArgIterator . init ( ) ;
}
2020-05-29 08:40:32 +02:00
/// You must deinitialize iterator's internal buffers by calling `deinit` when done.
2021-10-29 00:37:25 +01:00
pub fn argsWithAllocator ( allocator : mem . Allocator ) ArgIterator . InitError ! ArgIterator {
2020-05-29 08:40:32 +02:00
return ArgIterator . initWithAllocator ( allocator ) ;
}
test " args iterator " {
var ga = std . testing . allocator ;
2022-01-30 11:27:52 -08:00
var it = try argsWithAllocator ( ga ) ;
defer it . deinit ( ) ; // no-op unless WASI or Windows
2020-05-29 08:40:32 +02:00
2022-01-30 11:27:52 -08:00
const prog_name = it . next ( ) orelse unreachable ;
2020-05-29 17:12:19 +02:00
const expected_suffix = switch ( builtin . os . tag ) {
2020-05-29 08:40:32 +02:00
. wasi = > " test.wasm " ,
. windows = > " test.exe " ,
else = > " test " ,
} ;
2020-05-29 17:12:19 +02:00
const given_suffix = std . fs . path . basename ( prog_name ) ;
2021-05-04 20:47:26 +03:00
try testing . expect ( mem . eql ( u8 , expected_suffix , given_suffix ) ) ;
try testing . expect ( it . skip ( ) ) ; // Skip over zig_exe_path, passed to the test runner
2022-01-30 11:27:52 -08:00
try testing . expect ( it . next ( ) == null ) ;
2021-05-04 20:47:26 +03:00
try testing . expect ( ! it . skip ( ) ) ;
2020-05-29 08:40:32 +02:00
}
2019-05-24 18:27:18 -04:00
/// Caller must call argsFree on result.
2021-10-29 00:37:25 +01:00
pub fn argsAlloc ( allocator : mem . Allocator ) ! [ ] [ : 0 ] u8 {
2019-05-24 18:27:18 -04:00
// TODO refactor to only make 1 allocation.
2022-01-30 11:27:52 -08:00
var it = try argsWithAllocator ( allocator ) ;
2020-05-29 08:40:32 +02:00
defer it . deinit ( ) ;
2020-05-20 19:42:15 +02:00
2020-02-11 23:01:30 +11:00
var contents = std . ArrayList ( u8 ) . init ( allocator ) ;
2019-05-24 18:27:18 -04:00
defer contents . deinit ( ) ;
2019-05-26 23:35:26 -04:00
var slice_list = std . ArrayList ( usize ) . init ( allocator ) ;
2019-05-24 18:27:18 -04:00
defer slice_list . deinit ( ) ;
2022-01-30 11:27:52 -08:00
while ( it . next ( ) ) | arg | {
2020-10-22 17:52:48 -04:00
try contents . appendSlice ( arg [ 0 .. arg . len + 1 ] ) ;
2019-05-24 18:27:18 -04:00
try slice_list . append ( arg . len ) ;
}
2020-11-06 18:54:08 +00:00
const contents_slice = contents . items ;
const slice_sizes = slice_list . items ;
2019-05-24 18:27:18 -04:00
const slice_list_bytes = try math . mul ( usize , @sizeOf ( [ ] u8 ) , slice_sizes . len ) ;
2022-01-28 10:40:03 +01:00
const total_bytes = try math . add ( usize , slice_list_bytes , contents_slice . len ) ;
2019-05-24 18:27:18 -04:00
const buf = try allocator . alignedAlloc ( u8 , @alignOf ( [ ] u8 ) , total_bytes ) ;
errdefer allocator . free ( buf ) ;
2020-10-22 17:52:48 -04:00
const result_slice_list = mem . bytesAsSlice ( [ : 0 ] u8 , buf [ 0 .. slice_list_bytes ] ) ;
2019-05-24 18:27:18 -04:00
const result_contents = buf [ slice_list_bytes .. ] ;
mem . copy ( u8 , result_contents , contents_slice ) ;
var contents_index : usize = 0 ;
for ( slice_sizes ) | len , i | {
const new_index = contents_index + len ;
2020-10-22 17:52:48 -04:00
result_slice_list [ i ] = result_contents [ contents_index .. new_index : 0 ] ;
contents_index = new_index + 1 ;
2019-05-24 18:27:18 -04:00
}
return result_slice_list ;
}
2021-10-29 00:37:25 +01:00
pub fn argsFree ( allocator : mem . Allocator , args_alloc : [ ] const [ : 0 ] u8 ) void {
2019-05-24 18:27:18 -04:00
var total_bytes : usize = 0 ;
for ( args_alloc ) | arg | {
2020-10-22 17:52:48 -04:00
total_bytes += @sizeOf ( [ ] u8 ) + arg . len + 1 ;
2019-05-24 18:27:18 -04:00
}
const unaligned_allocated_buf = @ptrCast ( [ * ] const u8 , args_alloc . ptr ) [ 0 .. total_bytes ] ;
const aligned_allocated_buf = @alignCast ( @alignOf ( [ ] u8 ) , unaligned_allocated_buf ) ;
return allocator . free ( aligned_allocated_buf ) ;
}
2022-01-30 11:27:52 -08:00
test " general arg parsing " {
try testGeneralCmdLine ( " a b \t c d " , & [ _ ] [ ] const u8 { " a " , " b " , " c " , " d " } ) ;
try testGeneralCmdLine ( " \" abc \" d e " , & [ _ ] [ ] const u8 { " abc " , " d " , " e " } ) ;
try testGeneralCmdLine ( " a \\ \\ \\ b d \" e f \" g h " , & [ _ ] [ ] const u8 { " a \\ \\ \\ b " , " de fg " , " h " } ) ;
try testGeneralCmdLine ( " a \\ \\ \\ \" b c d " , & [ _ ] [ ] const u8 { " a \\ \" b " , " c " , " d " } ) ;
try testGeneralCmdLine ( " a \\ \\ \\ \\ \" b c \" d e " , & [ _ ] [ ] const u8 { " a \\ \\ b c " , " d " , " e " } ) ;
try testGeneralCmdLine ( " a b \t c \" d f " , & [ _ ] [ ] const u8 { " a " , " b " , " c " , " d f " } ) ;
try testGeneralCmdLine ( " j k l \\ " , & [ _ ] [ ] const u8 { " j " , " k " , " l \\ " } ) ;
try testGeneralCmdLine ( " \" \" x y z \\ \\ " , & [ _ ] [ ] const u8 { " " , " x " , " y " , " z \\ \\ " } ) ;
try testGeneralCmdLine ( " \" . \\ .. \\ zig-cache \\ build \" \" bin \\ zig.exe \" \" . \\ .. \" \" . \\ .. \\ zig-cache \" \" --help \" " , & [ _ ] [ ] const u8 {
2019-05-24 18:27:18 -04:00
" . \\ .. \\ zig-cache \\ build " ,
" bin \\ zig.exe " ,
" . \\ .. " ,
" . \\ .. \\ zig-cache " ,
" --help " ,
} ) ;
}
2022-01-30 11:27:52 -08:00
fn testGeneralCmdLine ( input_cmd_line : [ ] const u8 , expected_args : [ ] const [ ] const u8 ) ! void {
var it = try ArgIteratorGeneral ( . { . comments_supported = false } )
. init ( std . testing . allocator , input_cmd_line ) ;
defer it . deinit ( ) ;
for ( expected_args ) | expected_arg | {
const arg = it . next ( ) .? ;
try testing . expectEqualStrings ( expected_arg , arg ) ;
}
try testing . expect ( it . next ( ) == null ) ;
}
test " response file arg parsing " {
try testResponseFileCmdLine (
\\a b
\\c d\
, & [ _ ] [ ] const u8 { " a " , " b " , " c " , " d \\ " } ) ;
try testResponseFileCmdLine ( " a b c d \\ " , & [ _ ] [ ] const u8 { " a " , " b " , " c " , " d \\ " } ) ;
try testResponseFileCmdLine (
\\j
\\ k l # this is a comment \\ \\\ \\\\ "none" "\\" "\\\"
\\ "m" #another comment
\\
, & [ _ ] [ ] const u8 { " j " , " k " , " l " , " m " } ) ;
try testResponseFileCmdLine (
\\ "" q ""
\\ "r s # t" "u\" v" #another comment
\\
, & [ _ ] [ ] const u8 { " " , " q " , " " , " r s # t " , " u \" v " } ) ;
try testResponseFileCmdLine (
\\ -l"advapi32" a# b#c d#
\\e\\\
, & [ _ ] [ ] const u8 { " -ladvapi32 " , " a# " , " b#c " , " d# " , " e \\ \\ \\ " } ) ;
}
fn testResponseFileCmdLine ( input_cmd_line : [ ] const u8 , expected_args : [ ] const [ ] const u8 ) ! void {
var it = try ArgIteratorGeneral ( . { . comments_supported = true } )
. init ( std . testing . allocator , input_cmd_line ) ;
defer it . deinit ( ) ;
2019-05-24 18:27:18 -04:00
for ( expected_args ) | expected_arg | {
2022-01-30 11:27:52 -08:00
const arg = it . next ( ) .? ;
2021-05-04 20:47:26 +03:00
try testing . expectEqualStrings ( expected_arg , arg ) ;
2019-05-24 18:27:18 -04:00
}
2022-01-30 11:27:52 -08:00
try testing . expect ( it . next ( ) == null ) ;
2019-05-24 18:27:18 -04:00
}
pub const UserInfo = struct {
2020-09-03 15:08:37 +02:00
uid : os . uid_t ,
gid : os . gid_t ,
2019-05-24 18:27:18 -04:00
} ;
/// POSIX function which gets a uid from username.
pub fn getUserInfo ( name : [ ] const u8 ) ! UserInfo {
2020-02-25 01:52:27 -05:00
return switch ( builtin . os . tag ) {
2021-08-31 22:21:23 +10:00
. linux , . macos , . watchos , . tvos , . ios , . freebsd , . netbsd , . openbsd , . haiku , . solaris = > posixGetUserInfo ( name ) ,
2019-05-24 18:27:18 -04:00
else = > @compileError ( " Unsupported OS " ) ,
} ;
}
/// TODO this reads /etc/passwd. But sometimes the user/id mapping is in something else
/// like NIS, AD, etc. See `man nss` or look at an strace for `id myuser`.
pub fn posixGetUserInfo ( name : [ ] const u8 ) ! UserInfo {
2020-09-10 13:36:34 +02:00
const file = try std . fs . openFileAbsolute ( " /etc/passwd " , . { } ) ;
defer file . close ( ) ;
const reader = file . reader ( ) ;
2019-05-24 18:27:18 -04:00
const State = enum {
Start ,
WaitForNextLine ,
SkipPassword ,
ReadUserId ,
ReadGroupId ,
} ;
var buf : [ std . mem . page_size ] u8 = undefined ;
var name_index : usize = 0 ;
var state = State . Start ;
2020-09-03 15:08:37 +02:00
var uid : os . uid_t = 0 ;
var gid : os . gid_t = 0 ;
2019-05-24 18:27:18 -04:00
while ( true ) {
2020-06-08 22:34:50 -06:00
const amt_read = try reader . read ( buf [ 0 .. ] ) ;
2019-05-24 18:27:18 -04:00
for ( buf [ 0 .. amt_read ] ) | byte | {
switch ( state ) {
. Start = > switch ( byte ) {
':' = > {
state = if ( name_index == name . len ) State . SkipPassword else State . WaitForNextLine ;
} ,
'\n' = > return error . CorruptPasswordFile ,
else = > {
if ( name_index == name . len or name [ name_index ] != byte ) {
state = . WaitForNextLine ;
}
name_index += 1 ;
} ,
} ,
. WaitForNextLine = > switch ( byte ) {
'\n' = > {
name_index = 0 ;
state = . Start ;
} ,
else = > continue ,
} ,
. SkipPassword = > switch ( byte ) {
'\n' = > return error . CorruptPasswordFile ,
':' = > {
state = . ReadUserId ;
} ,
else = > continue ,
} ,
. ReadUserId = > switch ( byte ) {
':' = > {
state = . ReadGroupId ;
} ,
'\n' = > return error . CorruptPasswordFile ,
else = > {
const digit = switch ( byte ) {
'0' .. . '9' = > byte - '0' ,
else = > return error . CorruptPasswordFile ,
} ;
2020-09-10 13:36:34 +02:00
if ( @mulWithOverflow ( u32 , uid , 10 , & uid ) ) return error . CorruptPasswordFile ;
if ( @addWithOverflow ( u32 , uid , digit , & uid ) ) return error . CorruptPasswordFile ;
2019-05-24 18:27:18 -04:00
} ,
} ,
. ReadGroupId = > switch ( byte ) {
'\n' , ':' = > {
return UserInfo {
. uid = uid ,
. gid = gid ,
} ;
} ,
else = > {
const digit = switch ( byte ) {
'0' .. . '9' = > byte - '0' ,
else = > return error . CorruptPasswordFile ,
} ;
2020-09-10 13:36:34 +02:00
if ( @mulWithOverflow ( u32 , gid , 10 , & gid ) ) return error . CorruptPasswordFile ;
if ( @addWithOverflow ( u32 , gid , digit , & gid ) ) return error . CorruptPasswordFile ;
2019-05-24 18:27:18 -04:00
} ,
} ,
}
}
if ( amt_read < buf . len ) return error . UserNotFound ;
}
}
2019-05-26 13:17:34 -04:00
pub fn getBaseAddress ( ) usize {
2020-02-25 01:52:27 -05:00
switch ( builtin . os . tag ) {
2019-05-26 13:17:34 -04:00
. linux = > {
const base = os . system . getauxval ( std . elf . AT_BASE ) ;
if ( base != 0 ) {
return base ;
}
const phdr = os . system . getauxval ( std . elf . AT_PHDR ) ;
return phdr - @sizeOf ( std . elf . Ehdr ) ;
} ,
2020-10-12 14:29:43 +05:30
. macos , . freebsd , . netbsd = > {
2019-05-26 13:17:34 -04:00
return @ptrToInt ( & std . c . _mh_execute_header ) ;
} ,
. windows = > return @ptrToInt ( os . windows . kernel32 . GetModuleHandleW ( null ) ) ,
else = > @compileError ( " Unsupported OS " ) ,
}
}
2020-02-17 15:23:59 -05:00
/// Caller owns the result value and each inner slice.
2020-02-27 17:20:40 -05:00
/// TODO Remove the `Allocator` requirement from this API, which will remove the `Allocator`
/// requirement from `std.zig.system.NativeTargetInfo.detect`. Most likely this will require
/// introducing a new, lower-level function which takes a callback function, and then this
/// function which takes an allocator can exist on top of it.
2021-10-29 00:37:25 +01:00
pub fn getSelfExeSharedLibPaths ( allocator : Allocator ) error { OutOfMemory } ! [ ] [ : 0 ] u8 {
2020-02-17 15:23:59 -05:00
switch ( builtin . link_mode ) {
. Static = > return & [ _ ] [ : 0 ] u8 { } ,
. Dynamic = > { } ,
}
const List = std . ArrayList ( [ : 0 ] u8 ) ;
2020-02-25 01:52:27 -05:00
switch ( builtin . os . tag ) {
2020-02-17 15:23:59 -05:00
. linux ,
. freebsd ,
. netbsd ,
. dragonfly ,
2020-10-11 08:23:36 +00:00
. openbsd ,
2021-08-31 22:21:23 +10:00
. solaris ,
2020-02-17 15:23:59 -05:00
= > {
var paths = List . init ( allocator ) ;
errdefer {
const slice = paths . toOwnedSlice ( ) ;
for ( slice ) | item | {
allocator . free ( item ) ;
}
allocator . free ( slice ) ;
}
try os . dl_iterate_phdr ( & paths , error { OutOfMemory } , struct {
fn callback ( info : * os . dl_phdr_info , size : usize , list : * List ) ! void {
2021-06-19 21:10:22 -04:00
_ = size ;
2020-02-17 15:23:59 -05:00
const name = info . dlpi_name orelse return ;
if ( name [ 0 ] == '/' ) {
2021-11-30 00:13:07 -07:00
const item = try list . allocator . dupeZ ( u8 , mem . sliceTo ( name , 0 ) ) ;
2020-02-17 15:23:59 -05:00
errdefer list . allocator . free ( item ) ;
try list . append ( item ) ;
}
}
} . callback ) ;
return paths . toOwnedSlice ( ) ;
} ,
2020-10-12 14:29:43 +05:30
. macos , . ios , . watchos , . tvos = > {
2020-02-17 15:23:59 -05:00
var paths = List . init ( allocator ) ;
errdefer {
const slice = paths . toOwnedSlice ( ) ;
for ( slice ) | item | {
allocator . free ( item ) ;
}
allocator . free ( slice ) ;
}
const img_count = std . c . _dyld_image_count ( ) ;
var i : u32 = 0 ;
while ( i < img_count ) : ( i += 1 ) {
const name = std . c . _dyld_get_image_name ( i ) ;
2021-11-30 00:13:07 -07:00
const item = try allocator . dupeZ ( u8 , mem . sliceTo ( name , 0 ) ) ;
2020-02-17 15:23:59 -05:00
errdefer allocator . free ( item ) ;
try paths . append ( item ) ;
}
return paths . toOwnedSlice ( ) ;
} ,
2020-12-28 23:00:31 -06:00
// revisit if Haiku implements dl_iterat_phdr (https://dev.haiku-os.org/ticket/15743)
2020-12-25 07:48:04 -06:00
. haiku = > {
var paths = List . init ( allocator ) ;
errdefer {
const slice = paths . toOwnedSlice ( ) ;
for ( slice ) | item | {
allocator . free ( item ) ;
}
allocator . free ( slice ) ;
}
2020-12-28 23:00:31 -06:00
var b = " /boot/system/runtime_loader " ;
2021-11-30 00:13:07 -07:00
const item = try allocator . dupeZ ( u8 , mem . sliceTo ( b , 0 ) ) ;
2020-12-28 23:00:31 -06:00
errdefer allocator . free ( item ) ;
try paths . append ( item ) ;
2020-12-25 07:48:04 -06:00
return paths . toOwnedSlice ( ) ;
} ,
2020-02-17 16:03:01 -05:00
else = > @compileError ( " getSelfExeSharedLibPaths unimplemented for this target " ) ,
2020-02-17 15:23:59 -05:00
}
}
2020-12-26 13:50:26 -07:00
/// Tells whether calling the `execv` or `execve` functions will be a compile error.
2021-05-22 00:56:30 -05:00
pub const can_execv = switch ( builtin . os . tag ) {
. windows , . haiku = > false ,
else = > true ,
} ;
2020-12-26 13:50:26 -07:00
pub const ExecvError = std . os . ExecveError || error { OutOfMemory } ;
/// Replaces the current process image with the executed process.
/// This function must allocate memory to add a null terminating bytes on path and each arg.
/// It must also convert to KEY=VALUE\0 format for environment variables, and include null
/// pointers after the args and after the environment variables.
/// `argv[0]` is the executable path.
/// This function also uses the PATH environment variable to get the full path to the executable.
/// Due to the heap-allocation, it is illegal to call this function in a fork() child.
/// For that use case, use the `std.os` functions directly.
2021-10-29 00:37:25 +01:00
pub fn execv ( allocator : mem . Allocator , argv : [ ] const [ ] const u8 ) ExecvError {
2020-12-26 13:50:26 -07:00
return execve ( allocator , argv , null ) ;
}
/// Replaces the current process image with the executed process.
/// This function must allocate memory to add a null terminating bytes on path and each arg.
/// It must also convert to KEY=VALUE\0 format for environment variables, and include null
/// pointers after the args and after the environment variables.
/// `argv[0]` is the executable path.
/// This function also uses the PATH environment variable to get the full path to the executable.
/// Due to the heap-allocation, it is illegal to call this function in a fork() child.
/// For that use case, use the `std.os` functions directly.
pub fn execve (
2021-10-29 00:37:25 +01:00
allocator : mem . Allocator ,
2020-12-26 13:50:26 -07:00
argv : [ ] const [ ] const u8 ,
env_map : ? * const std . BufMap ,
) ExecvError {
if ( ! can_execv ) @compileError ( " The target OS does not support execv " ) ;
var arena_allocator = std . heap . ArenaAllocator . init ( allocator ) ;
defer arena_allocator . deinit ( ) ;
2021-10-29 02:08:41 +01:00
const arena = arena_allocator . allocator ( ) ;
2020-12-26 13:50:26 -07:00
2020-12-27 13:00:35 +01:00
const argv_buf = try arena . allocSentinel ( ? [ * : 0 ] u8 , argv . len , null ) ;
for ( argv ) | arg , i | argv_buf [ i ] = ( try arena . dupeZ ( u8 , arg ) ) . ptr ;
2020-12-26 13:50:26 -07:00
const envp = m : {
if ( env_map ) | m | {
const envp_buf = try child_process . createNullDelimitedEnvMap ( arena , m ) ;
break : m envp_buf . ptr ;
2021-10-04 23:47:27 -07:00
} else if ( builtin . link_libc ) {
2020-12-26 13:50:26 -07:00
break : m std . c . environ ;
2021-10-04 23:47:27 -07:00
} else if ( builtin . output_mode == . Exe ) {
2020-12-26 13:50:26 -07:00
// Then we have Zig start code and this works.
// TODO type-safety for null-termination of `os.environ`.
break : m @ptrCast ( [ * : null ] ? [ * : 0 ] u8 , os . environ . ptr ) ;
} else {
// TODO come up with a solution for this.
@compileError ( " missing std lib enhancement: std.process.execv implementation has no way to collect the environment variables to forward to the child process " ) ;
}
} ;
2020-12-27 13:00:35 +01:00
return os . execvpeZ_expandArg0 ( . no_expand , argv_buf . ptr [ 0 ] .? , argv_buf . ptr , envp ) ;
2020-12-26 13:50:26 -07:00
}