core:os -> core:os/old && core:os/os2 -> core:os

This commit is contained in:
Jeroen van Rijn committed 2026-02-09 15:50:21 +01:00
1 parent 8ed264680b
commit e7dbabf668
169 files changed
+1716 -1718

No files matched your search

+5 -5
View File
@@ -2,11 +2,11 @@
A small `GZIP` unpacker.
Example:
import "core:bytes"
import os "core:os/os2"
import "core:compress"
import "core:compress/gzip"
import "core:fmt"
import "core:bytes"
import "core:os"
import "core:compress"
import "core:compress/gzip"
import "core:fmt"
// Small GZIP file with fextra, fname and fcomment present.
@private
+6 -6
View File
@@ -14,12 +14,12 @@ package compress_gzip
to be the input to a complementary TAR implementation.
*/
import "core:compress/zlib"
import "core:compress"
import os "core:os/os2"
import "core:io"
import "core:bytes"
import "core:hash"
import "core:compress/zlib"
import "core:compress"
import "core:os"
import "core:io"
import "core:bytes"
import "core:hash"
Magic :: enum u16le {
GZIP = 0x8b << 8 | 0x1f,
+1 -1
View File
@@ -3,7 +3,7 @@
package crypto_hash
import "core:io"
import os "core:os/os2"
import "core:os"
// `hash_file` will read the file provided by the given handle and return the
// computed digest in a newly allocated slice.
+1 -1
View File
@@ -6,7 +6,7 @@ Example:
import "core:fmt"
import "core:encoding/csv"
import os "core:os/os2"
import "core:os"
// Requires keeping the entire CSV file in memory at once
iterate_csv_from_string :: proc(filename: string) {
+1 -1
View File
@@ -2,7 +2,7 @@
#+build !js
package encoding_hxa
import os "core:os/os2"
import "core:os"
read_from_file :: proc(filename: string, print_error := false, allocator := context.allocator, loc := #caller_location) -> (file: File, err: Read_Error) {
context.allocator = allocator
+1 -1
View File
@@ -3,7 +3,7 @@
package encoding_ini
import "base:runtime"
import os "core:os/os2"
import "core:os"
load_map_from_path :: proc(path: string, allocator: runtime.Allocator, options := DEFAULT_OPTIONS) -> (m: Map, err: runtime.Allocator_Error, ok: bool) {
data, data_err := os.read_entire_file(path, allocator)
+1 -1
View File
@@ -2,7 +2,7 @@
#+build !js
package encoding_xml
import os "core:os/os2"
import "core:os"
// Load an XML file
load_from_file :: proc(filename: string, options := DEFAULT_OPTIONS, error_handler := default_error_handler, allocator := context.allocator) -> (doc: ^Document, err: Error) {
+1 -1
View File
@@ -2,7 +2,7 @@ package flags
import "base:runtime"
import "core:net"
import os "core:os/os2"
import "core:os"
Parse_Error_Reason :: enum {
None,
+1 -1
View File
@@ -4,7 +4,7 @@ import "base:runtime"
import "core:flags"
import "core:fmt"
import "core:net"
import os "core:os/os2"
import "core:os"
import "core:time/datetime"
+12 -12
View File
@@ -1,18 +1,18 @@
#+private
package flags
import "base:intrinsics"
import "base:runtime"
import "core:fmt"
import "core:mem"
import "core:net"
@(require) import os "core:os/os2"
import "core:reflect"
import "core:strconv"
import "core:strings"
@require import "core:time"
@require import "core:time/datetime"
import "core:unicode/utf8"
import "base:intrinsics"
import "base:runtime"
import "core:fmt"
import "core:mem"
import "core:net"
@(require) import "core:os"
import "core:reflect"
import "core:strconv"
import "core:strings"
@(require) import "core:time"
@(require) import "core:time/datetime"
import "core:unicode/utf8"
@(optimization_mode="favor_size")
parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info: ^runtime.Type_Info) -> bool {
+8 -8
View File
@@ -1,14 +1,14 @@
#+private
package flags
@require import "base:runtime"
@require import "core:container/bit_array"
@require import "core:fmt"
@require import "core:mem"
@require import os "core:os/os2"
@require import "core:reflect"
@require import "core:strconv"
@require import "core:strings"
@require import "base:runtime"
@require import "core:container/bit_array"
@require import "core:fmt"
@require import "core:mem"
@require import "core:os"
@require import "core:reflect"
@require import "core:strconv"
@require import "core:strings"
// This proc is used to assert that `T` meets the expectations of the library.
@(optimization_mode="favor_size", disabled=ODIN_DISABLE_ASSERT)
+3 -3
View File
@@ -1,9 +1,9 @@
package flags
import "core:fmt"
@require import os "core:os/os2"
import "core:fmt"
@require import "core:os"
@require import "core:path/filepath"
import "core:strings"
import "core:strings"
/*
Parse any arguments into an annotated struct or exit if there was an error.
+4 -4
View File
@@ -3,10 +3,10 @@
#+build !orca
package fmt
import "base:runtime"
import os "core:os/os2"
import "core:io"
import "core:bufio"
import "base:runtime"
import "core:os"
import "core:io"
import "core:bufio"
// NOTE(Jeroen): The other option is to deprecate `fprint*` and make it an alias for `wprint*`, using File.stream directly.
+2 -2
View File
@@ -1,8 +1,8 @@
#+build !js
package core_image_bmp
import os "core:os/os2"
import "core:bytes"
import "core:os"
import "core:bytes"
load :: proc{load_from_file, load_from_bytes, load_from_context}
+1 -1
View File
@@ -1,7 +1,7 @@
#+build !js
package image
import os "core:os/os2"
import "core:os"
load :: proc{
load_from_bytes,
+1 -1
View File
@@ -1,7 +1,7 @@
#+build !js
package jpeg
import os "core:os/os2"
import "core:os"
load :: proc{load_from_file, load_from_bytes, load_from_context}
+1 -1
View File
@@ -1,7 +1,7 @@
#+build !js
package netpbm
import os "core:os/os2"
import "core:os"
load :: proc {
load_from_file,
+1 -1
View File
@@ -1,7 +1,7 @@
#+build !js
package png
import os "core:os/os2"
import "core:os"
load :: proc{load_from_file, load_from_bytes, load_from_context}
+2 -2
View File
@@ -1,8 +1,8 @@
#+build !js
package qoi
import os "core:os/os2"
import "core:bytes"
import "core:os"
import "core:bytes"
load :: proc{load_from_file, load_from_bytes, load_from_context}
+2 -2
View File
@@ -1,8 +1,8 @@
#+build !js
package tga
import os "core:os/os2"
import "core:bytes"
import "core:os"
import "core:bytes"
load :: proc{load_from_file, load_from_bytes, load_from_context}
+7 -7
View File
@@ -3,13 +3,13 @@
#+build !js
package log
import "base:runtime"
import "core:fmt"
import "core:strings"
import os "core:os/os2"
import "core:terminal"
import "core:terminal/ansi"
import "core:time"
import "base:runtime"
import "core:fmt"
import "core:strings"
import "core:os"
import "core:terminal"
import "core:terminal/ansi"
import "core:time"
Level_Headers := [?]string{
0..<10 = "[DEBUG] --- ",
+1 -1
View File
@@ -18,7 +18,7 @@ package math_big
*/
import "core:mem"
import os "core:os/os2"
import "core:os"
/*
We might add functions to read and write byte-encoded Ints from/to files, using `int_to_bytes_*` functions.
+2 -2
View File
@@ -5,8 +5,8 @@ virtual.Arena usage
Example:
// Source: https://github.com/odin-lang/examples/blob/master/arena_allocator/arena_allocator.odin
import "core:fmt"
import os "core:os/os2"
import "core:fmt"
import "core:os"
// virtual package implements a multi-purpose arena allocator. If you are on a
// platform that does not support virtual memory, then there is also a similar
+1 -1
View File
@@ -2,7 +2,7 @@
#+build !js
package mem_virtual
import os "core:os/os2"
import "core:os"
map_file :: proc{
map_file_from_path,
+1 -1
View File
@@ -2,7 +2,7 @@
#+private
package net
import os "core:os/os2"
import "core:os"
load_resolv_conf :: proc(resolv_conf_path: string, allocator := context.allocator) -> (name_servers: []Endpoint, ok: bool) {
context.allocator = allocator
+7 -7
View File
@@ -1,12 +1,12 @@
package odin_parser
import "core:odin/tokenizer"
import "core:odin/ast"
import "core:path/filepath"
import "core:fmt"
import os "core:os/os2"
import "core:slice"
import "core:strings"
import "core:odin/tokenizer"
import "core:odin/ast"
import "core:path/filepath"
import "core:fmt"
import "core:os"
import "core:slice"
import "core:strings"
collect_package :: proc(path: string) -> (pkg: ^ast.Package, success: bool) {
NO_POS :: tokenizer.Pos{}
File renamed without changes.
+2 -2
View File
@@ -160,8 +160,8 @@ extend its lifetime.
Example:
package main
import "core:fmt"
import os "core:os/os2"
import "core:fmt"
import "core:os"
main :: proc() {
f, oerr := os.open("core")
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -136,9 +136,9 @@ If an error occurred opening a directory, you may get zero'd info struct and
Example:
package main
import "core:fmt"
import "core:strings"
import os "core:os/os2"
import "core:fmt"
import "core:strings"
import "core:os"
main :: proc() {
w := os.walker_create("core")
File renamed without changes.
+113 -83
View File
@@ -1,114 +1,144 @@
package os
#+private
package os2
import win32 "core:sys/windows"
import "core:strings"
import "base:runtime"
import "core:time"
import win32 "core:sys/windows"
@(private="file")
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
// Ignore "." and ".."
if d.cFileName[0] == '.' && d.cFileName[1] == 0 {
return
}
if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 {
return
}
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}, Permissions_Read_Write_All) or_else 0)
defer win32.CloseHandle(handle)
fi.fullpath = path
fi.name = basename(path)
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
fi.type, fi.mode = _file_type_mode_from_file_attributes(d.dwFileAttributes, handle, d.dwReserved0)
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
if file_id_info: win32.FILE_ID_INFO; handle != nil && win32.GetFileInformationByHandleEx(handle, .FileIdInfo, &file_id_info, size_of(file_id_info)) {
#assert(size_of(fi.inode) == size_of(file_id_info.FileId))
#assert(size_of(fi.inode) == 16)
runtime.mem_copy_non_overlapping(&fi.inode, &file_id_info.FileId, 16)
}
return
}
Read_Directory_Iterator_Impl :: struct {
find_data: win32.WIN32_FIND_DATAW,
find_handle: win32.HANDLE,
path: string,
prev_fi: File_Info,
no_more_files: bool,
}
@(require_results)
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Error) {
@(require_results)
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) {
// Ignore "." and ".."
if d.cFileName[0] == '.' && d.cFileName[1] == 0 {
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
for !it.impl.no_more_files {
err: Error
file_info_delete(it.impl.prev_fi, file_allocator())
it.impl.prev_fi = {}
fi, err = find_data_to_file_info(it.impl.path, &it.impl.find_data, file_allocator())
if err != nil {
read_directory_iterator_set_error(it, it.impl.path, err)
return
}
if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 {
return
}
path := strings.concatenate({base_path, `\`, win32.utf16_to_utf8(d.cFileName[:]) or_else ""})
fi.fullpath = path
fi.name = basename(path)
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
if d.dwFileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
fi.mode |= 0o444
} else {
fi.mode |= 0o666
if fi.name != "" {
it.impl.prev_fi = fi
ok = true
index = it.index
it.index += 1
}
is_sym := false
if d.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_Point == 0 {
is_sym = false
} else {
is_sym = d.dwReserved0 == win32.IO_REPARSE_TAG_SYMLINK || d.dwReserved0 == win32.IO_REPARSE_TAG_MOUNT_POINT
}
if is_sym {
fi.mode |= File_Mode_Sym_Link
} else {
if d.dwFileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
fi.mode |= 0o111 | File_Mode_Dir
if !win32.FindNextFileW(it.impl.find_handle, &it.impl.find_data) {
e := _get_platform_error()
if pe, _ := is_platform_error(e); pe != i32(win32.ERROR_NO_MORE_FILES) {
read_directory_iterator_set_error(it, it.impl.path, e)
}
// fi.mode |= file_type_mode(h);
it.impl.no_more_files = true
}
if ok {
return
}
}
return
}
windows_set_file_info_times(&fi, d)
_read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
it.impl.no_more_files = false
fi.is_dir = fi.mode & File_Mode_Dir != 0
if f == nil || f.impl == nil {
read_directory_iterator_set_error(it, "", .Invalid_File)
return
}
if fd == 0 {
return nil, ERROR_INVALID_HANDLE
it.f = f
impl := (^File_Impl)(f.impl)
// NOTE: Allow calling `init` to target a new directory with the same iterator - reset idx.
if it.impl.find_handle != nil {
win32.FindClose(it.impl.find_handle)
}
if it.impl.path != "" {
delete(it.impl.path, file_allocator())
}
context.allocator = allocator
h := win32.HANDLE(fd)
dir_fi, _ := file_info_from_get_file_information_by_handle("", h)
if !dir_fi.is_dir {
return nil, .Not_Dir
}
n := n
size := n
if n <= 0 {
n = -1
size = 100
}
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
wpath := cleanpath_from_handle_u16(fd, context.temp_allocator) or_return
if len(wpath) == 0 {
if !is_directory(impl.name) {
read_directory_iterator_set_error(it, impl.name, .Invalid_Dir)
return
}
dfi := make([dynamic]File_Info, 0, size) or_return
wpath := string16(impl.wname)
temp_allocator := TEMP_ALLOCATOR_GUARD({})
wpath_search := make([]u16, len(wpath)+3, context.temp_allocator) or_return
wpath_search := make([]u16, len(wpath)+3, temp_allocator)
copy(wpath_search, wpath)
wpath_search[len(wpath)+0] = '\\'
wpath_search[len(wpath)+1] = '*'
wpath_search[len(wpath)+2] = 0
path := cleanpath_from_buf(wpath)
defer delete(path)
find_data := &win32.WIN32_FIND_DATAW{}
find_handle := win32.FindFirstFileW(cstring16(raw_data(wpath_search)), find_data)
if find_handle == win32.INVALID_HANDLE_VALUE {
err = get_last_error()
return dfi[:], err
it.impl.find_handle = win32.FindFirstFileW(cstring16(raw_data(wpath_search)), &it.impl.find_data)
if it.impl.find_handle == win32.INVALID_HANDLE_VALUE {
read_directory_iterator_set_error(it, impl.name, _get_platform_error())
return
}
defer win32.FindClose(find_handle)
for n != 0 {
fi: File_Info
fi = find_data_to_file_info(path, find_data)
if fi.name != "" {
append(&dfi, fi)
n -= 1
}
if !win32.FindNextFileW(find_handle, find_data) {
e := get_last_error()
if e == ERROR_NO_MORE_FILES {
break
}
return dfi[:], e
}
defer if it.err.err != nil {
win32.FindClose(it.impl.find_handle)
}
return dfi[:], nil
err: Error
it.impl.path, err = _cleanpath_from_buf(wpath, file_allocator())
if err != nil {
read_directory_iterator_set_error(it, impl.name, err)
}
return
}
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
if it.f == nil {
return
}
file_info_delete(it.impl.prev_fi, file_allocator())
delete(it.impl.path, file_allocator())
win32.FindClose(it.impl.find_handle)
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+82 -80
View File
@@ -1,30 +1,37 @@
package os
#+private
package os2
import win32 "core:sys/windows"
import "base:runtime"
// lookup_env gets the value of the environment variable named by the key
// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true
// Otherwise the returned value will be empty and the boolean will be false
// NOTE: the value will be allocated with the supplied allocator
@(require_results)
lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
_lookup_env_alloc :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
if key == "" {
return
}
wkey := win32.utf8_to_wstring(key)
n := win32.GetEnvironmentVariableW(wkey, nil, 0)
if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND {
return "", false
}
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
wkey, _ := win32_utf8_to_wstring(key, temp_allocator)
n := win32.GetEnvironmentVariableW(wkey, nil, 0)
if n == 0 {
err := win32.GetLastError()
if err == win32.ERROR_ENVVAR_NOT_FOUND {
return "", false
}
return "", true
}
b := make([]u16, n+1, temp_allocator)
b, _ := make([dynamic]u16, n, context.temp_allocator)
n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND {
if n == 0 {
err := win32.GetLastError()
if err == win32.ERROR_ENVVAR_NOT_FOUND {
return "", false
}
return "", false
}
value, _ = win32.utf16_to_utf8(b[:n], allocator)
value = win32_utf16_to_utf8(string16(b[:n]), allocator) or_else ""
found = true
return
}
@@ -33,7 +40,7 @@ lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value:
// Note that it is limited to environment names and values of 512 utf-16 values each
// due to the necessary utf-8 <> utf-16 conversion.
@(require_results)
lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
_lookup_env_buf :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
key_buf: [513]u16
wkey := win32.utf8_to_wstring(key_buf[:], key)
if wkey == nil {
@@ -57,78 +64,28 @@ lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error)
return value, nil
}
lookup_env :: proc{lookup_env_alloc, lookup_env_buffer}
_lookup_env :: proc{_lookup_env_alloc, _lookup_env_buf}
// get_env retrieves the value of the environment variable named by the key
// It returns the value, which will be empty if the variable is not present
// To distinguish between an empty value and an unset value, use lookup_env
// NOTE: the value will be allocated with the supplied allocator
@(require_results)
get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator)
return
}
@(require_results)
get_env_buf :: proc(buf: []u8, key: string) -> (value: string) {
value, _ = lookup_env(buf, key)
return
}
get_env :: proc{get_env_alloc, get_env_buf}
// set_env sets the value of the environment variable named by the key
set_env :: proc(key, value: string) -> Error {
k := win32.utf8_to_wstring(key)
v := win32.utf8_to_wstring(value)
_set_env :: proc(key, value: string) -> Error {
temp_allocator := TEMP_ALLOCATOR_GUARD({})
k := win32_utf8_to_wstring(key, temp_allocator) or_return
v := win32_utf8_to_wstring(value, temp_allocator) or_return
if !win32.SetEnvironmentVariableW(k, v) {
return get_last_error()
return _get_platform_error()
}
return nil
}
// unset_env unsets a single environment variable
unset_env :: proc(key: string) -> Error {
k := win32.utf8_to_wstring(key)
if !win32.SetEnvironmentVariableW(k, nil) {
return get_last_error()
}
return nil
_unset_env :: proc(key: string) -> bool {
temp_allocator := TEMP_ALLOCATOR_GUARD({})
k, _ := win32_utf8_to_wstring(key, temp_allocator)
return bool(win32.SetEnvironmentVariableW(k, nil))
}
// environ returns a copy of strings representing the environment, in the form "key=value"
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator
@(require_results)
environ :: proc(allocator := context.allocator) -> []string {
envs := ([^]win32.WCHAR)(win32.GetEnvironmentStringsW())
if envs == nil {
return nil
}
defer win32.FreeEnvironmentStringsW(envs)
r, err := make([dynamic]string, 0, 50, allocator)
if err != nil {
return nil
}
for from, i := 0, 0; true; i += 1 {
if c := envs[i]; c == 0 {
if i <= from {
break
}
append(&r, win32.utf16_to_utf8(envs[from:i], allocator) or_else "")
from = i + 1
}
}
return r[:]
}
// clear_env deletes all environment variables
clear_env :: proc() {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
envs := environ(context.temp_allocator)
_clear_env :: proc() {
temp_allocator := TEMP_ALLOCATOR_GUARD({})
envs, _ := environ(temp_allocator)
for env in envs {
for j in 1..<len(env) {
if env[j] == '=' {
@@ -138,3 +95,48 @@ clear_env :: proc() {
}
}
}
_environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error) {
envs := win32.GetEnvironmentStringsW()
if envs == nil {
return
}
defer win32.FreeEnvironmentStringsW(envs)
n := 0
for from, i, p := 0, 0, envs; true; i += 1 {
c := ([^]u16)(p)[i]
if c == 0 {
if i <= from {
break
}
n += 1
from = i + 1
}
}
r := make([dynamic]string, 0, n, allocator) or_return
defer if err != nil {
for e in r {
delete(e, allocator)
}
delete(r)
}
for from, i, p := 0, 0, envs; true; i += 1 {
c := ([^]u16)(p)[i]
if c == 0 {
if i <= from {
break
}
w := ([^]u16)(p)[from:i]
s := win32_utf16_to_utf8(w, allocator) or_return
append(&r, s)
from = i + 1
}
}
environ = r[:]
return
}
+43 -214
View File
@@ -1,13 +1,12 @@
package os
package os2
import "base:intrinsics"
import "base:runtime"
import "core:io"
import "base:runtime"
Platform_Error :: _Platform_Error
#assert(size_of(Platform_Error) <= 4)
#assert(intrinsics.type_has_nil(Platform_Error))
/*
General errors that are common within this package which cannot
be categorized by `io.Error` nor `runtime.Allocator_Error`.
*/
General_Error :: enum u32 {
None,
@@ -22,39 +21,43 @@ General_Error :: enum u32 {
Invalid_Dir,
Invalid_Path,
Invalid_Callback,
Invalid_Command,
Pattern_Has_Separator,
Pattern_Syntax_Error, // Indicates an error in `glob` or `match` pattern.
File_Is_Pipe,
Not_Dir,
// Environment variable not found.
No_HOME_Variable,
Env_Var_Not_Found,
}
// A platform specific error
Platform_Error :: _Platform_Error
Errno :: Error // alias for legacy use
/*
`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,
runtime.Allocator_Error,
Platform_Error,
}
#assert(size_of(Error) == 8)
#assert(size_of(Error) == size_of(u64))
ERROR_NONE :: Error{}
ERROR_EOF :: io.Error.EOF
// 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 "contextless" (ferr: Error) -> (err: i32, ok: bool) {
is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
v := ferr.(Platform_Error) or_else {}
return i32(v), i32(v) != 0
}
// Attempts to return the error `ferr` as a string without any allocation
@(require_results)
error_string :: proc "contextless" (ferr: Error) -> string {
error_string :: proc(ferr: Error) -> string {
if ferr == nil {
return ""
}
@@ -62,18 +65,19 @@ error_string :: proc "contextless" (ferr: Error) -> string {
case General_Error:
switch e {
case .None: return ""
case .Exist: return "file already exists"
case .Not_Exist: return "file does not exist"
case .Timeout: return "i/o timeout"
case .Broken_Pipe: return "Broken pipe"
case .Invalid_File: return "invalid file"
case .Invalid_Dir: return "invalid directory"
case .Invalid_Path: return "invalid path"
case .Invalid_Callback: return "invalid callback"
case .Pattern_Has_Separator: return "pattern has separator"
case .File_Is_Pipe: return "file is pipe"
case .Not_Dir: return "file is not directory"
case .Env_Var_Not_Found: return "environment variable not found"
case .Exist: return "file already exists"
case .Not_Exist: return "file does not exist"
case .Timeout: return "i/o timeout"
case .Broken_Pipe: return "Broken pipe"
case .Invalid_File: return "invalid file"
case .Invalid_Dir: return "invalid directory"
case .Invalid_Path: return "invalid path"
case .Invalid_Callback: return "invalid callback"
case .Invalid_Command: return "invalid command"
case .Pattern_Has_Separator: return "pattern has separator"
case .Pattern_Syntax_Error: return "glob pattern syntax error"
case .No_HOME_Variable: return "no $HOME variable"
case .Env_Var_Not_Found: return "environment variable not found"
}
case io.Error:
switch e {
@@ -106,210 +110,35 @@ error_string :: proc "contextless" (ferr: Error) -> string {
case .Mode_Not_Implemented: return "allocator mode not implemented"
}
case Platform_Error:
return _error_string(e)
return _error_string(i32(e))
}
return "unknown error"
}
print_error :: proc(f: Handle, ferr: Error, msg: string) -> (n: int, err: 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)
// msg + ": " + err_str + '\n'
length := len(msg) + 2 + len(err_str) + 1
buf_ := intrinsics.alloca(length, 1)
buf := buf_[:length]
buf := make([]u8, length, temp_allocator)
copy(buf, msg)
buf[len(msg)] = ':'
buf[len(msg) + 1] = ' '
copy(buf[len(msg) + 2:], err_str)
buf[length - 1] = '\n'
return write(f, buf)
write(f, buf)
}
@(require_results, private)
_error_string :: proc "contextless" (e: Platform_Error) -> string where intrinsics.type_is_enum(Platform_Error) {
if e == nil {
return ""
}
when ODIN_OS == .Darwin {
if s := string(_darwin_string_error(i32(e))); s != "" {
return s
}
}
when ODIN_OS != .Linux {
@(require_results)
binary_search :: proc "contextless" (array: $A/[]$T, key: T) -> (index: int, found: bool) #no_bounds_check {
n := len(array)
left, right := 0, n
for left < right {
mid := int(uint(left+right) >> 1)
if array[mid] < key {
left = mid+1
} else {
// equal or greater
right = mid
}
}
return left, left < n && array[left] == key
}
err := runtime.Type_Info_Enum_Value(e)
ti := &runtime.type_info_base(type_info_of(Platform_Error)).variant.(runtime.Type_Info_Enum)
if idx, ok := binary_search(ti.values, err); ok {
return ti.names[idx]
}
} else {
@(rodata, static)
pe_strings := [Platform_Error]string{
.NONE = "",
.EPERM = "Operation not permitted",
.ENOENT = "No such file or directory",
.ESRCH = "No such process",
.EINTR = "Interrupted system call",
.EIO = "Input/output error",
.ENXIO = "No such device or address",
.E2BIG = "Argument list too long",
.ENOEXEC = "Exec format error",
.EBADF = "Bad file descriptor",
.ECHILD = "No child processes",
.EAGAIN = "Resource temporarily unavailable",
.ENOMEM = "Cannot allocate memory",
.EACCES = "Permission denied",
.EFAULT = "Bad address",
.ENOTBLK = "Block device required",
.EBUSY = "Device or resource busy",
.EEXIST = "File exists",
.EXDEV = "Invalid cross-device link",
.ENODEV = "No such device",
.ENOTDIR = "Not a directory",
.EISDIR = "Is a directory",
.EINVAL = "Invalid argument",
.ENFILE = "Too many open files in system",
.EMFILE = "Too many open files",
.ENOTTY = "Inappropriate ioctl for device",
.ETXTBSY = "Text file busy",
.EFBIG = "File too large",
.ENOSPC = "No space left on device",
.ESPIPE = "Illegal seek",
.EROFS = "Read-only file system",
.EMLINK = "Too many links",
.EPIPE = "Broken pipe",
.EDOM = "Numerical argument out of domain",
.ERANGE = "Numerical result out of range",
.EDEADLK = "Resource deadlock avoided",
.ENAMETOOLONG = "File name too long",
.ENOLCK = "No locks available",
.ENOSYS = "Function not implemented",
.ENOTEMPTY = "Directory not empty",
.ELOOP = "Too many levels of symbolic links",
.EUNKNOWN_41 = "Unknown Error (41)",
.ENOMSG = "No message of desired type",
.EIDRM = "Identifier removed",
.ECHRNG = "Channel number out of range",
.EL2NSYNC = "Level 2 not synchronized",
.EL3HLT = "Level 3 halted",
.EL3RST = "Level 3 reset",
.ELNRNG = "Link number out of range",
.EUNATCH = "Protocol driver not attached",
.ENOCSI = "No CSI structure available",
.EL2HLT = "Level 2 halted",
.EBADE = "Invalid exchange",
.EBADR = "Invalid request descriptor",
.EXFULL = "Exchange full",
.ENOANO = "No anode",
.EBADRQC = "Invalid request code",
.EBADSLT = "Invalid slot",
.EUNKNOWN_58 = "Unknown Error (58)",
.EBFONT = "Bad font file format",
.ENOSTR = "Device not a stream",
.ENODATA = "No data available",
.ETIME = "Timer expired",
.ENOSR = "Out of streams resources",
.ENONET = "Machine is not on the network",
.ENOPKG = "Package not installed",
.EREMOTE = "Object is remote",
.ENOLINK = "Link has been severed",
.EADV = "Advertise error",
.ESRMNT = "Srmount error",
.ECOMM = "Communication error on send",
.EPROTO = "Protocol error",
.EMULTIHOP = "Multihop attempted",
.EDOTDOT = "RFS specific error",
.EBADMSG = "Bad message",
.EOVERFLOW = "Value too large for defined data type",
.ENOTUNIQ = "Name not unique on network",
.EBADFD = "File descriptor in bad state",
.EREMCHG = "Remote address changed",
.ELIBACC = "Can not access a needed shared library",
.ELIBBAD = "Accessing a corrupted shared library",
.ELIBSCN = ".lib section in a.out corrupted",
.ELIBMAX = "Attempting to link in too many shared libraries",
.ELIBEXEC = "Cannot exec a shared library directly",
.EILSEQ = "Invalid or incomplete multibyte or wide character",
.ERESTART = "Interrupted system call should be restarted",
.ESTRPIPE = "Streams pipe error",
.EUSERS = "Too many users",
.ENOTSOCK = "Socket operation on non-socket",
.EDESTADDRREQ = "Destination address required",
.EMSGSIZE = "Message too long",
.EPROTOTYPE = "Protocol wrong type for socket",
.ENOPROTOOPT = "Protocol not available",
.EPROTONOSUPPORT = "Protocol not supported",
.ESOCKTNOSUPPORT = "Socket type not supported",
.EOPNOTSUPP = "Operation not supported",
.EPFNOSUPPORT = "Protocol family not supported",
.EAFNOSUPPORT = "Address family not supported by protocol",
.EADDRINUSE = "Address already in use",
.EADDRNOTAVAIL = "Cannot assign requested address",
.ENETDOWN = "Network is down",
.ENETUNREACH = "Network is unreachable",
.ENETRESET = "Network dropped connection on reset",
.ECONNABORTED = "Software caused connection abort",
.ECONNRESET = "Connection reset by peer",
.ENOBUFS = "No buffer space available",
.EISCONN = "Transport endpoint is already connected",
.ENOTCONN = "Transport endpoint is not connected",
.ESHUTDOWN = "Cannot send after transport endpoint shutdown",
.ETOOMANYREFS = "Too many references: cannot splice",
.ETIMEDOUT = "Connection timed out",
.ECONNREFUSED = "Connection refused",
.EHOSTDOWN = "Host is down",
.EHOSTUNREACH = "No route to host",
.EALREADY = "Operation already in progress",
.EINPROGRESS = "Operation now in progress",
.ESTALE = "Stale file handle",
.EUCLEAN = "Structure needs cleaning",
.ENOTNAM = "Not a XENIX named type file",
.ENAVAIL = "No XENIX semaphores available",
.EISNAM = "Is a named type file",
.EREMOTEIO = "Remote I/O error",
.EDQUOT = "Disk quota exceeded",
.ENOMEDIUM = "No medium found",
.EMEDIUMTYPE = "Wrong medium type",
.ECANCELED = "Operation canceled",
.ENOKEY = "Required key not available",
.EKEYEXPIRED = "Key has expired",
.EKEYREVOKED = "Key has been revoked",
.EKEYREJECTED = "Key was rejected by service",
.EOWNERDEAD = "Owner died",
.ENOTRECOVERABLE = "State not recoverable",
.ERFKILL = "Operation not possible due to RF-kill",
.EHWPOISON = "Memory page has hardware error",
}
if Platform_Error.NONE <= e && e <= max(Platform_Error) {
return pe_strings[e]
}
}
return "<unknown platform error>"
}
@(private, require_results)
// 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
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+114
View File
@@ -0,0 +1,114 @@
package os
import win32 "core:sys/windows"
import "core:strings"
import "base:runtime"
@(require_results)
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Error) {
@(require_results)
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) {
// Ignore "." and ".."
if d.cFileName[0] == '.' && d.cFileName[1] == 0 {
return
}
if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 {
return
}
path := strings.concatenate({base_path, `\`, win32.utf16_to_utf8(d.cFileName[:]) or_else ""})
fi.fullpath = path
fi.name = basename(path)
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
if d.dwFileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
fi.mode |= 0o444
} else {
fi.mode |= 0o666
}
is_sym := false
if d.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_Point == 0 {
is_sym = false
} else {
is_sym = d.dwReserved0 == win32.IO_REPARSE_TAG_SYMLINK || d.dwReserved0 == win32.IO_REPARSE_TAG_MOUNT_POINT
}
if is_sym {
fi.mode |= File_Mode_Sym_Link
} else {
if d.dwFileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
fi.mode |= 0o111 | File_Mode_Dir
}
// fi.mode |= file_type_mode(h);
}
windows_set_file_info_times(&fi, d)
fi.is_dir = fi.mode & File_Mode_Dir != 0
return
}
if fd == 0 {
return nil, ERROR_INVALID_HANDLE
}
context.allocator = allocator
h := win32.HANDLE(fd)
dir_fi, _ := file_info_from_get_file_information_by_handle("", h)
if !dir_fi.is_dir {
return nil, .Not_Dir
}
n := n
size := n
if n <= 0 {
n = -1
size = 100
}
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
wpath := cleanpath_from_handle_u16(fd, context.temp_allocator) or_return
if len(wpath) == 0 {
return
}
dfi := make([dynamic]File_Info, 0, size) or_return
wpath_search := make([]u16, len(wpath)+3, context.temp_allocator) or_return
copy(wpath_search, wpath)
wpath_search[len(wpath)+0] = '\\'
wpath_search[len(wpath)+1] = '*'
wpath_search[len(wpath)+2] = 0
path := cleanpath_from_buf(wpath)
defer delete(path)
find_data := &win32.WIN32_FIND_DATAW{}
find_handle := win32.FindFirstFileW(cstring16(raw_data(wpath_search)), find_data)
if find_handle == win32.INVALID_HANDLE_VALUE {
err = get_last_error()
return dfi[:], err
}
defer win32.FindClose(find_handle)
for n != 0 {
fi: File_Info
fi = find_data_to_file_info(path, find_data)
if fi.name != "" {
append(&dfi, fi)
n -= 1
}
if !win32.FindNextFileW(find_handle, find_data) {
e := get_last_error()
if e == ERROR_NO_MORE_FILES {
break
}
return dfi[:], e
}
}
return dfi[:], nil
}
+140
View File
@@ -0,0 +1,140 @@
package os
import win32 "core:sys/windows"
import "base:runtime"
// lookup_env gets the value of the environment variable named by the key
// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true
// Otherwise the returned value will be empty and the boolean will be false
// NOTE: the value will be allocated with the supplied allocator
@(require_results)
lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
if key == "" {
return
}
wkey := win32.utf8_to_wstring(key)
n := win32.GetEnvironmentVariableW(wkey, nil, 0)
if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND {
return "", false
}
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
b, _ := make([dynamic]u16, n, context.temp_allocator)
n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND {
return "", false
}
value, _ = win32.utf16_to_utf8(b[:n], allocator)
found = true
return
}
// This version of `lookup_env` doesn't allocate and instead requires the user to provide a buffer.
// Note that it is limited to environment names and values of 512 utf-16 values each
// due to the necessary utf-8 <> utf-16 conversion.
@(require_results)
lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
key_buf: [513]u16
wkey := win32.utf8_to_wstring(key_buf[:], key)
if wkey == nil {
return "", .Buffer_Full
}
n2 := win32.GetEnvironmentVariableW(wkey, nil, 0)
if n2 == 0 {
return "", .Env_Var_Not_Found
}
val_buf: [513]u16
n2 = win32.GetEnvironmentVariableW(wkey, raw_data(val_buf[:]), u32(len(val_buf[:])))
if n2 == 0 {
return "", .Env_Var_Not_Found
} else if int(n2) > len(buf) {
return "", .Buffer_Full
}
value = win32.utf16_to_utf8(buf, val_buf[:n2])
return value, nil
}
lookup_env :: proc{lookup_env_alloc, lookup_env_buffer}
// get_env retrieves the value of the environment variable named by the key
// It returns the value, which will be empty if the variable is not present
// To distinguish between an empty value and an unset value, use lookup_env
// NOTE: the value will be allocated with the supplied allocator
@(require_results)
get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator)
return
}
@(require_results)
get_env_buf :: proc(buf: []u8, key: string) -> (value: string) {
value, _ = lookup_env(buf, key)
return
}
get_env :: proc{get_env_alloc, get_env_buf}
// set_env sets the value of the environment variable named by the key
set_env :: proc(key, value: string) -> Error {
k := win32.utf8_to_wstring(key)
v := win32.utf8_to_wstring(value)
if !win32.SetEnvironmentVariableW(k, v) {
return get_last_error()
}
return nil
}
// unset_env unsets a single environment variable
unset_env :: proc(key: string) -> Error {
k := win32.utf8_to_wstring(key)
if !win32.SetEnvironmentVariableW(k, nil) {
return get_last_error()
}
return nil
}
// environ returns a copy of strings representing the environment, in the form "key=value"
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator
@(require_results)
environ :: proc(allocator := context.allocator) -> []string {
envs := ([^]win32.WCHAR)(win32.GetEnvironmentStringsW())
if envs == nil {
return nil
}
defer win32.FreeEnvironmentStringsW(envs)
r, err := make([dynamic]string, 0, 50, allocator)
if err != nil {
return nil
}
for from, i := 0, 0; true; i += 1 {
if c := envs[i]; c == 0 {
if i <= from {
break
}
append(&r, win32.utf16_to_utf8(envs[from:i], allocator) or_else "")
from = i + 1
}
}
return r[:]
}
// clear_env deletes all environment variables
clear_env :: proc() {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
envs := environ(context.temp_allocator)
for env in envs {
for j in 1..<len(env) {
if env[j] == '=' {
unset_env(env[0:j])
break
}
}
}
}
+318
View File
@@ -0,0 +1,318 @@
package os
import "base:intrinsics"
import "base:runtime"
import "core:io"
Platform_Error :: _Platform_Error
#assert(size_of(Platform_Error) <= 4)
#assert(intrinsics.type_has_nil(Platform_Error))
General_Error :: enum u32 {
None,
Exist,
Not_Exist,
Timeout,
Broken_Pipe,
Invalid_File,
Invalid_Dir,
Invalid_Path,
Invalid_Callback,
Pattern_Has_Separator,
File_Is_Pipe,
Not_Dir,
// Environment variable not found.
Env_Var_Not_Found,
}
Errno :: Error // alias for legacy use
Error :: union #shared_nil {
General_Error,
io.Error,
runtime.Allocator_Error,
Platform_Error,
}
#assert(size_of(Error) == 8)
ERROR_NONE :: Error{}
ERROR_EOF :: io.Error.EOF
@(require_results)
is_platform_error :: proc "contextless" (ferr: Error) -> (err: i32, ok: bool) {
v := ferr.(Platform_Error) or_else {}
return i32(v), i32(v) != 0
}
@(require_results)
error_string :: proc "contextless" (ferr: Error) -> string {
if ferr == nil {
return ""
}
switch e in ferr {
case General_Error:
switch e {
case .None: return ""
case .Exist: return "file already exists"
case .Not_Exist: return "file does not exist"
case .Timeout: return "i/o timeout"
case .Broken_Pipe: return "Broken pipe"
case .Invalid_File: return "invalid file"
case .Invalid_Dir: return "invalid directory"
case .Invalid_Path: return "invalid path"
case .Invalid_Callback: return "invalid callback"
case .Pattern_Has_Separator: return "pattern has separator"
case .File_Is_Pipe: return "file is pipe"
case .Not_Dir: return "file is not directory"
case .Env_Var_Not_Found: return "environment variable not found"
}
case io.Error:
switch e {
case .None: return ""
case .EOF: return "eof"
case .Unexpected_EOF: return "unexpected eof"
case .Short_Write: return "short write"
case .Invalid_Write: return "invalid write result"
case .Short_Buffer: return "short buffer"
case .No_Progress: return "multiple read calls return no data or error"
case .Invalid_Whence: return "invalid whence"
case .Invalid_Offset: return "invalid offset"
case .Invalid_Unread: return "invalid unread"
case .Negative_Read: return "negative read"
case .Negative_Write: return "negative write"
case .Negative_Count: return "negative count"
case .Buffer_Full: return "buffer full"
case .Permission_Denied: return "permission denied"
case .Closed: return "file already closed"
case .No_Size: return "file has no definite size"
case .Unsupported: return "unsupported"
case .Unknown: //
}
case runtime.Allocator_Error:
switch e {
case .None: return ""
case .Out_Of_Memory: return "out of memory"
case .Invalid_Pointer: return "invalid allocator pointer"
case .Invalid_Argument: return "invalid allocator argument"
case .Mode_Not_Implemented: return "allocator mode not implemented"
}
case Platform_Error:
return _error_string(e)
}
return "unknown error"
}
print_error :: proc(f: Handle, ferr: Error, msg: string) -> (n: int, err: Error) {
err_str := error_string(ferr)
// msg + ": " + err_str + '\n'
length := len(msg) + 2 + len(err_str) + 1
buf_ := intrinsics.alloca(length, 1)
buf := buf_[:length]
copy(buf, msg)
buf[len(msg)] = ':'
buf[len(msg) + 1] = ' '
copy(buf[len(msg) + 2:], err_str)
buf[length - 1] = '\n'
return write(f, buf)
}
@(require_results, private)
_error_string :: proc "contextless" (e: Platform_Error) -> string where intrinsics.type_is_enum(Platform_Error) {
if e == nil {
return ""
}
when ODIN_OS == .Darwin {
if s := string(_darwin_string_error(i32(e))); s != "" {
return s
}
}
when ODIN_OS != .Linux {
@(require_results)
binary_search :: proc "contextless" (array: $A/[]$T, key: T) -> (index: int, found: bool) #no_bounds_check {
n := len(array)
left, right := 0, n
for left < right {
mid := int(uint(left+right) >> 1)
if array[mid] < key {
left = mid+1
} else {
// equal or greater
right = mid
}
}
return left, left < n && array[left] == key
}
err := runtime.Type_Info_Enum_Value(e)
ti := &runtime.type_info_base(type_info_of(Platform_Error)).variant.(runtime.Type_Info_Enum)
if idx, ok := binary_search(ti.values, err); ok {
return ti.names[idx]
}
} else {
@(rodata, static)
pe_strings := [Platform_Error]string{
.NONE = "",
.EPERM = "Operation not permitted",
.ENOENT = "No such file or directory",
.ESRCH = "No such process",
.EINTR = "Interrupted system call",
.EIO = "Input/output error",
.ENXIO = "No such device or address",
.E2BIG = "Argument list too long",
.ENOEXEC = "Exec format error",
.EBADF = "Bad file descriptor",
.ECHILD = "No child processes",
.EAGAIN = "Resource temporarily unavailable",
.ENOMEM = "Cannot allocate memory",
.EACCES = "Permission denied",
.EFAULT = "Bad address",
.ENOTBLK = "Block device required",
.EBUSY = "Device or resource busy",
.EEXIST = "File exists",
.EXDEV = "Invalid cross-device link",
.ENODEV = "No such device",
.ENOTDIR = "Not a directory",
.EISDIR = "Is a directory",
.EINVAL = "Invalid argument",
.ENFILE = "Too many open files in system",
.EMFILE = "Too many open files",
.ENOTTY = "Inappropriate ioctl for device",
.ETXTBSY = "Text file busy",
.EFBIG = "File too large",
.ENOSPC = "No space left on device",
.ESPIPE = "Illegal seek",
.EROFS = "Read-only file system",
.EMLINK = "Too many links",
.EPIPE = "Broken pipe",
.EDOM = "Numerical argument out of domain",
.ERANGE = "Numerical result out of range",
.EDEADLK = "Resource deadlock avoided",
.ENAMETOOLONG = "File name too long",
.ENOLCK = "No locks available",
.ENOSYS = "Function not implemented",
.ENOTEMPTY = "Directory not empty",
.ELOOP = "Too many levels of symbolic links",
.EUNKNOWN_41 = "Unknown Error (41)",
.ENOMSG = "No message of desired type",
.EIDRM = "Identifier removed",
.ECHRNG = "Channel number out of range",
.EL2NSYNC = "Level 2 not synchronized",
.EL3HLT = "Level 3 halted",
.EL3RST = "Level 3 reset",
.ELNRNG = "Link number out of range",
.EUNATCH = "Protocol driver not attached",
.ENOCSI = "No CSI structure available",
.EL2HLT = "Level 2 halted",
.EBADE = "Invalid exchange",
.EBADR = "Invalid request descriptor",
.EXFULL = "Exchange full",
.ENOANO = "No anode",
.EBADRQC = "Invalid request code",
.EBADSLT = "Invalid slot",
.EUNKNOWN_58 = "Unknown Error (58)",
.EBFONT = "Bad font file format",
.ENOSTR = "Device not a stream",
.ENODATA = "No data available",
.ETIME = "Timer expired",
.ENOSR = "Out of streams resources",
.ENONET = "Machine is not on the network",
.ENOPKG = "Package not installed",
.EREMOTE = "Object is remote",
.ENOLINK = "Link has been severed",
.EADV = "Advertise error",
.ESRMNT = "Srmount error",
.ECOMM = "Communication error on send",
.EPROTO = "Protocol error",
.EMULTIHOP = "Multihop attempted",
.EDOTDOT = "RFS specific error",
.EBADMSG = "Bad message",
.EOVERFLOW = "Value too large for defined data type",
.ENOTUNIQ = "Name not unique on network",
.EBADFD = "File descriptor in bad state",
.EREMCHG = "Remote address changed",
.ELIBACC = "Can not access a needed shared library",
.ELIBBAD = "Accessing a corrupted shared library",
.ELIBSCN = ".lib section in a.out corrupted",
.ELIBMAX = "Attempting to link in too many shared libraries",
.ELIBEXEC = "Cannot exec a shared library directly",
.EILSEQ = "Invalid or incomplete multibyte or wide character",
.ERESTART = "Interrupted system call should be restarted",
.ESTRPIPE = "Streams pipe error",
.EUSERS = "Too many users",
.ENOTSOCK = "Socket operation on non-socket",
.EDESTADDRREQ = "Destination address required",
.EMSGSIZE = "Message too long",
.EPROTOTYPE = "Protocol wrong type for socket",
.ENOPROTOOPT = "Protocol not available",
.EPROTONOSUPPORT = "Protocol not supported",
.ESOCKTNOSUPPORT = "Socket type not supported",
.EOPNOTSUPP = "Operation not supported",
.EPFNOSUPPORT = "Protocol family not supported",
.EAFNOSUPPORT = "Address family not supported by protocol",
.EADDRINUSE = "Address already in use",
.EADDRNOTAVAIL = "Cannot assign requested address",
.ENETDOWN = "Network is down",
.ENETUNREACH = "Network is unreachable",
.ENETRESET = "Network dropped connection on reset",
.ECONNABORTED = "Software caused connection abort",
.ECONNRESET = "Connection reset by peer",
.ENOBUFS = "No buffer space available",
.EISCONN = "Transport endpoint is already connected",
.ENOTCONN = "Transport endpoint is not connected",
.ESHUTDOWN = "Cannot send after transport endpoint shutdown",
.ETOOMANYREFS = "Too many references: cannot splice",
.ETIMEDOUT = "Connection timed out",
.ECONNREFUSED = "Connection refused",
.EHOSTDOWN = "Host is down",
.EHOSTUNREACH = "No route to host",
.EALREADY = "Operation already in progress",
.EINPROGRESS = "Operation now in progress",
.ESTALE = "Stale file handle",
.EUCLEAN = "Structure needs cleaning",
.ENOTNAM = "Not a XENIX named type file",
.ENAVAIL = "No XENIX semaphores available",
.EISNAM = "Is a named type file",
.EREMOTEIO = "Remote I/O error",
.EDQUOT = "Disk quota exceeded",
.ENOMEDIUM = "No medium found",
.EMEDIUMTYPE = "Wrong medium type",
.ECANCELED = "Operation canceled",
.ENOKEY = "Required key not available",
.EKEYEXPIRED = "Key has expired",
.EKEYREVOKED = "Key has been revoked",
.EKEYREJECTED = "Key was rejected by service",
.EOWNERDEAD = "Owner died",
.ENOTRECOVERABLE = "State not recoverable",
.ERFKILL = "Operation not possible due to RF-kill",
.EHWPOISON = "Memory page has hardware error",
}
if Platform_Error.NONE <= e && e <= max(Platform_Error) {
return pe_strings[e]
}
}
return "<unknown platform error>"
}
@(private, require_results)
error_to_io_error :: proc(ferr: Error) -> io.Error {
if ferr == nil {
return .None
}
return ferr.(io.Error) or_else .Unknown
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+33
View File
@@ -0,0 +1,33 @@
package os
import "core:time"
File_Info :: struct {
fullpath: string, // allocated
name: string, // uses `fullpath` as underlying data
size: i64,
mode: File_Mode,
is_dir: bool,
creation_time: time.Time,
modification_time: time.Time,
access_time: time.Time,
}
file_info_slice_delete :: proc(infos: []File_Info, allocator := context.allocator) {
for i := len(infos)-1; i >= 0; i -= 1 {
file_info_delete(infos[i], allocator)
}
delete(infos, allocator)
}
file_info_delete :: proc(fi: File_Info, allocator := context.allocator) {
delete(fi.fullpath, allocator)
}
File_Mode :: distinct u32
File_Mode_Dir :: File_Mode(1<<16)
File_Mode_Named_Pipe :: File_Mode(1<<17)
File_Mode_Device :: File_Mode(1<<18)
File_Mode_Char_Device :: File_Mode(1<<19)
File_Mode_Sym_Link :: File_Mode(1<<20)
File renamed without changes.
+303
View File
@@ -0,0 +1,303 @@
package os
import "core:time"
import "base:runtime"
import win32 "core:sys/windows"
@(private, require_results)
full_path_from_name :: proc(name: string, allocator := context.allocator) -> (path: string, err: Errno) {
context.allocator = allocator
name := name
if name == "" {
name = "."
}
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
p := win32.utf8_to_utf16(name, context.temp_allocator)
buf := make([dynamic]u16, 100)
defer delete(buf)
for {
n := win32.GetFullPathNameW(cstring16(raw_data(p)), u32(len(buf)), cstring16(raw_data(buf)), nil)
if n == 0 {
return "", get_last_error()
}
if n <= u32(len(buf)) {
return win32.utf16_to_utf8(buf[:n], allocator) or_else "", nil
}
resize(&buf, len(buf)*2)
}
return
}
@(private, require_results)
_stat :: proc(name: string, create_file_attributes: u32, allocator := context.allocator) -> (fi: File_Info, e: Errno) {
if len(name) == 0 {
return {}, ERROR_PATH_NOT_FOUND
}
context.allocator = allocator
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
wname := win32.utf8_to_wstring(fix_long_path(name), context.temp_allocator)
fa: win32.WIN32_FILE_ATTRIBUTE_DATA
ok := win32.GetFileAttributesExW(wname, win32.GetFileExInfoStandard, &fa)
if ok && fa.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
// Not a symlink
return file_info_from_win32_file_attribute_data(&fa, name)
}
err := 0 if ok else win32.GetLastError()
if err == win32.ERROR_SHARING_VIOLATION {
fd: win32.WIN32_FIND_DATAW
sh := win32.FindFirstFileW(wname, &fd)
if sh == win32.INVALID_HANDLE_VALUE {
e = get_last_error()
return
}
win32.FindClose(sh)
return file_info_from_win32_find_data(&fd, name)
}
h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil)
if h == win32.INVALID_HANDLE_VALUE {
e = get_last_error()
return
}
defer win32.CloseHandle(h)
return file_info_from_get_file_information_by_handle(name, h)
}
@(require_results)
lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) {
attrs := win32.FILE_FLAG_BACKUP_SEMANTICS
attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT
return _stat(name, attrs, allocator)
}
@(require_results)
stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) {
attrs := win32.FILE_FLAG_BACKUP_SEMANTICS
return _stat(name, attrs, allocator)
}
@(require_results)
fstat :: proc(fd: Handle, allocator := context.allocator) -> (fi: File_Info, err: Errno) {
if fd == 0 {
err = ERROR_INVALID_HANDLE
}
context.allocator = allocator
path := cleanpath_from_handle(fd) or_return
defer if err != nil {
delete(path)
}
h := win32.HANDLE(fd)
switch win32.GetFileType(h) {
case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR:
fi.name = basename(path)
fi.mode |= file_type_mode(h)
err = nil
case:
fi = file_info_from_get_file_information_by_handle(path, h) or_return
}
fi.fullpath = path
return
}
@(private, require_results)
cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
buf := buf
N := 0
for c, i in buf {
if c == 0 { break }
N = i+1
}
buf = buf[:N]
if len(buf) >= 4 && buf[0] == '\\' && buf[1] == '\\' && buf[2] == '?' && buf[3] == '\\' {
buf = buf[4:]
/*
NOTE(Jeroen): Properly handle UNC paths.
We need to turn `\\?\UNC\synology.local` into `\\synology.local`.
*/
if len(buf) >= 3 && buf[0] == 'U' && buf[1] == 'N' && buf[2] == 'C' {
buf = buf[2:]
buf[0] = '\\'
}
}
return buf
}
@(private, require_results)
cleanpath_from_handle :: proc(fd: Handle) -> (s: string, err: Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
buf := cleanpath_from_handle_u16(fd, context.temp_allocator) or_return
return win32.utf16_to_utf8(buf, context.allocator)
}
@(private, require_results)
cleanpath_from_handle_u16 :: proc(fd: Handle, allocator: runtime.Allocator) -> ([]u16, Errno) {
if fd == 0 {
return nil, ERROR_INVALID_HANDLE
}
h := win32.HANDLE(fd)
n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0)
if n == 0 {
return nil, get_last_error()
}
buf := make([]u16, max(n, win32.DWORD(260))+1, allocator)
buf_len := win32.GetFinalPathNameByHandleW(h, cstring16(raw_data(buf)), n, 0)
return buf[:buf_len], nil
}
@(private, require_results)
cleanpath_from_buf :: proc(buf: []u16) -> string {
buf := buf
buf = cleanpath_strip_prefix(buf)
return win32.utf16_to_utf8(buf, context.allocator) or_else ""
}
@(private, require_results)
basename :: proc(name: string) -> (base: string) {
name := name
if len(name) > 3 && name[:3] == `\\?` {
name = name[3:]
}
if len(name) == 2 && name[1] == ':' {
return "."
} else if len(name) > 2 && name[1] == ':' {
name = name[2:]
}
i := len(name)-1
for ; i > 0 && (name[i] == '/' || name[i] == '\\'); i -= 1 {
name = name[:i]
}
for i -= 1; i >= 0; i -= 1 {
if name[i] == '/' || name[i] == '\\' {
name = name[i+1:]
break
}
}
return name
}
@(private, require_results)
file_type_mode :: proc(h: win32.HANDLE) -> File_Mode {
switch win32.GetFileType(h) {
case win32.FILE_TYPE_PIPE:
return File_Mode_Named_Pipe
case win32.FILE_TYPE_CHAR:
return File_Mode_Device | File_Mode_Char_Device
}
return 0
}
@(private, require_results)
file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (mode: File_Mode) {
if FileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
mode |= 0o444
} else {
mode |= 0o666
}
is_sym := false
if FileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
is_sym = false
} else {
is_sym = ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT
}
if is_sym {
mode |= File_Mode_Sym_Link
} else {
if FileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
mode |= 0o111 | File_Mode_Dir
}
if h != nil {
mode |= file_type_mode(h)
}
}
return
}
@(private)
windows_set_file_info_times :: proc(fi: ^File_Info, d: ^$T) {
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
}
@(private, require_results)
file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Errno) {
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
fi.is_dir = fi.mode & File_Mode_Dir != 0
windows_set_file_info_times(&fi, d)
fi.fullpath, e = full_path_from_name(name)
fi.name = basename(fi.fullpath)
return
}
@(private, require_results)
file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string) -> (fi: File_Info, e: Errno) {
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
fi.is_dir = fi.mode & File_Mode_Dir != 0
windows_set_file_info_times(&fi, d)
fi.fullpath, e = full_path_from_name(name)
fi.name = basename(fi.fullpath)
return
}
@(private, require_results)
file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE) -> (File_Info, Errno) {
d: win32.BY_HANDLE_FILE_INFORMATION
if !win32.GetFileInformationByHandle(h, &d) {
err := get_last_error()
return {}, err
}
ti: win32.FILE_ATTRIBUTE_TAG_INFO
if !win32.GetFileInformationByHandleEx(h, .FileAttributeTagInfo, &ti, size_of(ti)) {
err := get_last_error()
if err != ERROR_INVALID_PARAMETER {
return {}, err
}
// Indicate this is a symlink on FAT file systems
ti.ReparseTag = 0
}
fi: File_Info
fi.fullpath = path
fi.name = basename(path)
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
fi.mode |= file_mode_from_file_attributes(ti.FileAttributes, h, ti.ReparseTag)
fi.is_dir = fi.mode & File_Mode_Dir != 0
windows_set_file_info_times(&fi, &d)
return fi, nil
}
File renamed without changes.
-144
View File
@@ -1,144 +0,0 @@
#+private
package os2
import "base:runtime"
import "core:time"
import win32 "core:sys/windows"
@(private="file")
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
// Ignore "." and ".."
if d.cFileName[0] == '.' && d.cFileName[1] == 0 {
return
}
if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 {
return
}
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}, Permissions_Read_Write_All) or_else 0)
defer win32.CloseHandle(handle)
fi.fullpath = path
fi.name = basename(path)
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
fi.type, fi.mode = _file_type_mode_from_file_attributes(d.dwFileAttributes, handle, d.dwReserved0)
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
if file_id_info: win32.FILE_ID_INFO; handle != nil && win32.GetFileInformationByHandleEx(handle, .FileIdInfo, &file_id_info, size_of(file_id_info)) {
#assert(size_of(fi.inode) == size_of(file_id_info.FileId))
#assert(size_of(fi.inode) == 16)
runtime.mem_copy_non_overlapping(&fi.inode, &file_id_info.FileId, 16)
}
return
}
Read_Directory_Iterator_Impl :: struct {
find_data: win32.WIN32_FIND_DATAW,
find_handle: win32.HANDLE,
path: string,
prev_fi: File_Info,
no_more_files: bool,
}
@(require_results)
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
for !it.impl.no_more_files {
err: Error
file_info_delete(it.impl.prev_fi, file_allocator())
it.impl.prev_fi = {}
fi, err = find_data_to_file_info(it.impl.path, &it.impl.find_data, file_allocator())
if err != nil {
read_directory_iterator_set_error(it, it.impl.path, err)
return
}
if fi.name != "" {
it.impl.prev_fi = fi
ok = true
index = it.index
it.index += 1
}
if !win32.FindNextFileW(it.impl.find_handle, &it.impl.find_data) {
e := _get_platform_error()
if pe, _ := is_platform_error(e); pe != i32(win32.ERROR_NO_MORE_FILES) {
read_directory_iterator_set_error(it, it.impl.path, e)
}
it.impl.no_more_files = true
}
if ok {
return
}
}
return
}
_read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
it.impl.no_more_files = false
if f == nil || f.impl == nil {
read_directory_iterator_set_error(it, "", .Invalid_File)
return
}
it.f = f
impl := (^File_Impl)(f.impl)
// NOTE: Allow calling `init` to target a new directory with the same iterator - reset idx.
if it.impl.find_handle != nil {
win32.FindClose(it.impl.find_handle)
}
if it.impl.path != "" {
delete(it.impl.path, file_allocator())
}
if !is_directory(impl.name) {
read_directory_iterator_set_error(it, impl.name, .Invalid_Dir)
return
}
wpath := string16(impl.wname)
temp_allocator := TEMP_ALLOCATOR_GUARD({})
wpath_search := make([]u16, len(wpath)+3, temp_allocator)
copy(wpath_search, wpath)
wpath_search[len(wpath)+0] = '\\'
wpath_search[len(wpath)+1] = '*'
wpath_search[len(wpath)+2] = 0
it.impl.find_handle = win32.FindFirstFileW(cstring16(raw_data(wpath_search)), &it.impl.find_data)
if it.impl.find_handle == win32.INVALID_HANDLE_VALUE {
read_directory_iterator_set_error(it, impl.name, _get_platform_error())
return
}
defer if it.err.err != nil {
win32.FindClose(it.impl.find_handle)
}
err: Error
it.impl.path, err = _cleanpath_from_buf(wpath, file_allocator())
if err != nil {
read_directory_iterator_set_error(it, impl.name, err)
}
return
}
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
if it.f == nil {
return
}
file_info_delete(it.impl.prev_fi, file_allocator())
delete(it.impl.path, file_allocator())
win32.FindClose(it.impl.find_handle)
}
-142
View File
@@ -1,142 +0,0 @@
#+private
package os2
import win32 "core:sys/windows"
import "base:runtime"
_lookup_env_alloc :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
if key == "" {
return
}
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
wkey, _ := win32_utf8_to_wstring(key, temp_allocator)
n := win32.GetEnvironmentVariableW(wkey, nil, 0)
if n == 0 {
err := win32.GetLastError()
if err == win32.ERROR_ENVVAR_NOT_FOUND {
return "", false
}
return "", true
}
b := make([]u16, n+1, temp_allocator)
n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
if n == 0 {
err := win32.GetLastError()
if err == win32.ERROR_ENVVAR_NOT_FOUND {
return "", false
}
return "", false
}
value = win32_utf16_to_utf8(string16(b[:n]), allocator) or_else ""
found = true
return
}
// This version of `lookup_env` doesn't allocate and instead requires the user to provide a buffer.
// Note that it is limited to environment names and values of 512 utf-16 values each
// due to the necessary utf-8 <> utf-16 conversion.
@(require_results)
_lookup_env_buf :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
key_buf: [513]u16
wkey := win32.utf8_to_wstring(key_buf[:], key)
if wkey == nil {
return "", .Buffer_Full
}
n2 := win32.GetEnvironmentVariableW(wkey, nil, 0)
if n2 == 0 {
return "", .Env_Var_Not_Found
}
val_buf: [513]u16
n2 = win32.GetEnvironmentVariableW(wkey, raw_data(val_buf[:]), u32(len(val_buf[:])))
if n2 == 0 {
return "", .Env_Var_Not_Found
} else if int(n2) > len(buf) {
return "", .Buffer_Full
}
value = win32.utf16_to_utf8(buf, val_buf[:n2])
return value, nil
}
_lookup_env :: proc{_lookup_env_alloc, _lookup_env_buf}
_set_env :: proc(key, value: string) -> Error {
temp_allocator := TEMP_ALLOCATOR_GUARD({})
k := win32_utf8_to_wstring(key, temp_allocator) or_return
v := win32_utf8_to_wstring(value, temp_allocator) or_return
if !win32.SetEnvironmentVariableW(k, v) {
return _get_platform_error()
}
return nil
}
_unset_env :: proc(key: string) -> bool {
temp_allocator := TEMP_ALLOCATOR_GUARD({})
k, _ := win32_utf8_to_wstring(key, temp_allocator)
return bool(win32.SetEnvironmentVariableW(k, nil))
}
_clear_env :: proc() {
temp_allocator := TEMP_ALLOCATOR_GUARD({})
envs, _ := environ(temp_allocator)
for env in envs {
for j in 1..<len(env) {
if env[j] == '=' {
unset_env(env[0:j])
break
}
}
}
}
_environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error) {
envs := win32.GetEnvironmentStringsW()
if envs == nil {
return
}
defer win32.FreeEnvironmentStringsW(envs)
n := 0
for from, i, p := 0, 0, envs; true; i += 1 {
c := ([^]u16)(p)[i]
if c == 0 {
if i <= from {
break
}
n += 1
from = i + 1
}
}
r := make([dynamic]string, 0, n, allocator) or_return
defer if err != nil {
for e in r {
delete(e, allocator)
}
delete(r)
}
for from, i, p := 0, 0, envs; true; i += 1 {
c := ([^]u16)(p)[i]
if c == 0 {
if i <= from {
break
}
w := ([^]u16)(p)[from:i]
s := win32_utf16_to_utf8(w, allocator) or_return
append(&r, s)
from = i + 1
}
}
environ = r[:]
return
}
-147
View File
@@ -1,147 +0,0 @@
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,
Exist,
Not_Exist,
Timeout,
Broken_Pipe,
Invalid_File,
Invalid_Dir,
Invalid_Path,
Invalid_Callback,
Invalid_Command,
Pattern_Has_Separator,
Pattern_Syntax_Error, // Indicates an error in `glob` or `match` pattern.
No_HOME_Variable,
Env_Var_Not_Found,
}
// 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,
runtime.Allocator_Error,
Platform_Error,
}
#assert(size_of(Error) == size_of(u64))
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 {}
return i32(v), i32(v) != 0
}
// Attempts to return the error `ferr` as a string without any allocation
@(require_results)
error_string :: proc(ferr: Error) -> string {
if ferr == nil {
return ""
}
switch e in ferr {
case General_Error:
switch e {
case .None: return ""
case .Exist: return "file already exists"
case .Not_Exist: return "file does not exist"
case .Timeout: return "i/o timeout"
case .Broken_Pipe: return "Broken pipe"
case .Invalid_File: return "invalid file"
case .Invalid_Dir: return "invalid directory"
case .Invalid_Path: return "invalid path"
case .Invalid_Callback: return "invalid callback"
case .Invalid_Command: return "invalid command"
case .Pattern_Has_Separator: return "pattern has separator"
case .Pattern_Syntax_Error: return "glob pattern syntax error"
case .No_HOME_Variable: return "no $HOME variable"
case .Env_Var_Not_Found: return "environment variable not found"
}
case io.Error:
switch e {
case .None: return ""
case .EOF: return "eof"
case .Unexpected_EOF: return "unexpected eof"
case .Short_Write: return "short write"
case .Invalid_Write: return "invalid write result"
case .Short_Buffer: return "short buffer"
case .No_Progress: return "multiple read calls return no data or error"
case .Invalid_Whence: return "invalid whence"
case .Invalid_Offset: return "invalid offset"
case .Invalid_Unread: return "invalid unread"
case .Negative_Read: return "negative read"
case .Negative_Write: return "negative write"
case .Negative_Count: return "negative count"
case .Buffer_Full: return "buffer full"
case .Permission_Denied: return "permission denied"
case .Closed: return "file already closed"
case .No_Size: return "file has no definite size"
case .Unsupported: return "unsupported"
case .Unknown: //
}
case runtime.Allocator_Error:
switch e {
case .None: return ""
case .Out_Of_Memory: return "out of memory"
case .Invalid_Pointer: return "invalid allocator pointer"
case .Invalid_Argument: return "invalid allocator argument"
case .Mode_Not_Implemented: return "allocator mode not implemented"
}
case Platform_Error:
return _error_string(i32(e))
}
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)
// msg + ": " + err_str + '\n'
length := len(msg) + 2 + len(err_str) + 1
buf := make([]u8, length, temp_allocator)
copy(buf, msg)
buf[len(msg)] = ':'
buf[len(msg) + 1] = ' '
copy(buf[len(msg) + 2:], err_str)
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
}
-117
View File
@@ -1,117 +0,0 @@
package os2
import "base:runtime"
import "core:strings"
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, // fullpath of the file
name: string, // base name of the file
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,
modification_time: time.Time,
access_time: time.Time,
}
@(require_results)
file_info_clone :: proc(fi: File_Info, allocator: runtime.Allocator) -> (cloned: File_Info, err: runtime.Allocator_Error) {
cloned = fi
cloned.fullpath = strings.clone(fi.fullpath, allocator) or_return
_, cloned.name = split_path(cloned.fullpath)
return
}
file_info_slice_delete :: proc(infos: []File_Info, allocator: runtime.Allocator) {
#reverse for info in infos {
file_info_delete(info, allocator)
}
delete(infos, allocator)
}
file_info_delete :: proc(fi: File_Info, allocator: runtime.Allocator) {
delete(fi.fullpath, allocator)
}
@(require_results)
fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
if f == nil {
return {}, nil
} else if f.stream.procedure != nil {
fi: File_Info
data := ([^]byte)(&fi)[:size_of(fi)]
_, err := f.stream.procedure(f, .Fstat, data, 0, nil, allocator)
return fi, err
}
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)
}
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)
}
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({})
fi, err := fstat(f, temp_allocator)
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({})
fi, err := stat(path, temp_allocator)
return fi.modification_time, err
}
is_reserved_name :: proc(path: string) -> bool {
return _is_reserved_name(path)
}
-393
View File
@@ -1,393 +0,0 @@
#+private
package os2
import "base:runtime"
import "core:time"
import "core:strings"
import win32 "core:sys/windows"
_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
if f == nil || (^File_Impl)(f.impl).fd == nil {
return
}
path := _cleanpath_from_handle(f, allocator) or_return
h := _handle(f)
switch win32.GetFileType(h) {
case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR:
fi = File_Info {
fullpath = path,
name = basename(path),
type = file_type(h),
}
return
}
return _file_info_from_get_file_information_by_handle(path, h, allocator)
}
_stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS, allocator)
}
_lstat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS|win32.FILE_FLAG_OPEN_REPARSE_POINT, allocator)
}
_same_file :: proc(fi1, fi2: File_Info) -> bool {
return fi1.fullpath == fi2.fullpath
}
full_path_from_name :: proc(name: string, allocator: runtime.Allocator) -> (path: string, err: Error) {
name := name
if name == "" {
name = "."
}
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
p := win32_utf8_to_utf16(name, temp_allocator) or_return
n := win32.GetFullPathNameW(cstring16(raw_data(p)), 0, nil, nil)
if n == 0 {
return "", _get_platform_error()
}
buf := make([]u16, n+1, temp_allocator)
n = win32.GetFullPathNameW(cstring16(raw_data(p)), u32(len(buf)), cstring16(raw_data(buf)), nil)
if n == 0 {
return "", _get_platform_error()
}
return win32_utf16_to_utf8(buf[:n], allocator)
}
internal_stat :: proc(name: string, create_file_attributes: u32, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) {
if len(name) == 0 {
return {}, .Not_Exist
}
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
wname := _fix_long_path(name, temp_allocator) or_return
fa: win32.WIN32_FILE_ATTRIBUTE_DATA
ok := win32.GetFileAttributesExW(wname, win32.GetFileExInfoStandard, &fa)
if ok && fa.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
// Not a symlink
fi = _file_info_from_win32_file_attribute_data(&fa, name, allocator) or_return
if fi.type == .Undetermined {
fi.type = _file_type_from_create_file(wname, create_file_attributes)
}
return
}
err := 0 if ok else win32.GetLastError()
if err == win32.ERROR_SHARING_VIOLATION {
fd: win32.WIN32_FIND_DATAW
sh := win32.FindFirstFileW(wname, &fd)
if sh == win32.INVALID_HANDLE_VALUE {
e = _get_platform_error()
return
}
win32.FindClose(sh)
fi = _file_info_from_win32_find_data(&fd, name, allocator) or_return
if fi.type == .Undetermined {
fi.type = _file_type_from_create_file(wname, create_file_attributes)
}
return
}
h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil)
if h == win32.INVALID_HANDLE_VALUE {
e = _get_platform_error()
return
}
defer win32.CloseHandle(h)
return _file_info_from_get_file_information_by_handle(name, h, allocator)
}
_cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
buf := buf
N := 0
for c, i in buf {
if c == 0 { break }
N = i+1
}
buf = buf[:N]
if len(buf) >= 4 {
if buf[0] == '\\' &&
buf[1] == '\\' &&
buf[2] == '?' &&
buf[3] == '\\' {
buf = buf[4:]
}
}
return buf
}
_cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (string, Error) {
if f == nil {
return "", nil
}
h := _handle(f)
n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0)
if n == 0 {
return "", _get_platform_error()
}
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
buf := make([]u16, max(n, 260)+1, temp_allocator)
n = win32.GetFinalPathNameByHandleW(h, cstring16(raw_data(buf)), u32(len(buf)), 0)
return _cleanpath_from_buf(string16(buf[:n]), allocator)
}
_cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) {
if f == nil {
return nil, nil
}
h := _handle(f)
n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0)
if n == 0 {
return nil, _get_platform_error()
}
temp_allocator := TEMP_ALLOCATOR_GUARD({})
buf := make([]u16, max(n, 260)+1, temp_allocator)
n = win32.GetFinalPathNameByHandleW(h, cstring16(raw_data(buf)), u32(len(buf)), 0)
return _cleanpath_strip_prefix(buf[:n]), nil
}
_cleanpath_from_buf :: proc(buf: string16, allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) {
buf := transmute([]u16)buf
buf = _cleanpath_strip_prefix(buf)
return win32_utf16_to_utf8(buf, allocator)
}
basename :: proc(name: string) -> (base: string) {
name := name
if len(name) > 3 && name[:3] == `\\?` {
name = name[3:]
}
if len(name) == 2 && name[1] == ':' {
return "."
} else if len(name) > 2 && name[1] == ':' {
name = name[2:]
}
i := len(name)-1
for ; i > 0 && (name[i] == '/' || name[i] == '\\'); i -= 1 {
name = name[:i]
}
for i -= 1; i >= 0; i -= 1 {
if name[i] == '/' || name[i] == '\\' {
name = name[i+1:]
break
}
}
return name
}
file_type :: proc(h: win32.HANDLE) -> File_Type {
switch win32.GetFileType(h) {
case win32.FILE_TYPE_PIPE: return .Named_Pipe
case win32.FILE_TYPE_CHAR: return .Character_Device
case win32.FILE_TYPE_DISK: return .Regular
}
return .Undetermined
}
_file_type_from_create_file :: proc(wname: win32.wstring, create_file_attributes: u32) -> File_Type {
h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil)
if h == win32.INVALID_HANDLE_VALUE {
return .Undetermined
}
defer win32.CloseHandle(h)
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: Permissions) {
if file_attributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
mode += Permissions_Write_All
} else {
mode += Permissions_Read_Write_All
}
is_sym := false
if file_attributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
is_sym = false
} else {
is_sym = ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT
}
if is_sym {
type = .Symlink
} else if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
type = .Directory
mode += Permissions_Execute_All
} else if h != nil {
type = file_type(h)
}
return
}
// a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC)
time_as_filetime :: #force_inline proc(t: time.Time) -> (ft: win32.LARGE_INTEGER) {
win := u64(t._nsec / 100) + 116444736000000000
return win32.LARGE_INTEGER(win)
}
filetime_as_time_li :: #force_inline proc(ft: win32.LARGE_INTEGER) -> (t: time.Time) {
return {_nsec=(i64(ft) - 116444736000000000) * 100}
}
filetime_as_time_ft :: #force_inline proc(ft: win32.FILETIME) -> (t: time.Time) {
return filetime_as_time_li(win32.LARGE_INTEGER(ft.dwLowDateTime) + win32.LARGE_INTEGER(ft.dwHighDateTime) << 32)
}
filetime_as_time :: proc{filetime_as_time_ft, filetime_as_time_li}
_file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) {
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
fi.type = type
fi.mode |= mode
fi.creation_time = filetime_as_time(d.ftCreationTime)
fi.modification_time = filetime_as_time(d.ftLastWriteTime)
fi.access_time = filetime_as_time(d.ftLastAccessTime)
fi.fullpath, e = full_path_from_name(name, allocator)
fi.name = basename(fi.fullpath)
return
}
_file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) {
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
fi.type = type
fi.mode |= mode
fi.creation_time = filetime_as_time(d.ftCreationTime)
fi.modification_time = filetime_as_time(d.ftLastWriteTime)
fi.access_time = filetime_as_time(d.ftLastAccessTime)
fi.fullpath, e = full_path_from_name(name, allocator)
fi.name = basename(fi.fullpath)
return
}
_file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE, allocator: runtime.Allocator) -> (File_Info, Error) {
d: win32.BY_HANDLE_FILE_INFORMATION
if !win32.GetFileInformationByHandle(h, &d) {
return {}, _get_platform_error()
}
ti: win32.FILE_ATTRIBUTE_TAG_INFO
if !win32.GetFileInformationByHandleEx(h, .FileAttributeTagInfo, &ti, size_of(ti)) {
err := _get_platform_error()
if perr, ok := is_platform_error(err); ok && perr != i32(win32.ERROR_INVALID_PARAMETER) {
return {}, err
}
// Indicate this is a symlink on FAT file systems
ti.ReparseTag = 0
}
fi: File_Info
fi.fullpath = path
fi.name = basename(path)
fi.inode = u128(u64(d.nFileIndexHigh)<<32 + u64(d.nFileIndexLow))
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, h, 0)
fi.type = type
fi.mode |= mode
fi.creation_time = filetime_as_time(d.ftCreationTime)
fi.modification_time = filetime_as_time(d.ftLastWriteTime)
fi.access_time = filetime_as_time(d.ftLastAccessTime)
return fi, nil
}
reserved_names := [?]string{
"CON", "PRN", "AUX", "NUL",
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
}
_is_reserved_name :: proc(path: string) -> bool {
if len(path) == 0 {
return false
}
for reserved in reserved_names {
if strings.equal_fold(path, reserved) {
return true
}
}
return false
}
_volume_name_len :: proc(path: string) -> (length: int) {
if len(path) < 2 {
return 0
}
if path[1] == ':' {
switch path[0] {
case 'a'..='z', 'A'..='Z':
return 2
}
}
/*
See: URL: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
Further allowed paths can be of the form of:
- \\server\share or \\server\share\more\path
- \\?\C:\...
- \\.\PhysicalDriveX
*/
// Any remaining kind of path has to start with two slashes.
if !_is_path_separator(path[0]) || !_is_path_separator(path[1]) {
return 0
}
// Device path. The volume name is the whole string
if len(path) >= 5 && path[2] == '.' && _is_path_separator(path[3]) {
return len(path)
}
// We're a UNC share `\\host\share`, file namespace `\\?\C:` or UNC in file namespace `\\?\\host\share`
prefix := 2
// File namespace.
if len(path) >= 5 && path[2] == '?' && _is_path_separator(path[3]) {
if _is_path_separator(path[4]) {
// `\\?\\` UNC path in file namespace
prefix = 5
}
if len(path) >= 6 && path[5] == ':' {
switch path[4] {
case 'a'..='z', 'A'..='Z':
return 6
case:
return 0
}
}
}
// UNC path, minimum version of the volume is `\\h\s` for host, share.
// Can also contain an IP address in the host position.
slash_count := 0
for i in prefix..<len(path) {
// Host needs to be at least 1 character
if _is_path_separator(path[i]) && i > 0 {
slash_count += 1
if slash_count == 2 {
return i
}
}
}
return len(path)
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loaded 100 of 169 files, more files were not shown because too many files have changed in this diff. Show more