mirror of
https://github.com/odin-lang/Odin.git
synced 2026-10-20 11:24:26 -04:00
core:os -> core:os/old && core:os/os2 -> core:os
This commit is contained in:
1 parent
8ed264680b
commit
e7dbabf668
169 files changed
+1716
-1718
No files matched your search
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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,7 +1,7 @@
|
||||
#+build !js
|
||||
package image
|
||||
|
||||
import os "core:os/os2"
|
||||
import "core:os"
|
||||
|
||||
load :: proc{
|
||||
load_from_bytes,
|
||||
|
||||
@@ -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,7 +1,7 @@
|
||||
#+build !js
|
||||
package netpbm
|
||||
|
||||
import os "core:os/os2"
|
||||
import "core:os"
|
||||
|
||||
load :: proc {
|
||||
load_from_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}
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
@@ -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] --- ",
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#+build !js
|
||||
package mem_virtual
|
||||
|
||||
import os "core:os/os2"
|
||||
import "core:os"
|
||||
|
||||
map_file :: proc{
|
||||
map_file_from_path,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
@@ -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
@@ -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
@@ -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
@@ -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.
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
@@ -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.
@@ -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.
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
Reference in new issue
Block a user