diff --git a/core/os/os2/file.odin b/core/os/os2/file.odin index 5375258ca..1da37dfa4 100644 --- a/core/os/os2/file.odin +++ b/core/os/os2/file.odin @@ -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, @@ -121,6 +122,9 @@ perm :: proc{ perm_number, } +/* + `perm_number` converts an integer value `perm` to the bit set `Permissions` +*/ @(require_results) perm_number :: proc "contextless" (perm: int) -> Permissions { return transmute(Permissions)u32(perm & 0o777) @@ -128,16 +132,34 @@ perm_number :: proc "contextless" (perm: int) -> Permissions { - +// `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}, Permissions_Default) + 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) @@ -151,7 +173,10 @@ open :: proc(name: string, flags := File_Flags{.Read}, perm := Permissions_Defau // 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()) @@ -161,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) @@ -189,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) @@ -196,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) @@ -203,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) @@ -210,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) @@ -217,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) @@ -224,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) @@ -231,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) @@ -238,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) } @@ -270,18 +364,35 @@ 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 +/* + 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) } @@ -300,6 +411,12 @@ fchange_mode :: proc(f: ^File, mode: Permissions) -> Error { 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) } @@ -307,27 +424,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({}) @@ -340,6 +475,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({}) @@ -350,7 +488,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)