mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 15:48:51 +00:00
Merge tag 'dev-2025-11'
This commit is contained in:
@@ -6,6 +6,10 @@ import "core:strings"
|
||||
|
||||
read_dir :: read_directory
|
||||
|
||||
/*
|
||||
Reads the file `f` (assuming it is a directory) and returns the unsorted directory entries.
|
||||
This returns up to `n` entries OR all of them if `n <= 0`.
|
||||
*/
|
||||
@(require_results)
|
||||
read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files: []File_Info, err: Error) {
|
||||
if f == nil {
|
||||
@@ -47,11 +51,18 @@ read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Reads the file `f` (assuming it is a directory) and returns all of the unsorted directory entries.
|
||||
*/
|
||||
@(require_results)
|
||||
read_all_directory :: proc(f: ^File, allocator: runtime.Allocator) -> (fi: []File_Info, err: Error) {
|
||||
return read_directory(f, -1, allocator)
|
||||
}
|
||||
|
||||
/*
|
||||
Reads the named directory by path (assuming it is a directory) and returns the unsorted directory entries.
|
||||
This returns up to `n` entries OR all of them if `n <= 0`.
|
||||
*/
|
||||
@(require_results)
|
||||
read_directory_by_path :: proc(path: string, n: int, allocator: runtime.Allocator) -> (fi: []File_Info, err: Error) {
|
||||
f := open(path) or_return
|
||||
@@ -59,6 +70,9 @@ read_directory_by_path :: proc(path: string, n: int, allocator: runtime.Allocato
|
||||
return read_directory(f, n, allocator)
|
||||
}
|
||||
|
||||
/*
|
||||
Reads the named directory by path (assuming it is a directory) and returns all of the unsorted directory entries.
|
||||
*/
|
||||
@(require_results)
|
||||
read_all_directory_by_path :: proc(path: string, allocator: runtime.Allocator) -> (fi: []File_Info, err: Error) {
|
||||
return read_directory_by_path(path, -1, allocator)
|
||||
|
||||
@@ -18,7 +18,7 @@ find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW, al
|
||||
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
|
||||
path := concatenate({base_path, `\`, win32_wstring_to_utf8(cstring16(raw_data(d.cFileName[:])), temp_allocator) or_else ""}, allocator) or_return
|
||||
|
||||
handle := win32.HANDLE(_open_internal(path, {.Read}, 0o666) or_else 0)
|
||||
handle := win32.HANDLE(_open_internal(path, {.Read}, Permissions_Read_Write_All) or_else 0)
|
||||
defer win32.CloseHandle(handle)
|
||||
|
||||
fi.fullpath = path
|
||||
|
||||
@@ -3,6 +3,10 @@ package os2
|
||||
import "core:io"
|
||||
import "base:runtime"
|
||||
|
||||
/*
|
||||
General errors that are common within this package which cannot
|
||||
be categorized by `io.Error` nor `runtime.Allocator_Error`.
|
||||
*/
|
||||
General_Error :: enum u32 {
|
||||
None,
|
||||
|
||||
@@ -33,8 +37,12 @@ General_Error :: enum u32 {
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
// A platform specific error
|
||||
Platform_Error :: _Platform_Error
|
||||
|
||||
/*
|
||||
`Error` is a union of different classes of errors that could be returned from procedures in this package.
|
||||
*/
|
||||
Error :: union #shared_nil {
|
||||
General_Error,
|
||||
io.Error,
|
||||
@@ -46,6 +54,7 @@ Error :: union #shared_nil {
|
||||
ERROR_NONE :: Error{}
|
||||
|
||||
|
||||
// Attempts to convert an `Error` into a platform specific error as an integer. `ok` is false if not possible
|
||||
@(require_results)
|
||||
is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
|
||||
v := ferr.(Platform_Error) or_else {}
|
||||
@@ -53,6 +62,7 @@ is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
|
||||
}
|
||||
|
||||
|
||||
// Attempts to return the error `ferr` as a string without any allocation
|
||||
@(require_results)
|
||||
error_string :: proc(ferr: Error) -> string {
|
||||
if ferr == nil {
|
||||
@@ -112,6 +122,9 @@ error_string :: proc(ferr: Error) -> string {
|
||||
return "unknown error"
|
||||
}
|
||||
|
||||
/*
|
||||
`print_error` is a utility procedure which will print an error `ferr` to a specified file `f`.
|
||||
*/
|
||||
print_error :: proc(f: ^File, ferr: Error, msg: string) {
|
||||
temp_allocator := TEMP_ALLOCATOR_GUARD({})
|
||||
err_str := error_string(ferr)
|
||||
@@ -127,3 +140,14 @@ print_error :: proc(f: ^File, ferr: Error, msg: string) {
|
||||
buf[length - 1] = '\n'
|
||||
write(f, buf)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Attempts to convert an `Error` `ferr` into an `io.Error`
|
||||
@(private)
|
||||
error_to_io_error :: proc(ferr: Error) -> io.Error {
|
||||
if ferr == nil {
|
||||
return .None
|
||||
}
|
||||
return ferr.(io.Error) or_else .Unknown
|
||||
}
|
||||
|
||||
+198
-13
@@ -56,6 +56,7 @@ File_Type :: enum {
|
||||
Character_Device,
|
||||
}
|
||||
|
||||
// Represents the file flags for a file handle
|
||||
File_Flags :: distinct bit_set[File_Flag; uint]
|
||||
File_Flag :: enum {
|
||||
Read,
|
||||
@@ -90,17 +91,77 @@ O_SPARSE :: File_Flags{.Sparse}
|
||||
*/
|
||||
O_INHERITABLE :: File_Flags{.Inheritable}
|
||||
|
||||
stdin: ^File = nil // OS-Specific
|
||||
stdout: ^File = nil // OS-Specific
|
||||
stderr: ^File = nil // OS-Specific
|
||||
Permissions :: distinct bit_set[Permission_Flag; u32]
|
||||
Permission_Flag :: enum u32 {
|
||||
Execute_Other = 0,
|
||||
Write_Other = 1,
|
||||
Read_Other = 2,
|
||||
|
||||
@(require_results)
|
||||
create :: proc(name: string) -> (^File, Error) {
|
||||
return open(name, {.Read, .Write, .Create}, 0o777)
|
||||
Execute_Group = 3,
|
||||
Write_Group = 4,
|
||||
Read_Group = 5,
|
||||
|
||||
Execute_User = 6,
|
||||
Write_User = 7,
|
||||
Read_User = 8,
|
||||
}
|
||||
|
||||
Permissions_Execute_All :: Permissions{.Execute_User, .Execute_Group, .Execute_Other}
|
||||
Permissions_Write_All :: Permissions{.Write_User, .Write_Group, .Write_Other}
|
||||
Permissions_Read_All :: Permissions{.Read_User, .Read_Group, .Read_Other}
|
||||
|
||||
Permissions_Read_Write_All :: Permissions_Read_All + Permissions_Write_All
|
||||
|
||||
Permissions_All :: Permissions_Read_All + Permissions_Write_All + Permissions_Execute_All
|
||||
|
||||
Permissions_Default_File :: Permissions_Read_All + Permissions_Write_All
|
||||
Permissions_Default_Directory :: Permissions_Read_All + Permissions_Write_All + Permissions_Execute_All
|
||||
Permissions_Default :: Permissions_Default_Directory
|
||||
|
||||
perm :: proc{
|
||||
perm_number,
|
||||
}
|
||||
|
||||
/*
|
||||
`perm_number` converts an integer value `perm` to the bit set `Permissions`
|
||||
*/
|
||||
@(require_results)
|
||||
open :: proc(name: string, flags := File_Flags{.Read}, perm := 0o777) -> (^File, Error) {
|
||||
perm_number :: proc "contextless" (perm: int) -> Permissions {
|
||||
return transmute(Permissions)u32(perm & 0o777)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// `stdin` is an open file pointing to the standard input file stream
|
||||
stdin: ^File = nil // OS-Specific
|
||||
|
||||
// `stdout` is an open file pointing to the standard output file stream
|
||||
stdout: ^File = nil // OS-Specific
|
||||
|
||||
// `stderr` is an open file pointing to the standard error file stream
|
||||
stderr: ^File = nil // OS-Specific
|
||||
|
||||
/*
|
||||
`create` creates or truncates a named file `name`.
|
||||
If the file already exists, it is truncated.
|
||||
If the file does not exist, it is created with the `Permissions_Default_File` permissions.
|
||||
If successful, a `^File` is return which can be used for I/O.
|
||||
And error is returned if any is encountered.
|
||||
*/
|
||||
@(require_results)
|
||||
create :: proc(name: string) -> (^File, Error) {
|
||||
return open(name, {.Read, .Write, .Create, .Trunc}, Permissions_Default_File)
|
||||
}
|
||||
|
||||
/*
|
||||
`open` is a generalized open call, which defaults to opening for reading.
|
||||
If the file does not exist, and the `{.Create}` flag is passed, it is created with the permissions `perm`,
|
||||
and please note that the containing directory must exist otherwise and an error will be returned.
|
||||
If successful, a `^File` is return which can be used for I/O.
|
||||
And error is returned if any is encountered.
|
||||
*/
|
||||
@(require_results)
|
||||
open :: proc(name: string, flags := File_Flags{.Read}, perm := Permissions_Default) -> (^File, Error) {
|
||||
return _open(name, flags, perm)
|
||||
}
|
||||
|
||||
@@ -112,7 +173,10 @@ open :: proc(name: string, flags := File_Flags{.Read}, perm := 0o777) -> (^File,
|
||||
// return _open_buffered(name, buffer_size, flags, perm)
|
||||
// }
|
||||
|
||||
|
||||
/*
|
||||
`new_file` returns a new `^File` with the given file descriptor `handle` and `name`.
|
||||
The return value will only be `nil` IF the `handle` is not a valid file descriptor.
|
||||
*/
|
||||
@(require_results)
|
||||
new_file :: proc(handle: uintptr, name: string) -> ^File {
|
||||
file, err := _new_file(handle, name, file_allocator())
|
||||
@@ -122,16 +186,25 @@ new_file :: proc(handle: uintptr, name: string) -> ^File {
|
||||
return file
|
||||
}
|
||||
|
||||
/*
|
||||
`clone` returns a new `^File` based on the passed file `f` with the same underlying file descriptor.
|
||||
*/
|
||||
@(require_results)
|
||||
clone :: proc(f: ^File) -> (^File, Error) {
|
||||
return _clone(f)
|
||||
}
|
||||
|
||||
/*
|
||||
`fd` returns the file descriptor of the file `f` passed. If the file is not valid, an invalid handle will be returned.
|
||||
*/
|
||||
@(require_results)
|
||||
fd :: proc(f: ^File) -> uintptr {
|
||||
return _fd(f)
|
||||
}
|
||||
|
||||
/*
|
||||
`name` returns the name of the file. The lifetime of this string lasts as long as the file handle itself.
|
||||
*/
|
||||
@(require_results)
|
||||
name :: proc(f: ^File) -> string {
|
||||
return _name(f)
|
||||
@@ -150,6 +223,16 @@ close :: proc(f: ^File) -> Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
seek sets the offsets for the next read or write on a file to a specified `offset`,
|
||||
according to what `whence` is set.
|
||||
`.Start` is relative to the origin of the file.
|
||||
`.Current` is relative to the current offset.
|
||||
`.End` is relative to the end.
|
||||
It returns the new offset and an error, if any is encountered.
|
||||
Prefer `read_at` or `write_at` if the offset does not want to be changed.
|
||||
|
||||
*/
|
||||
seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
|
||||
if f != nil {
|
||||
return io.seek(f.stream, offset, whence)
|
||||
@@ -157,6 +240,11 @@ seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Err
|
||||
return 0, .Invalid_File
|
||||
}
|
||||
|
||||
/*
|
||||
`read` reads up to len(p) bytes from the file `f`, and then stores them in `p`.
|
||||
It returns the number of bytes read and an error, if any is encountered.
|
||||
At the end of a file, it returns `0, io.EOF`.
|
||||
*/
|
||||
read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
if f != nil {
|
||||
return io.read(f.stream, p)
|
||||
@@ -164,6 +252,12 @@ read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
return 0, .Invalid_File
|
||||
}
|
||||
|
||||
/*
|
||||
`read_at` reads up to len(p) bytes from the file `f` at the byte offset `offset`, and then stores them in `p`.
|
||||
It returns the number of bytes read and an error, if any is encountered.
|
||||
`read_at` always returns a non-nil error when `n < len(p)`.
|
||||
At the end of a file, the error is `io.EOF`.
|
||||
*/
|
||||
read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
if f != nil {
|
||||
return io.read_at(f.stream, p, offset)
|
||||
@@ -171,6 +265,11 @@ read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
return 0, .Invalid_File
|
||||
}
|
||||
|
||||
/*
|
||||
`write` writes `len(p)` bytes from `p` to the file `f`. It returns the number of bytes written to
|
||||
and an error, if any is encountered.
|
||||
`write` returns a non-nil error when `n != len(p)`.
|
||||
*/
|
||||
write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
if f != nil {
|
||||
return io.write(f.stream, p)
|
||||
@@ -178,6 +277,11 @@ write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
return 0, .Invalid_File
|
||||
}
|
||||
|
||||
/*
|
||||
`write_at` writes `len(p)` bytes from `p` to the file `f` starting at byte offset `offset`.
|
||||
It returns the number of bytes written to and an error, if any is encountered.
|
||||
`write_at` returns a non-nil error when `n != len(p)`.
|
||||
*/
|
||||
write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
if f != nil {
|
||||
return io.write_at(f.stream, p, offset)
|
||||
@@ -185,6 +289,9 @@ write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
return 0, .Invalid_File
|
||||
}
|
||||
|
||||
/*
|
||||
`file_size` returns the length of the file `f` in bytes and an error, if any is encountered.
|
||||
*/
|
||||
file_size :: proc(f: ^File) -> (n: i64, err: Error) {
|
||||
if f != nil {
|
||||
return io.size(f.stream)
|
||||
@@ -192,6 +299,9 @@ file_size :: proc(f: ^File) -> (n: i64, err: Error) {
|
||||
return 0, .Invalid_File
|
||||
}
|
||||
|
||||
/*
|
||||
`flush` flushes a file `f`
|
||||
*/
|
||||
flush :: proc(f: ^File) -> Error {
|
||||
if f != nil {
|
||||
return io.flush(f.stream)
|
||||
@@ -199,31 +309,54 @@ flush :: proc(f: ^File) -> Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
`sync` commits the current contents of the file `f` to stable storage.
|
||||
This usually means flushing the file system's in-memory copy to disk.
|
||||
*/
|
||||
sync :: proc(f: ^File) -> Error {
|
||||
return _sync(f)
|
||||
}
|
||||
|
||||
/*
|
||||
`truncate` changes the size of the file `f` to `size` in bytes.
|
||||
This can be used to shorten or lengthen a file.
|
||||
It does not change the "offset" of the file.
|
||||
*/
|
||||
truncate :: proc(f: ^File, size: i64) -> Error {
|
||||
return _truncate(f, size)
|
||||
}
|
||||
|
||||
/*
|
||||
`remove` removes a named file or (empty) directory.
|
||||
*/
|
||||
remove :: proc(name: string) -> Error {
|
||||
return _remove(name)
|
||||
}
|
||||
|
||||
/*
|
||||
`rename` renames (moves) `old_path` to `new_path`.
|
||||
*/
|
||||
rename :: proc(old_path, new_path: string) -> Error {
|
||||
return _rename(old_path, new_path)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
`link` creates a `new_name` as a hard link to the `old_name` file.
|
||||
*/
|
||||
link :: proc(old_name, new_name: string) -> Error {
|
||||
return _link(old_name, new_name)
|
||||
}
|
||||
|
||||
/*
|
||||
`symlink` creates a `new_name` as a symbolic link to the `old_name` file.
|
||||
*/
|
||||
symlink :: proc(old_name, new_name: string) -> Error {
|
||||
return _symlink(old_name, new_name)
|
||||
}
|
||||
|
||||
/*
|
||||
`read_link` returns the destinction of the named symbolic link `name`.
|
||||
*/
|
||||
read_link :: proc(name: string, allocator: runtime.Allocator) -> (string, Error) {
|
||||
return _read_link(name,allocator)
|
||||
}
|
||||
@@ -231,36 +364,65 @@ read_link :: proc(name: string, allocator: runtime.Allocator) -> (string, Error)
|
||||
|
||||
chdir :: change_directory
|
||||
|
||||
/*
|
||||
Changes the current working directory to the named directory.
|
||||
*/
|
||||
change_directory :: proc(name: string) -> Error {
|
||||
return _chdir(name)
|
||||
}
|
||||
|
||||
chmod :: change_mode
|
||||
|
||||
change_mode :: proc(name: string, mode: int) -> Error {
|
||||
/*
|
||||
Changes the mode/permissions of the named file to `mode`.
|
||||
If the file is a symbolic link, it changes the mode of the link's target.
|
||||
|
||||
On Windows, only `{.Write_User}` of `mode` is used, and controls whether or not
|
||||
the file has a read-only attribute. Use `{.Read_User}` for a read-only file and
|
||||
`{.Read_User, .Write_User}` for a readable & writable file.
|
||||
*/
|
||||
change_mode :: proc(name: string, mode: Permissions) -> Error {
|
||||
return _chmod(name, mode)
|
||||
}
|
||||
|
||||
chown :: change_owner
|
||||
|
||||
/*
|
||||
Changes the numeric `uid` and `gid` of a named file. If the file is a symbolic link,
|
||||
it changes the `uid` and `gid` of the link's target.
|
||||
|
||||
On Windows, it always returns an error.
|
||||
*/
|
||||
change_owner :: proc(name: string, uid, gid: int) -> Error {
|
||||
return _chown(name, uid, gid)
|
||||
}
|
||||
|
||||
fchdir :: fchange_directory
|
||||
|
||||
/*
|
||||
Changes the current working directory to the file, which must be a directory.
|
||||
*/
|
||||
fchange_directory :: proc(f: ^File) -> Error {
|
||||
return _fchdir(f)
|
||||
}
|
||||
|
||||
fchmod :: fchange_mode
|
||||
|
||||
fchange_mode :: proc(f: ^File, mode: int) -> Error {
|
||||
/*
|
||||
Changes the current `mode` permissions of the file `f`.
|
||||
*/
|
||||
fchange_mode :: proc(f: ^File, mode: Permissions) -> Error {
|
||||
return _fchmod(f, mode)
|
||||
}
|
||||
|
||||
fchown :: fchange_owner
|
||||
|
||||
/*
|
||||
Changes the numeric `uid` and `gid` of the file `f`. If the file is a symbolic link,
|
||||
it changes the `uid` and `gid` of the link's target.
|
||||
|
||||
On Windows, it always returns an error.
|
||||
*/
|
||||
fchange_owner :: proc(f: ^File, uid, gid: int) -> Error {
|
||||
return _fchown(f, uid, gid)
|
||||
}
|
||||
@@ -268,27 +430,45 @@ fchange_owner :: proc(f: ^File, uid, gid: int) -> Error {
|
||||
|
||||
lchown :: change_owner_do_not_follow_links
|
||||
|
||||
/*
|
||||
Changes the numeric `uid` and `gid` of the file `f`. If the file is a symbolic link,
|
||||
it changes the `uid` and `gid` of the lin itself.
|
||||
|
||||
On Windows, it always returns an error.
|
||||
*/
|
||||
change_owner_do_not_follow_links :: proc(name: string, uid, gid: int) -> Error {
|
||||
return _lchown(name, uid, gid)
|
||||
}
|
||||
|
||||
chtimes :: change_times
|
||||
|
||||
/*
|
||||
Changes the access `atime` and modification `mtime` times of a named file.
|
||||
*/
|
||||
change_times :: proc(name: string, atime, mtime: time.Time) -> Error {
|
||||
return _chtimes(name, atime, mtime)
|
||||
}
|
||||
|
||||
fchtimes :: fchange_times
|
||||
|
||||
/*
|
||||
Changes the access `atime` and modification `mtime` times of the file `f`.
|
||||
*/
|
||||
fchange_times :: proc(f: ^File, atime, mtime: time.Time) -> Error {
|
||||
return _fchtimes(f, atime, mtime)
|
||||
}
|
||||
|
||||
/*
|
||||
`exists` returns whether or not a named file exists.
|
||||
*/
|
||||
@(require_results)
|
||||
exists :: proc(path: string) -> bool {
|
||||
return _exists(path)
|
||||
}
|
||||
|
||||
/*
|
||||
`is_file` returns whether or not the type of a named file is a `File_Type.Regular` file.
|
||||
*/
|
||||
@(require_results)
|
||||
is_file :: proc(path: string) -> bool {
|
||||
temp_allocator := TEMP_ALLOCATOR_GUARD({})
|
||||
@@ -301,6 +481,9 @@ is_file :: proc(path: string) -> bool {
|
||||
|
||||
is_dir :: is_directory
|
||||
|
||||
/*
|
||||
Returns whether or not the type of a named file is a `File_Type.Directory` file.
|
||||
*/
|
||||
@(require_results)
|
||||
is_directory :: proc(path: string) -> bool {
|
||||
temp_allocator := TEMP_ALLOCATOR_GUARD({})
|
||||
@@ -311,7 +494,9 @@ is_directory :: proc(path: string) -> bool {
|
||||
return fi.type == .Directory
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
`copy_file` copies a file from `src_path` to `dst_path` and returns an error if any was encountered.
|
||||
*/
|
||||
copy_file :: proc(dst_path, src_path: string) -> Error {
|
||||
when #defined(_copy_file_native) {
|
||||
return _copy_file_native(dst_path, src_path)
|
||||
@@ -331,7 +516,7 @@ _copy_file :: proc(dst_path, src_path: string) -> Error {
|
||||
return .Invalid_File
|
||||
}
|
||||
|
||||
dst := open(dst_path, {.Read, .Write, .Create, .Trunc}, info.mode & 0o777) or_return
|
||||
dst := open(dst_path, {.Read, .Write, .Create, .Trunc}, info.mode & Permissions_All) or_return
|
||||
defer close(dst)
|
||||
|
||||
_, err := io.copy(to_writer(dst), to_reader(src))
|
||||
|
||||
+11
-11
@@ -65,7 +65,7 @@ _standard_stream_init :: proc "contextless" () {
|
||||
stderr = new_std(&files[2], 2, "/proc/self/fd/2")
|
||||
}
|
||||
|
||||
_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
|
||||
_open :: proc(name: string, flags: File_Flags, perm: Permissions) -> (f: ^File, err: Error) {
|
||||
temp_allocator := TEMP_ALLOCATOR_GUARD({})
|
||||
name_cstr := clone_to_cstring(name, temp_allocator) or_return
|
||||
|
||||
@@ -88,7 +88,7 @@ _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Err
|
||||
if .Trunc in flags { sys_flags += {.TRUNC} }
|
||||
if .Inheritable in flags { sys_flags -= {.CLOEXEC} }
|
||||
|
||||
fd, errno := linux.open(name_cstr, sys_flags, transmute(linux.Mode)u32(perm))
|
||||
fd, errno := linux.open(name_cstr, sys_flags, transmute(linux.Mode)transmute(u32)perm)
|
||||
if errno != .NONE {
|
||||
return nil, _get_platform_error(errno)
|
||||
}
|
||||
@@ -132,7 +132,7 @@ _clone :: proc(f: ^File) -> (clone: ^File, err: Error) {
|
||||
|
||||
|
||||
@(require_results)
|
||||
_open_buffered :: proc(name: string, buffer_size: uint, flags := File_Flags{.Read}, perm := 0o777) -> (f: ^File, err: Error) {
|
||||
_open_buffered :: proc(name: string, buffer_size: uint, flags := File_Flags{.Read}, perm: Permissions) -> (f: ^File, err: Error) {
|
||||
assert(buffer_size > 0)
|
||||
f, err = _open(name, flags, perm)
|
||||
if f != nil && err == nil {
|
||||
@@ -198,7 +198,7 @@ _seek :: proc(f: ^File_Impl, offset: i64, whence: io.Seek_From) -> (ret: i64, er
|
||||
case .NONE:
|
||||
return n, nil
|
||||
case:
|
||||
return -1, _get_platform_error(errno)
|
||||
return 0, _get_platform_error(errno)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ _read :: proc(f: ^File_Impl, p: []byte) -> (i64, Error) {
|
||||
|
||||
n, errno := linux.read(f.fd, p[:min(len(p), MAX_RW)])
|
||||
if errno != .NONE {
|
||||
return -1, _get_platform_error(errno)
|
||||
return 0, _get_platform_error(errno)
|
||||
}
|
||||
return i64(n), io.Error.EOF if n == 0 else nil
|
||||
}
|
||||
@@ -223,7 +223,7 @@ _read_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (i64, Error) {
|
||||
}
|
||||
n, errno := linux.pread(f.fd, p[:min(len(p), MAX_RW)], offset)
|
||||
if errno != .NONE {
|
||||
return -1, _get_platform_error(errno)
|
||||
return 0, _get_platform_error(errno)
|
||||
}
|
||||
if n == 0 {
|
||||
return 0, .EOF
|
||||
@@ -276,7 +276,7 @@ _file_size :: proc(f: ^File_Impl) -> (n: i64, err: Error) {
|
||||
s: linux.Stat = ---
|
||||
errno := linux.fstat(f.fd, &s)
|
||||
if errno != .NONE {
|
||||
return -1, _get_platform_error(errno)
|
||||
return 0, _get_platform_error(errno)
|
||||
}
|
||||
|
||||
if s.mode & linux.S_IFMT == linux.S_IFREG {
|
||||
@@ -369,15 +369,15 @@ _fchdir :: proc(f: ^File) -> Error {
|
||||
return _get_platform_error(linux.fchdir(impl.fd))
|
||||
}
|
||||
|
||||
_chmod :: proc(name: string, mode: int) -> Error {
|
||||
_chmod :: proc(name: string, mode: Permissions) -> Error {
|
||||
temp_allocator := TEMP_ALLOCATOR_GUARD({})
|
||||
name_cstr := clone_to_cstring(name, temp_allocator) or_return
|
||||
return _get_platform_error(linux.chmod(name_cstr, transmute(linux.Mode)(u32(mode))))
|
||||
return _get_platform_error(linux.chmod(name_cstr, transmute(linux.Mode)transmute(u32)mode))
|
||||
}
|
||||
|
||||
_fchmod :: proc(f: ^File, mode: int) -> Error {
|
||||
_fchmod :: proc(f: ^File, mode: Permissions) -> Error {
|
||||
impl := (^File_Impl)(f.impl)
|
||||
return _get_platform_error(linux.fchmod(impl.fd, transmute(linux.Mode)(u32(mode))))
|
||||
return _get_platform_error(linux.fchmod(impl.fd, transmute(linux.Mode)transmute(u32)mode))
|
||||
}
|
||||
|
||||
// NOTE: will throw error without super user priviledges
|
||||
|
||||
+21
-14
@@ -46,7 +46,7 @@ init_std_files :: proc "contextless" () {
|
||||
stderr = new_std(&files[2], posix.STDERR_FILENO, "/dev/stderr")
|
||||
}
|
||||
|
||||
_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
|
||||
_open :: proc(name: string, flags: File_Flags, perm: Permissions) -> (f: ^File, err: Error) {
|
||||
if name == "" {
|
||||
err = .Invalid_Path
|
||||
return
|
||||
@@ -72,7 +72,7 @@ _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Err
|
||||
temp_allocator := TEMP_ALLOCATOR_GUARD({})
|
||||
cname := clone_to_cstring(name, temp_allocator) or_return
|
||||
|
||||
fd := posix.open(cname, sys_flags, transmute(posix.mode_t)posix._mode_t(perm))
|
||||
fd := posix.open(cname, sys_flags, transmute(posix.mode_t)posix._mode_t(transmute(u32)perm))
|
||||
if fd < 0 {
|
||||
err = _get_platform_error()
|
||||
return
|
||||
@@ -284,17 +284,17 @@ _fchdir :: proc(f: ^File) -> Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
_fchmod :: proc(f: ^File, mode: int) -> Error {
|
||||
if posix.fchmod(__fd(f), transmute(posix.mode_t)posix._mode_t(mode)) != .OK {
|
||||
_fchmod :: proc(f: ^File, mode: Permissions) -> Error {
|
||||
if posix.fchmod(__fd(f), transmute(posix.mode_t)posix._mode_t(transmute(u32)mode)) != .OK {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_chmod :: proc(name: string, mode: int) -> (err: Error) {
|
||||
_chmod :: proc(name: string, mode: Permissions) -> (err: Error) {
|
||||
temp_allocator := TEMP_ALLOCATOR_GUARD({})
|
||||
cname := clone_to_cstring(name, temp_allocator) or_return
|
||||
if posix.chmod(cname, transmute(posix.mode_t)posix._mode_t(mode)) != .OK {
|
||||
if posix.chmod(cname, transmute(posix.mode_t)posix._mode_t(transmute(u32)mode)) != .OK {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
@@ -382,12 +382,14 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
|
||||
}
|
||||
|
||||
to_read := uint(min(len(p), MAX_RW))
|
||||
n = i64(posix.read(fd, raw_data(p), to_read))
|
||||
_n := i64(posix.read(fd, raw_data(p), to_read))
|
||||
switch {
|
||||
case n == 0:
|
||||
case _n == 0:
|
||||
err = .EOF
|
||||
case n < 0:
|
||||
case _n < 0:
|
||||
err = .Unknown
|
||||
case:
|
||||
n = _n
|
||||
}
|
||||
return
|
||||
|
||||
@@ -402,12 +404,14 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
|
||||
}
|
||||
|
||||
to_read := uint(min(len(p), MAX_RW))
|
||||
n = i64(posix.pread(fd, raw_data(p), to_read, posix.off_t(offset)))
|
||||
_n := i64(posix.pread(fd, raw_data(p), to_read, posix.off_t(offset)))
|
||||
switch {
|
||||
case n == 0:
|
||||
case _n == 0:
|
||||
err = .EOF
|
||||
case n < 0:
|
||||
case _n < 0:
|
||||
err = .Unknown
|
||||
case:
|
||||
n = _n
|
||||
}
|
||||
return
|
||||
|
||||
@@ -460,15 +464,18 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
|
||||
return
|
||||
}
|
||||
|
||||
n = i64(posix.lseek(fd, posix.off_t(offset), posix.Whence(whence)))
|
||||
if n < 0 {
|
||||
_n := i64(posix.lseek(fd, posix.off_t(offset), posix.Whence(whence)))
|
||||
if _n < 0 {
|
||||
#partial switch posix.get_errno() {
|
||||
case .EINVAL:
|
||||
err = .Invalid_Offset
|
||||
case:
|
||||
err = .Unknown
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
n = _n
|
||||
return
|
||||
|
||||
case .Size:
|
||||
|
||||
@@ -8,7 +8,7 @@ import "core:sys/posix"
|
||||
|
||||
_posix_absolute_path :: proc(fd: posix.FD, name: string, allocator: runtime.Allocator) -> (path: cstring, err: Error) {
|
||||
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
|
||||
cname := clone_to_cstring(name, temp_allocator)
|
||||
cname := clone_to_cstring(name, temp_allocator) or_return
|
||||
|
||||
buf: [posix.PATH_MAX]byte
|
||||
path = posix.realpath(cname, raw_data(buf[:]))
|
||||
|
||||
@@ -2,6 +2,7 @@ package os2
|
||||
|
||||
import "core:io"
|
||||
|
||||
// Converts a file `f` into an `io.Stream`
|
||||
to_stream :: proc(f: ^File) -> (s: io.Stream) {
|
||||
if f != nil {
|
||||
assert(f.stream.procedure != nil)
|
||||
@@ -10,14 +11,16 @@ to_stream :: proc(f: ^File) -> (s: io.Stream) {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
This is an alias of `to_stream` which converts a file `f` to an `io.Stream`.
|
||||
It can be useful to indicate what the stream is meant to be used for as a writer,
|
||||
even if it has no logical difference.
|
||||
*/
|
||||
to_writer :: to_stream
|
||||
|
||||
/*
|
||||
This is an alias of `to_stream` which converts a file `f` to an `io.Stream`.
|
||||
It can be useful to indicate what the stream is meant to be used for as a reader,
|
||||
even if it has no logical difference.
|
||||
*/
|
||||
to_reader :: to_stream
|
||||
|
||||
|
||||
@(private)
|
||||
error_to_io_error :: proc(ferr: Error) -> io.Error {
|
||||
if ferr == nil {
|
||||
return .None
|
||||
}
|
||||
return ferr.(io.Error) or_else .Unknown
|
||||
}
|
||||
|
||||
+94
-19
@@ -4,10 +4,18 @@ import "base:runtime"
|
||||
import "core:strconv"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
/*
|
||||
`write_string` writes a string `s` to file `f`.
|
||||
Returns the number of bytes written and an error, if any is encountered.
|
||||
*/
|
||||
write_string :: proc(f: ^File, s: string) -> (n: int, err: Error) {
|
||||
return write(f, transmute([]byte)s)
|
||||
}
|
||||
|
||||
/*
|
||||
`write_strings` writes a variadic list of strings `strings` to file `f`.
|
||||
Returns the number of bytes written and an error, if any is encountered.
|
||||
*/
|
||||
write_strings :: proc(f: ^File, strings: ..string) -> (n: int, err: Error) {
|
||||
for s in strings {
|
||||
m: int
|
||||
@@ -19,11 +27,18 @@ write_strings :: proc(f: ^File, strings: ..string) -> (n: int, err: Error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
`write_byte` writes a byte `b` to file `f`.
|
||||
Returns the number of bytes written and an error, if any is encountered.
|
||||
*/
|
||||
write_byte :: proc(f: ^File, b: byte) -> (n: int, err: Error) {
|
||||
return write(f, []byte{b})
|
||||
}
|
||||
|
||||
/*
|
||||
`write_rune` writes a rune `r` as an UTF-8 encoded string to file `f`.
|
||||
Returns the number of bytes written and an error, if any is encountered.
|
||||
*/
|
||||
write_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) {
|
||||
if r < utf8.RUNE_SELF {
|
||||
return write_byte(f, byte(r))
|
||||
@@ -34,6 +49,10 @@ write_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) {
|
||||
return write(f, b[:n])
|
||||
}
|
||||
|
||||
/*
|
||||
`write_encoded_rune` writes a rune `r` as an UTF-8 encoded string which with escaped control codes to file `f`.
|
||||
Returns the number of bytes written and an error, if any is encountered.
|
||||
*/
|
||||
write_encoded_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) {
|
||||
wrap :: proc(m: int, merr: Error, n: ^int, err: ^Error) -> bool {
|
||||
n^ += m
|
||||
@@ -73,6 +92,31 @@ write_encoded_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
`write_ptr` is a utility procedure that writes the bytes points at `data` with length `len`.
|
||||
|
||||
It is equivalent to: `write(f, ([^]byte)(data)[:len])`
|
||||
*/
|
||||
write_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) {
|
||||
return write(f, ([^]byte)(data)[:len])
|
||||
}
|
||||
|
||||
/*
|
||||
`read_ptr` is a utility procedure that reads the bytes points at `data` with length `len`.
|
||||
|
||||
It is equivalent to: `read(f, ([^]byte)(data)[:len])`
|
||||
*/
|
||||
read_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) {
|
||||
return read(f, ([^]byte)(data)[:len])
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
`read_at_least` reads from `f` into `buf` until it has read at least `min` bytes.
|
||||
It returns the number of bytes copied and an error if fewer bytes were read.
|
||||
The error is only an `io.EOF` if no bytes were read.
|
||||
*/
|
||||
read_at_least :: proc(f: ^File, buf: []byte, min: int) -> (n: int, err: Error) {
|
||||
if len(buf) < min {
|
||||
return 0, .Short_Buffer
|
||||
@@ -88,17 +132,17 @@ read_at_least :: proc(f: ^File, buf: []byte, min: int) -> (n: int, err: Error) {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
`read_full` reads exactly `len(buf)` bytes from `f` into `buf`.
|
||||
It returns the number of bytes copied and an error if fewer bytes were read.
|
||||
The error is only an `io.EOF` if no bytes were read.
|
||||
|
||||
It is equivalent to `read_at_least(f, buf, len(buf))`.
|
||||
*/
|
||||
read_full :: proc(f: ^File, buf: []byte) -> (n: int, err: Error) {
|
||||
return read_at_least(f, buf, len(buf))
|
||||
}
|
||||
|
||||
write_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) {
|
||||
return write(f, ([^]byte)(data)[:len])
|
||||
}
|
||||
|
||||
read_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) {
|
||||
return read(f, ([^]byte)(data)[:len])
|
||||
}
|
||||
|
||||
|
||||
read_entire_file :: proc{
|
||||
@@ -106,18 +150,23 @@ read_entire_file :: proc{
|
||||
read_entire_file_from_file,
|
||||
}
|
||||
|
||||
/*
|
||||
`read_entire_file_from_path` reads the entire named file `name` into memory allocated with `allocator`.
|
||||
A slice of bytes and an error is returned, if any error is encountered.
|
||||
*/
|
||||
@(require_results)
|
||||
read_entire_file_from_path :: proc(name: string, allocator: runtime.Allocator) -> (data: []byte, err: Error) {
|
||||
f, ferr := open(name)
|
||||
if ferr != nil {
|
||||
return nil, ferr
|
||||
}
|
||||
read_entire_file_from_path :: proc(name: string, allocator: runtime.Allocator, loc := #caller_location) -> (data: []byte, err: Error) {
|
||||
f := open(name) or_return
|
||||
defer close(f)
|
||||
return read_entire_file_from_file(f, allocator)
|
||||
return read_entire_file_from_file(f, allocator, loc)
|
||||
}
|
||||
|
||||
/*
|
||||
`read_entire_file_from_file` reads the entire file `f` into memory allocated with `allocator`.
|
||||
A slice of bytes and an error is returned, if any error is encountered.
|
||||
*/
|
||||
@(require_results)
|
||||
read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (data: []byte, err: Error) {
|
||||
read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator, loc := #caller_location) -> (data: []byte, err: Error) {
|
||||
size: int
|
||||
has_size := false
|
||||
if size64, serr := file_size(f); serr == nil {
|
||||
@@ -129,7 +178,7 @@ read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (d
|
||||
|
||||
if has_size && size > 0 {
|
||||
total: int
|
||||
data = make([]byte, size, allocator) or_return
|
||||
data = make([]byte, size, allocator, loc) or_return
|
||||
for total < len(data) {
|
||||
n: int
|
||||
n, err = read(f, data[total:])
|
||||
@@ -145,13 +194,13 @@ read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (d
|
||||
return
|
||||
} else {
|
||||
buffer: [1024]u8
|
||||
out_buffer := make([dynamic]u8, 0, 0, allocator)
|
||||
out_buffer := make([dynamic]u8, 0, 0, allocator, loc)
|
||||
total := 0
|
||||
for {
|
||||
n: int
|
||||
n, err = read(f, buffer[:])
|
||||
total += n
|
||||
append_elems(&out_buffer, ..buffer[:n]) or_return
|
||||
append_elems(&out_buffer, ..buffer[:n], loc=loc) or_return
|
||||
if err != nil {
|
||||
if err == .EOF || err == .Broken_Pipe {
|
||||
err = nil
|
||||
@@ -163,8 +212,23 @@ read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (d
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
`write_entire_file` writes the contents of `data` into named file `name`.
|
||||
It defaults with the permssions `perm := Permissions_Read_All + {.Write_User}`, and `truncate`s by default.
|
||||
An error is returned if any is encountered.
|
||||
*/
|
||||
write_entire_file :: proc{
|
||||
write_entire_file_from_bytes,
|
||||
write_entire_file_from_string,
|
||||
}
|
||||
|
||||
/*
|
||||
`write_entire_file_from_bytes` writes the contents of `data` into named file `name`.
|
||||
It defaults with the permssions `perm := Permissions_Read_All + {.Write_User}`, and `truncate`s by default.
|
||||
An error is returned if any is encountered.
|
||||
*/
|
||||
@(require_results)
|
||||
write_entire_file :: proc(name: string, data: []byte, perm: int = 0o644, truncate := true) -> Error {
|
||||
write_entire_file_from_bytes :: proc(name: string, data: []byte, perm := Permissions_Read_All + {.Write_User}, truncate := true) -> Error {
|
||||
flags := O_WRONLY|O_CREATE
|
||||
if truncate {
|
||||
flags |= O_TRUNC
|
||||
@@ -177,3 +241,14 @@ write_entire_file :: proc(name: string, data: []byte, perm: int = 0o644, truncat
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
`write_entire_file_from_string` writes the contents of `data` into named file `name`.
|
||||
It defaults with the permssions `perm := Permissions_Read_All + {.Write_User}`, and `truncate`s by default.
|
||||
An error is returned if any is encountered.
|
||||
*/
|
||||
@(require_results)
|
||||
write_entire_file_from_string :: proc(name: string, data: string, perm := Permissions_Read_All + {.Write_User}, truncate := true) -> Error {
|
||||
return write_entire_file(name, transmute([]byte)data, perm, truncate)
|
||||
}
|
||||
|
||||
@@ -51,8 +51,8 @@ init_std_files :: proc "contextless" () {
|
||||
}
|
||||
|
||||
@(init)
|
||||
init_preopens :: proc() {
|
||||
strip_prefixes :: proc(path: string) -> string {
|
||||
init_preopens :: proc "contextless" () {
|
||||
strip_prefixes :: proc "contextless" (path: string) -> string {
|
||||
path := path
|
||||
loop: for len(path) > 0 {
|
||||
switch {
|
||||
@@ -69,6 +69,8 @@ init_preopens :: proc() {
|
||||
return path
|
||||
}
|
||||
|
||||
context = runtime.default_context()
|
||||
|
||||
n: int
|
||||
n_loop: for fd := wasi.fd_t(3); ; fd += 1 {
|
||||
_, err := wasi.fd_prestat_get(fd)
|
||||
@@ -171,7 +173,7 @@ match_preopen :: proc(path: string) -> (wasi.fd_t, string, bool) {
|
||||
return match.fd, relative, true
|
||||
}
|
||||
|
||||
_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
|
||||
_open :: proc(name: string, flags: File_Flags, perm: Permissions) -> (f: ^File, err: Error) {
|
||||
dir_fd, relative, ok := match_preopen(name)
|
||||
if !ok {
|
||||
return nil, .Invalid_Path
|
||||
@@ -373,11 +375,11 @@ _fchdir :: proc(f: ^File) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_fchmod :: proc(f: ^File, mode: int) -> Error {
|
||||
_fchmod :: proc(f: ^File, mode: Permissions) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_chmod :: proc(name: string, mode: int) -> Error {
|
||||
_chmod :: proc(name: string, mode: Permissions) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import win32 "core:sys/windows"
|
||||
|
||||
INVALID_HANDLE :: ~uintptr(0)
|
||||
|
||||
S_IWRITE :: 0o200
|
||||
_ERROR_BAD_NETPATH :: 53
|
||||
MAX_RW :: 1<<30
|
||||
|
||||
@@ -81,7 +80,7 @@ _handle :: proc "contextless" (f: ^File) -> win32.HANDLE {
|
||||
return win32.HANDLE(_fd(f))
|
||||
}
|
||||
|
||||
_open_internal :: proc(name: string, flags: File_Flags, perm: int) -> (handle: uintptr, err: Error) {
|
||||
_open_internal :: proc(name: string, flags: File_Flags, perm: Permissions) -> (handle: uintptr, err: Error) {
|
||||
if len(name) == 0 {
|
||||
err = .Not_Exist
|
||||
return
|
||||
@@ -122,7 +121,7 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: int) -> (handle: u
|
||||
}
|
||||
|
||||
attrs: u32 = win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS
|
||||
if perm & S_IWRITE == 0 {
|
||||
if .Write_User not_in perm {
|
||||
attrs = win32.FILE_ATTRIBUTE_READONLY
|
||||
if create_mode == win32.CREATE_ALWAYS {
|
||||
// NOTE(bill): Open has just asked to create a file in read-only mode.
|
||||
@@ -150,7 +149,7 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: int) -> (handle: u
|
||||
}
|
||||
|
||||
|
||||
_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
|
||||
_open :: proc(name: string, flags: File_Flags, perm: Permissions) -> (f: ^File, err: Error) {
|
||||
flags := flags if flags != nil else {.Read}
|
||||
handle := _open_internal(name, flags, perm) or_return
|
||||
return _new_file(handle, name, file_allocator())
|
||||
@@ -193,7 +192,7 @@ _new_file :: proc(handle: uintptr, name: string, allocator: runtime.Allocator) -
|
||||
|
||||
|
||||
@(require_results)
|
||||
_open_buffered :: proc(name: string, buffer_size: uint, flags := File_Flags{.Read}, perm := 0o777) -> (f: ^File, err: Error) {
|
||||
_open_buffered :: proc(name: string, buffer_size: uint, flags := File_Flags{.Read}, perm: Permissions) -> (f: ^File, err: Error) {
|
||||
assert(buffer_size > 0)
|
||||
flags := flags if flags != nil else {.Read}
|
||||
handle := _open_internal(name, flags, perm) or_return
|
||||
@@ -744,7 +743,7 @@ _fchdir :: proc(f: ^File) -> Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
_fchmod :: proc(f: ^File, mode: int) -> Error {
|
||||
_fchmod :: proc(f: ^File, mode: Permissions) -> Error {
|
||||
if f == nil || f.impl == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -753,7 +752,7 @@ _fchmod :: proc(f: ^File, mode: int) -> Error {
|
||||
return _get_platform_error()
|
||||
}
|
||||
attrs := d.dwFileAttributes
|
||||
if mode & S_IWRITE != 0 {
|
||||
if .Write_User in mode {
|
||||
attrs &~= win32.FILE_ATTRIBUTE_READONLY
|
||||
} else {
|
||||
attrs |= win32.FILE_ATTRIBUTE_READONLY
|
||||
@@ -780,7 +779,7 @@ _chdir :: proc(name: string) -> Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
_chmod :: proc(name: string, mode: int) -> Error {
|
||||
_chmod :: proc(name: string, mode: Permissions) -> Error {
|
||||
f := open(name, {.Write}) or_return
|
||||
defer close(f)
|
||||
return _fchmod(f, mode)
|
||||
|
||||
@@ -2,6 +2,9 @@ package os2
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
/*
|
||||
Returns the default `heap_allocator` for this specific platform.
|
||||
*/
|
||||
@(require_results)
|
||||
heap_allocator :: proc() -> runtime.Allocator {
|
||||
return runtime.Allocator{
|
||||
|
||||
@@ -34,7 +34,7 @@ _mkdir_all :: proc(path: string, perm: int) -> Error {
|
||||
return .Exist
|
||||
}
|
||||
|
||||
clean_path := clean_path(path, temp_allocator)
|
||||
clean_path := clean_path(path, temp_allocator) or_return
|
||||
return internal_mkdir_all(clean_path)
|
||||
|
||||
internal_mkdir_all :: proc(path: string) -> Error {
|
||||
@@ -114,3 +114,7 @@ _get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err
|
||||
|
||||
return concatenate({"/", arg}, allocator)
|
||||
}
|
||||
|
||||
_get_absolute_path :: proc(path: string, allocator: runtime.Allocator) -> (absolute_path: string, err: Error) {
|
||||
return "", .Unsupported
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ delete_args :: proc "contextless" () {
|
||||
Exit the current process.
|
||||
*/
|
||||
exit :: proc "contextless" (code: int) -> ! {
|
||||
_exit(code)
|
||||
runtime.exit(code)
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -13,11 +13,6 @@ import "core:sys/linux"
|
||||
|
||||
PIDFD_UNASSIGNED :: ~uintptr(0)
|
||||
|
||||
@(private="package")
|
||||
_exit :: proc "contextless" (code: int) -> ! {
|
||||
linux.exit_group(i32(code))
|
||||
}
|
||||
|
||||
@(private="package")
|
||||
_get_uid :: proc() -> int {
|
||||
return int(linux.getuid())
|
||||
@@ -427,7 +422,8 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
strings.write_string(&exe_builder, executable_name)
|
||||
|
||||
exe_path = strings.to_cstring(&exe_builder) or_return
|
||||
if linux.access(exe_path, linux.X_OK) == .NONE {
|
||||
stat := linux.Stat{}
|
||||
if linux.stat(exe_path, &stat) == .NONE && .IFREG in stat.mode && .IXUSR in stat.mode {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
|
||||
@@ -10,10 +10,6 @@ import "core:strings"
|
||||
import kq "core:sys/kqueue"
|
||||
import "core:sys/posix"
|
||||
|
||||
_exit :: proc "contextless" (code: int) -> ! {
|
||||
posix.exit(i32(code))
|
||||
}
|
||||
|
||||
_get_uid :: proc() -> int {
|
||||
return int(posix.getuid())
|
||||
}
|
||||
|
||||
@@ -4,11 +4,7 @@ package os2
|
||||
import "base:runtime"
|
||||
|
||||
import "core:time"
|
||||
import "core:sys/wasm/wasi"
|
||||
|
||||
_exit :: proc "contextless" (code: int) -> ! {
|
||||
wasi.proc_exit(wasi.exitcode_t(code))
|
||||
}
|
||||
// import "core:sys/wasm/wasi"
|
||||
|
||||
_get_uid :: proc() -> int {
|
||||
return 0
|
||||
|
||||
@@ -7,11 +7,6 @@ import "core:strings"
|
||||
import win32 "core:sys/windows"
|
||||
import "core:time"
|
||||
|
||||
@(private="package")
|
||||
_exit :: proc "contextless" (code: int) -> ! {
|
||||
win32.ExitProcess(u32(code))
|
||||
}
|
||||
|
||||
@(private="package")
|
||||
_get_uid :: proc() -> int {
|
||||
return -1
|
||||
|
||||
+29
-5
@@ -6,13 +6,16 @@ import "core:time"
|
||||
|
||||
Fstat_Callback :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error)
|
||||
|
||||
/*
|
||||
`File_Info` describes a file and is returned from `stat`, `fstat`, and `lstat`.
|
||||
*/
|
||||
File_Info :: struct {
|
||||
fullpath: string,
|
||||
name: string,
|
||||
fullpath: string, // fullpath of the file
|
||||
name: string, // base name of the file
|
||||
|
||||
inode: u128, // might be zero if cannot be determined
|
||||
size: i64 `fmt:"M"`,
|
||||
mode: int `fmt:"o"`,
|
||||
inode: u128, // might be zero if cannot be determined
|
||||
size: i64 `fmt:"M"`, // length in bytes for regular files; system-dependent for other file types
|
||||
mode: Permissions, // file permission flags
|
||||
type: File_Type,
|
||||
|
||||
creation_time: time.Time,
|
||||
@@ -49,6 +52,10 @@ fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||
return {}, .Invalid_Callback
|
||||
}
|
||||
|
||||
/*
|
||||
`stat` returns a `File_Info` describing the named file from the file system.
|
||||
The resulting `File_Info` must be deleted with `file_info_delete`.
|
||||
*/
|
||||
@(require_results)
|
||||
stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||
return _stat(name, allocator)
|
||||
@@ -56,12 +63,21 @@ stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||
|
||||
lstat :: stat_do_not_follow_links
|
||||
|
||||
/*
|
||||
Returns a `File_Info` describing the named file from the file system.
|
||||
If the file is a symbolic link, the `File_Info` returns describes the symbolic link,
|
||||
rather than following the link.
|
||||
The resulting `File_Info` must be deleted with `file_info_delete`.
|
||||
*/
|
||||
@(require_results)
|
||||
stat_do_not_follow_links :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||
return _lstat(name, allocator)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Returns true if two `File_Info`s are equivalent.
|
||||
*/
|
||||
@(require_results)
|
||||
same_file :: proc(fi1, fi2: File_Info) -> bool {
|
||||
return _same_file(fi1, fi2)
|
||||
@@ -71,6 +87,10 @@ same_file :: proc(fi1, fi2: File_Info) -> bool {
|
||||
last_write_time :: modification_time
|
||||
last_write_time_by_name :: modification_time_by_path
|
||||
|
||||
/*
|
||||
Returns the modification time of the file `f`.
|
||||
The resolution of the timestamp is system-dependent.
|
||||
*/
|
||||
@(require_results)
|
||||
modification_time :: proc(f: ^File) -> (time.Time, Error) {
|
||||
temp_allocator := TEMP_ALLOCATOR_GUARD({})
|
||||
@@ -78,6 +98,10 @@ modification_time :: proc(f: ^File) -> (time.Time, Error) {
|
||||
return fi.modification_time, err
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the modification time of the named file `path`.
|
||||
The resolution of the timestamp is system-dependent.
|
||||
*/
|
||||
@(require_results)
|
||||
modification_time_by_path :: proc(path: string) -> (time.Time, Error) {
|
||||
temp_allocator := TEMP_ALLOCATOR_GUARD({})
|
||||
|
||||
@@ -26,7 +26,7 @@ _fstat_internal :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (fi: File
|
||||
case linux.S_IFREG: type = .Regular
|
||||
case linux.S_IFSOCK: type = .Socket
|
||||
}
|
||||
mode := int(0o7777 & transmute(u32)s.mode)
|
||||
mode := transmute(Permissions)(0o7777 & transmute(u32)s.mode)
|
||||
|
||||
// TODO: As of Linux 4.11, the new statx syscall can retrieve creation_time
|
||||
fi = File_Info {
|
||||
|
||||
@@ -14,7 +14,7 @@ internal_stat :: proc(stat: posix.stat_t, fullpath: string) -> (fi: File_Info) {
|
||||
fi.inode = u128(stat.st_ino)
|
||||
fi.size = i64(stat.st_size)
|
||||
|
||||
fi.mode = int(transmute(posix._mode_t)(stat.st_mode - posix.S_IFMT))
|
||||
fi.mode = transmute(Permissions)u32(transmute(posix._mode_t)(stat.st_mode - posix.S_IFMT))
|
||||
|
||||
fi.type = .Undetermined
|
||||
switch {
|
||||
|
||||
@@ -211,11 +211,11 @@ _file_type_from_create_file :: proc(wname: win32.wstring, create_file_attributes
|
||||
return file_type(h)
|
||||
}
|
||||
|
||||
_file_type_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (type: File_Type, mode: int) {
|
||||
_file_type_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (type: File_Type, mode: Permissions) {
|
||||
if file_attributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
|
||||
mode |= 0o444
|
||||
mode += Permissions_Write_All
|
||||
} else {
|
||||
mode |= 0o666
|
||||
mode += Permissions_Read_Write_All
|
||||
}
|
||||
|
||||
is_sym := false
|
||||
@@ -229,7 +229,7 @@ _file_type_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: wi
|
||||
type = .Symlink
|
||||
} else if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
type = .Directory
|
||||
mode |= 0o111
|
||||
mode += Permissions_Execute_All
|
||||
} else if h != nil {
|
||||
type = file_type(h)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ MAX_ATTEMPTS :: 1<<13 // Should be enough for everyone, right?
|
||||
|
||||
// Creates a new temperatory file in the directory `dir`.
|
||||
//
|
||||
// Opens the file for reading and writing, with 0o666 permissions, and returns the new `^File`.
|
||||
// Opens the file for reading and writing, with `Permissions_Read_Write_All` permissions, and returns the new `^File`.
|
||||
// The filename is generated by taking a pattern, and adding a randomized string to the end.
|
||||
// If the pattern includes an "*", the random string replaces the last "*".
|
||||
// If `dir` is an empty string, `temp_directory()` will be used.
|
||||
@@ -26,7 +26,7 @@ create_temp_file :: proc(dir, pattern: string) -> (f: ^File, err: Error) {
|
||||
attempts := 0
|
||||
for {
|
||||
name := concatenate_strings_from_buffer(name_buf[:], prefix, random_string(rand_buf[:]), suffix)
|
||||
f, err = open(name, {.Read, .Write, .Create, .Excl}, 0o666)
|
||||
f, err = open(name, {.Read, .Write, .Create, .Excl}, Permissions_Read_Write_All)
|
||||
if err == .Exist {
|
||||
close(f)
|
||||
attempts += 1
|
||||
@@ -80,6 +80,19 @@ make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator)
|
||||
}
|
||||
|
||||
temp_dir :: temp_directory
|
||||
|
||||
/*
|
||||
Returns the default directory to use for temporary files.
|
||||
|
||||
On Unix systems, it typically returns $TMPDIR if non-empty, otherwlse `/tmp`.
|
||||
On Windows, it uses `GetTempPathW`, returning the first non-empty value from one of the following:
|
||||
* `%TMP%`
|
||||
* `%TEMP%`
|
||||
* `%USERPROFILE %`
|
||||
* or the Windows directory
|
||||
See https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppathw for more information.
|
||||
On wasi, it returns `/tmp`.
|
||||
*/
|
||||
@(require_results)
|
||||
temp_directory :: proc(allocator: runtime.Allocator) -> (string, Error) {
|
||||
return _temp_dir(allocator)
|
||||
|
||||
+12
-12
@@ -2,7 +2,6 @@
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
import "core:encoding/ini"
|
||||
import "core:strings"
|
||||
|
||||
_user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
@@ -157,18 +156,19 @@ _xdg_user_dirs_lookup :: proc(xdg_key: string, allocator: runtime.Allocator) ->
|
||||
user_dirs_path := concatenate({config_dir, "/user-dirs.dirs"}, temp_allocator) or_return
|
||||
content := read_entire_file(user_dirs_path, temp_allocator) or_return
|
||||
|
||||
it := ini.Iterator{
|
||||
section = "",
|
||||
_src = string(content),
|
||||
options = ini.Options{
|
||||
comment = "#",
|
||||
key_lower_case = false,
|
||||
},
|
||||
}
|
||||
xdg_dirs := string(content)
|
||||
for line in strings.split_lines_iterator(&xdg_dirs) {
|
||||
if len(line) > 0 && line[0] == '#' {
|
||||
continue
|
||||
}
|
||||
|
||||
for k, v in ini.iterate(&it) {
|
||||
if k == xdg_key {
|
||||
return replace_environment_placeholders(v, allocator), nil
|
||||
equals := strings.index(line, "=")
|
||||
if equals > -1 {
|
||||
if line[:equals] == xdg_key {
|
||||
// Unquote to return a bare path string as we do on Windows
|
||||
val := strings.trim(line[equals+1:], "\"")
|
||||
return replace_environment_placeholders(val, allocator), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user