mirror of
https://github.com/odin-lang/Odin.git
synced 2026-02-11 22:08:42 +00:00
Merge pull request #6245 from odin-lang/new_os
`core:os/os2` -> `core:os` integration
This commit is contained in:
@@ -5,6 +5,7 @@ Example:
|
||||
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.
|
||||
@@ -22,7 +23,8 @@ Example:
|
||||
|
||||
main :: proc() {
|
||||
// Set up output buffer.
|
||||
buf := bytes.Buffer{}
|
||||
buf: bytes.Buffer
|
||||
defer bytes.buffer_destroy(&buf)
|
||||
|
||||
stdout :: proc(s: string) {
|
||||
os.write_string(os.stdout, s)
|
||||
@@ -31,15 +33,13 @@ Example:
|
||||
os.write_string(os.stderr, s)
|
||||
}
|
||||
|
||||
args := os.args
|
||||
|
||||
if len(args) < 2 {
|
||||
if len(os.args) < 2 {
|
||||
stderr("No input file specified.\n")
|
||||
err := load(data=TEST, buf=&buf, known_gzip_size=len(TEST))
|
||||
err := gzip.load(data=TEST, buf=&buf, known_gzip_size=len(TEST))
|
||||
if err == nil {
|
||||
stdout("Displaying test vector: ")
|
||||
stdout("Displaying test vector: \"")
|
||||
stdout(bytes.buffer_to_string(&buf))
|
||||
stdout("\n")
|
||||
stdout("\"\n")
|
||||
} else {
|
||||
fmt.printf("gzip.load returned %v\n", err)
|
||||
}
|
||||
@@ -47,35 +47,31 @@ Example:
|
||||
os.exit(0)
|
||||
}
|
||||
|
||||
// The rest are all files.
|
||||
args = args[1:]
|
||||
err: Error
|
||||
for file in os.args[1:] {
|
||||
err: gzip.Error
|
||||
|
||||
for file in args {
|
||||
if file == "-" {
|
||||
// Read from stdin
|
||||
s := os.stream_from_handle(os.stdin)
|
||||
ctx := &compress.Context_Stream_Input{
|
||||
input = s,
|
||||
input = os.stdin.stream,
|
||||
}
|
||||
err = load(ctx, &buf)
|
||||
err = gzip.load(ctx, &buf)
|
||||
} else {
|
||||
err = load(file, &buf)
|
||||
err = gzip.load(file, &buf)
|
||||
}
|
||||
if err != nil {
|
||||
if err != E_General.File_Not_Found {
|
||||
stderr("File not found: ")
|
||||
stderr(file)
|
||||
stderr("\n")
|
||||
os.exit(1)
|
||||
}
|
||||
switch err {
|
||||
case nil:
|
||||
stdout(bytes.buffer_to_string(&buf))
|
||||
case gzip.E_General.File_Not_Found:
|
||||
stderr("File not found: ")
|
||||
stderr(file)
|
||||
stderr("\n")
|
||||
os.exit(1)
|
||||
case:
|
||||
stderr("GZIP returned an error.\n")
|
||||
bytes.buffer_destroy(&buf)
|
||||
os.exit(2)
|
||||
}
|
||||
stdout(bytes.buffer_to_string(&buf))
|
||||
}
|
||||
bytes.buffer_destroy(&buf)
|
||||
}
|
||||
*/
|
||||
package compress_gzip
|
||||
|
||||
@@ -107,14 +107,10 @@ load :: proc{load_from_bytes, load_from_file, load_from_context}
|
||||
load_from_file :: proc(filename: string, buf: ^bytes.Buffer, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
data, ok := os.read_entire_file(filename)
|
||||
defer delete(data)
|
||||
file_data, file_err := os.read_entire_file(filename, allocator)
|
||||
defer delete(file_data)
|
||||
|
||||
err = E_General.File_Not_Found
|
||||
if ok {
|
||||
err = load_from_bytes(data, buf, len(data), expected_output_size)
|
||||
}
|
||||
return
|
||||
return load_from_bytes(file_data, buf, len(file_data), expected_output_size) if file_err == nil else E_General.File_Not_Found
|
||||
}
|
||||
|
||||
load_from_bytes :: proc(data: []byte, buf: ^bytes.Buffer, known_gzip_size := -1, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||
|
||||
10
core/crypto/hash/hash_js.odin
Normal file
10
core/crypto/hash/hash_js.odin
Normal file
@@ -0,0 +1,10 @@
|
||||
#+build js
|
||||
package crypto_hash
|
||||
|
||||
hash :: proc {
|
||||
hash_stream,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
hash_bytes_to_buffer,
|
||||
hash_string_to_buffer,
|
||||
}
|
||||
@@ -1,26 +1,27 @@
|
||||
#+build !freestanding
|
||||
#+build !js
|
||||
package crypto_hash
|
||||
|
||||
import "core:io"
|
||||
import "core:os"
|
||||
|
||||
// hash_file will read the file provided by the given handle and return the
|
||||
// `hash_file` will read the file provided by the given handle and return the
|
||||
// computed digest in a newly allocated slice.
|
||||
hash_file :: proc(
|
||||
algorithm: Algorithm,
|
||||
hd: os.Handle,
|
||||
hash_file_by_handle :: proc(
|
||||
algorithm: Algorithm,
|
||||
handle: ^os.File,
|
||||
load_at_once := false,
|
||||
allocator := context.allocator,
|
||||
allocator := context.allocator,
|
||||
) -> (
|
||||
[]byte,
|
||||
io.Error,
|
||||
) {
|
||||
if !load_at_once {
|
||||
return hash_stream(algorithm, os.stream_from_handle(hd), allocator)
|
||||
return hash_stream(algorithm, os.to_stream(handle), allocator)
|
||||
}
|
||||
|
||||
buf, ok := os.read_entire_file(hd, allocator)
|
||||
if !ok {
|
||||
buf, err := os.read_entire_file(handle, allocator)
|
||||
if err != nil {
|
||||
return nil, io.Error.Unknown
|
||||
}
|
||||
defer delete(buf, allocator)
|
||||
@@ -28,11 +29,30 @@ hash_file :: proc(
|
||||
return hash_bytes(algorithm, buf, allocator), io.Error.None
|
||||
}
|
||||
|
||||
hash_file_by_name :: proc(
|
||||
algorithm: Algorithm,
|
||||
filename: string,
|
||||
load_at_once := false,
|
||||
allocator := context.allocator,
|
||||
) -> (
|
||||
[]byte,
|
||||
io.Error,
|
||||
) {
|
||||
handle, err := os.open(filename)
|
||||
defer os.close(handle)
|
||||
|
||||
if err != nil {
|
||||
return {}, io.Error.Unknown
|
||||
}
|
||||
return hash_file_by_handle(algorithm, handle, load_at_once, allocator)
|
||||
}
|
||||
|
||||
|
||||
hash :: proc {
|
||||
hash_stream,
|
||||
hash_file,
|
||||
hash_file_by_handle,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
hash_bytes_to_buffer,
|
||||
hash_string_to_buffer,
|
||||
}
|
||||
}
|
||||
@@ -16,14 +16,15 @@ Example:
|
||||
r.reuse_record_buffer = true // Without it you have to each of the fields within it
|
||||
defer csv.reader_destroy(&r)
|
||||
|
||||
csv_data, ok := os.read_entire_file(filename)
|
||||
if ok {
|
||||
csv_data, csv_err := os.read_entire_file(filename, context.allocator)
|
||||
defer delete(csv_data)
|
||||
|
||||
if csv_err == nil {
|
||||
csv.reader_init_with_string(&r, string(csv_data))
|
||||
} else {
|
||||
fmt.printfln("Unable to open file: %v", filename)
|
||||
fmt.eprintfln("Unable to open file: %v. Error: %v", filename, csv_err)
|
||||
return
|
||||
}
|
||||
defer delete(csv_data)
|
||||
|
||||
for r, i, err in csv.iterator_next(&r) {
|
||||
if err != nil { /* Do something with error */ }
|
||||
@@ -39,16 +40,16 @@ Example:
|
||||
r: csv.Reader
|
||||
r.trim_leading_space = true
|
||||
r.reuse_record = true // Without it you have to delete(record)
|
||||
r.reuse_record_buffer = true // Without it you have to each of the fields within it
|
||||
r.reuse_record_buffer = true // Without it you have to delete each of the fields within it
|
||||
defer csv.reader_destroy(&r)
|
||||
|
||||
handle, err := os.open(filename)
|
||||
defer os.close(handle)
|
||||
if err != nil {
|
||||
fmt.eprintfln("Error opening file: %v", filename)
|
||||
fmt.eprintfln("Unable to open file: %v. Error: %v", filename, err)
|
||||
return
|
||||
}
|
||||
defer os.close(handle)
|
||||
csv.reader_init(&r, os.stream_from_handle(handle))
|
||||
csv.reader_init(&r, handle.stream)
|
||||
|
||||
for r, i in csv.iterator_next(&r) {
|
||||
for f, j in r {
|
||||
@@ -64,21 +65,23 @@ Example:
|
||||
r.trim_leading_space = true
|
||||
defer csv.reader_destroy(&r)
|
||||
|
||||
csv_data, ok := os.read_entire_file(filename)
|
||||
if ok {
|
||||
csv.reader_init_with_string(&r, string(csv_data))
|
||||
} else {
|
||||
fmt.printfln("Unable to open file: %v", filename)
|
||||
csv_data, csv_err := os.read_entire_file(filename, context.allocator)
|
||||
defer delete(csv_data, context.allocator)
|
||||
if err != nil {
|
||||
fmt.eprintfln("Unable to open file: %v. Error: %v", filename, csv_err)
|
||||
return
|
||||
}
|
||||
defer delete(csv_data)
|
||||
csv.reader_init_with_string(&r, string(csv_data))
|
||||
|
||||
records, err := csv.read_all(&r)
|
||||
if err != nil { /* Do something with CSV parse error */ }
|
||||
|
||||
defer {
|
||||
for rec in records {
|
||||
delete(rec)
|
||||
for record in records {
|
||||
for field in record {
|
||||
delete(field)
|
||||
}
|
||||
delete(record)
|
||||
}
|
||||
delete(records)
|
||||
}
|
||||
|
||||
34
core/encoding/hxa/hxa_os.odin
Normal file
34
core/encoding/hxa/hxa_os.odin
Normal file
@@ -0,0 +1,34 @@
|
||||
#+build !freestanding
|
||||
#+build !js
|
||||
package encoding_hxa
|
||||
|
||||
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
|
||||
|
||||
data, data_err := os.read_entire_file(filename, allocator, loc)
|
||||
if data_err != nil {
|
||||
err = .Unable_To_Read_File
|
||||
delete(data, allocator)
|
||||
return
|
||||
}
|
||||
file, err = read(data, filename, print_error, allocator)
|
||||
file.backing = data
|
||||
return
|
||||
}
|
||||
|
||||
write_to_file :: proc(filepath: string, file: File) -> (err: Write_Error) {
|
||||
required := required_write_size(file)
|
||||
buf, alloc_err := make([]byte, required)
|
||||
if alloc_err == .Out_Of_Memory {
|
||||
return .Failed_File_Write
|
||||
}
|
||||
defer delete(buf)
|
||||
|
||||
write_internal(&Writer{data = buf}, file)
|
||||
if os.write_entire_file(filepath, buf) != nil {
|
||||
err =.Failed_File_Write
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package encoding_hxa
|
||||
|
||||
import "core:fmt"
|
||||
import "core:os"
|
||||
import "core:mem"
|
||||
|
||||
Read_Error :: enum {
|
||||
@@ -11,20 +10,6 @@ Read_Error :: enum {
|
||||
Unable_To_Read_File,
|
||||
}
|
||||
|
||||
read_from_file :: proc(filename: string, print_error := false, allocator := context.allocator, loc := #caller_location) -> (file: File, err: Read_Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
data, ok := os.read_entire_file(filename, allocator, loc)
|
||||
if !ok {
|
||||
err = .Unable_To_Read_File
|
||||
delete(data, allocator, loc)
|
||||
return
|
||||
}
|
||||
file, err = read(data, filename, print_error, allocator, loc)
|
||||
file.backing = data
|
||||
return
|
||||
}
|
||||
|
||||
read :: proc(data: []byte, filename := "<input>", print_error := false, allocator := context.allocator, loc := #caller_location) -> (file: File, err: Read_Error) {
|
||||
Reader :: struct {
|
||||
filename: string,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package encoding_hxa
|
||||
|
||||
import "core:os"
|
||||
import "core:mem"
|
||||
import "core:mem"
|
||||
|
||||
Write_Error :: enum {
|
||||
None,
|
||||
@@ -9,21 +8,6 @@ Write_Error :: enum {
|
||||
Failed_File_Write,
|
||||
}
|
||||
|
||||
write_to_file :: proc(filepath: string, file: File) -> (err: Write_Error) {
|
||||
required := required_write_size(file)
|
||||
buf, alloc_err := make([]byte, required)
|
||||
if alloc_err == .Out_Of_Memory {
|
||||
return .Failed_File_Write
|
||||
}
|
||||
defer delete(buf)
|
||||
|
||||
write_internal(&Writer{data = buf}, file)
|
||||
if !os.write_entire_file(filepath, buf) {
|
||||
err =.Failed_File_Write
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
write :: proc(buf: []byte, file: File) -> (n: int, err: Write_Error) {
|
||||
required := required_write_size(file)
|
||||
if len(buf) < required {
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
// Reader and writer for a variant of the `.ini` file format with `key = value` entries in `[sections]`.
|
||||
package encoding_ini
|
||||
|
||||
import "base:runtime"
|
||||
import "base:intrinsics"
|
||||
import "core:strings"
|
||||
import "core:strconv"
|
||||
import "core:io"
|
||||
import "core:os"
|
||||
import "core:fmt"
|
||||
import "base:runtime"
|
||||
import "base:intrinsics"
|
||||
import "core:strings"
|
||||
import "core:strconv"
|
||||
import "core:io"
|
||||
import "core:fmt"
|
||||
_ :: fmt
|
||||
|
||||
Options :: struct {
|
||||
@@ -120,17 +119,6 @@ load_map_from_string :: proc(src: string, allocator: runtime.Allocator, options
|
||||
return
|
||||
}
|
||||
|
||||
load_map_from_path :: proc(path: string, allocator: runtime.Allocator, options := DEFAULT_OPTIONS) -> (m: Map, err: runtime.Allocator_Error, ok: bool) {
|
||||
data := os.read_entire_file(path, allocator) or_return
|
||||
defer delete(data, allocator)
|
||||
m, err = load_map_from_string(string(data), allocator, options)
|
||||
ok = err == nil
|
||||
defer if !ok {
|
||||
delete_map(m)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
save_map_to_string :: proc(m: Map, allocator: runtime.Allocator) -> (data: string) {
|
||||
b := strings.builder_make(allocator)
|
||||
_, _ = write_map(strings.to_writer(&b), m)
|
||||
@@ -191,4 +179,4 @@ write_map :: proc(w: io.Writer, m: Map) -> (n: int, err: io.Error) {
|
||||
section_index += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
20
core/encoding/ini/ini_os.odin
Normal file
20
core/encoding/ini/ini_os.odin
Normal file
@@ -0,0 +1,20 @@
|
||||
#+build !freestanding
|
||||
#+build !js
|
||||
package encoding_ini
|
||||
|
||||
import "base:runtime"
|
||||
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)
|
||||
defer delete(data, allocator)
|
||||
if data_err != nil {
|
||||
return
|
||||
}
|
||||
m, err = load_map_from_string(string(data), allocator, options)
|
||||
ok = err == nil
|
||||
defer if !ok {
|
||||
delete_map(m)
|
||||
}
|
||||
return
|
||||
}
|
||||
18
core/encoding/xml/xml_os.odin
Normal file
18
core/encoding/xml/xml_os.odin
Normal file
@@ -0,0 +1,18 @@
|
||||
#+build !freestanding
|
||||
#+build !js
|
||||
package encoding_xml
|
||||
|
||||
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) {
|
||||
context.allocator = allocator
|
||||
options := options
|
||||
|
||||
data, data_err := os.read_entire_file(filename, allocator)
|
||||
if data_err != nil { return {}, .File_Error }
|
||||
|
||||
options.flags += { .Input_May_Be_Modified }
|
||||
|
||||
return parse_bytes(data, options, filename, error_handler, allocator)
|
||||
}
|
||||
@@ -9,13 +9,12 @@ package encoding_xml
|
||||
- Jeroen van Rijn: Initial implementation.
|
||||
*/
|
||||
|
||||
import "core:bytes"
|
||||
import "core:encoding/entity"
|
||||
import "base:intrinsics"
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:strings"
|
||||
import "base:runtime"
|
||||
import "base:runtime"
|
||||
import "core:bytes"
|
||||
import "core:encoding/entity"
|
||||
import "base:intrinsics"
|
||||
import "core:mem"
|
||||
import "core:strings"
|
||||
|
||||
likely :: intrinsics.expect
|
||||
|
||||
@@ -373,19 +372,6 @@ parse_string :: proc(data: string, options := DEFAULT_OPTIONS, path := "", error
|
||||
|
||||
parse :: proc { parse_string, parse_bytes }
|
||||
|
||||
// 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) {
|
||||
context.allocator = allocator
|
||||
options := options
|
||||
|
||||
data, data_ok := os.read_entire_file(filename)
|
||||
if !data_ok { return {}, .File_Error }
|
||||
|
||||
options.flags += { .Input_May_Be_Modified }
|
||||
|
||||
return parse_bytes(data, options, filename, error_handler, allocator)
|
||||
}
|
||||
|
||||
destroy :: proc(doc: ^Document, allocator := context.allocator) {
|
||||
context.allocator = allocator
|
||||
if doc == nil { return }
|
||||
|
||||
@@ -37,8 +37,8 @@ Unified_Parse_Error_Reason :: union #shared_nil {
|
||||
Open_File_Error :: struct {
|
||||
filename: string,
|
||||
errno: os.Error,
|
||||
mode: int,
|
||||
perms: int,
|
||||
flags: os.File_Flags,
|
||||
perms: os.Permissions,
|
||||
}
|
||||
|
||||
// Raised during parsing.
|
||||
|
||||
@@ -76,8 +76,8 @@ Distinct_Int :: distinct int
|
||||
main :: proc() {
|
||||
Options :: struct {
|
||||
|
||||
file: os.Handle `args:"pos=0,required,file=r" usage:"Input file."`,
|
||||
output: os.Handle `args:"pos=1,file=cw" usage:"Output file."`,
|
||||
file: ^os.File `args:"pos=0,required,file=r" usage:"Input file."`,
|
||||
output: ^os.File `args:"pos=1,file=cw" usage:"Output file."`,
|
||||
|
||||
hub: net.Host_Or_Endpoint `usage:"Internet address to contact for updates."`,
|
||||
schedule: datetime.DateTime `usage:"Launch tasks at this time."`,
|
||||
@@ -126,7 +126,7 @@ main :: proc() {
|
||||
|
||||
fmt.printfln("%#v", opt)
|
||||
|
||||
if opt.output != 0 {
|
||||
if opt.output != nil {
|
||||
os.write_string(opt.output, "Hellope!\n")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
#+private
|
||||
package flags
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
import "core:fmt"
|
||||
import "core:mem"
|
||||
import "core:net"
|
||||
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"
|
||||
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 {
|
||||
@@ -209,7 +209,7 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info:
|
||||
parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type: typeid, arg_tag: string, out_error: ^Error) {
|
||||
// Core types currently supported:
|
||||
//
|
||||
// - os.Handle
|
||||
// - ^os.File
|
||||
// - time.Time
|
||||
// - datetime.DateTime
|
||||
// - net.Host_Or_Endpoint
|
||||
@@ -217,64 +217,61 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
|
||||
GENERIC_RFC_3339_ERROR :: "Invalid RFC 3339 string. Try this format: `yyyy-mm-ddThh:mm:ssZ`, for example `2024-02-29T16:30:00Z`."
|
||||
|
||||
out_error^ = nil
|
||||
|
||||
if data_type == os.Handle {
|
||||
if data_type == ^os.File {
|
||||
// NOTE: `os` is hopefully available everywhere, even if it might panic on some calls.
|
||||
wants_read := false
|
||||
wants_write := false
|
||||
mode: int
|
||||
flags: os.File_Flags
|
||||
|
||||
if file, ok := get_struct_subtag(arg_tag, SUBTAG_FILE); ok {
|
||||
for i in 0..<len(file) {
|
||||
#no_bounds_check switch file[i] {
|
||||
case 'r': wants_read = true
|
||||
case 'w': wants_write = true
|
||||
case 'c': mode |= os.O_CREATE
|
||||
case 'a': mode |= os.O_APPEND
|
||||
case 't': mode |= os.O_TRUNC
|
||||
case 'r': flags |= {.Read}
|
||||
case 'w': flags |= {.Write}
|
||||
case 'c': flags |= {.Create}
|
||||
case 'a': flags |= {.Append}
|
||||
case 't': flags |= {.Trunc}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sane default.
|
||||
// owner/group/other: r--r--r--
|
||||
perms: int = 0o444
|
||||
octal_perms: int = 0o444
|
||||
|
||||
if wants_read && wants_write {
|
||||
mode |= os.O_RDWR
|
||||
perms |= 0o200
|
||||
} else if wants_write {
|
||||
mode |= os.O_WRONLY
|
||||
perms |= 0o200
|
||||
if flags >= {.Read, .Write} {
|
||||
octal_perms |= 0o200
|
||||
} else if .Write in flags {
|
||||
octal_perms |= 0o200
|
||||
} else {
|
||||
mode |= os.O_RDONLY
|
||||
flags |= {.Read}
|
||||
}
|
||||
|
||||
if permstr, ok := get_struct_subtag(arg_tag, SUBTAG_PERMS); ok {
|
||||
if value, parse_ok := strconv.parse_u64_of_base(permstr, 8); parse_ok {
|
||||
perms = int(value)
|
||||
octal_perms = int(value)
|
||||
}
|
||||
}
|
||||
|
||||
handle, errno := os.open(str, mode, perms)
|
||||
if errno != nil {
|
||||
perms := os.perm(octal_perms)
|
||||
|
||||
f, error := os.open(str, flags, perms)
|
||||
if error != nil {
|
||||
// NOTE(Feoramund): os.Error is system-dependent, and there's
|
||||
// currently no good way to translate them all into strings.
|
||||
//
|
||||
// The upcoming `os2` package will hopefully solve this.
|
||||
// The upcoming `core:os` package will hopefully solve this.
|
||||
//
|
||||
// We can at least provide the number for now, so the user can look
|
||||
// it up.
|
||||
out_error^ = Open_File_Error {
|
||||
str,
|
||||
errno,
|
||||
mode,
|
||||
error,
|
||||
flags,
|
||||
perms,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
(^os.Handle)(ptr)^ = handle
|
||||
(^^os.File)(ptr)^ = f
|
||||
return
|
||||
}
|
||||
|
||||
@@ -475,6 +472,11 @@ parse_and_set_pointer_by_type :: proc(ptr: rawptr, str: string, type_info: ^runt
|
||||
}
|
||||
|
||||
case:
|
||||
if type_info.id == ^os.File {
|
||||
parse_and_set_pointer_by_named_type(ptr, str, type_info.id, arg_tag, &error)
|
||||
return
|
||||
}
|
||||
|
||||
if !parse_and_set_pointer_by_base_type(ptr, str, type_info) {
|
||||
return Parse_Error {
|
||||
// The caller will add more details.
|
||||
|
||||
@@ -138,20 +138,20 @@ validate_structure :: proc(model_type: $T, style: Parsing_Style, loc := #caller_
|
||||
allowed_to_define_file_perms: bool = ---
|
||||
#partial switch specific_type_info in field.type.variant {
|
||||
case runtime.Type_Info_Map:
|
||||
allowed_to_define_file_perms = specific_type_info.value.id == os.Handle
|
||||
allowed_to_define_file_perms = specific_type_info.value.id == ^os.File
|
||||
case runtime.Type_Info_Dynamic_Array:
|
||||
allowed_to_define_file_perms = specific_type_info.elem.id == os.Handle
|
||||
allowed_to_define_file_perms = specific_type_info.elem.id == ^os.File
|
||||
case:
|
||||
allowed_to_define_file_perms = field.type.id == os.Handle
|
||||
allowed_to_define_file_perms = field.type.id == ^os.File
|
||||
}
|
||||
|
||||
if _, has_file := get_struct_subtag(args_tag, SUBTAG_FILE); has_file {
|
||||
fmt.assertf(allowed_to_define_file_perms, "%T.%s has `%s` defined, but it is not nor does it contain an `os.Handle` type.",
|
||||
fmt.assertf(allowed_to_define_file_perms, "%T.%s has `%s` defined, but it is not nor does it contain an `^os.File` type.",
|
||||
model_type, field.name, SUBTAG_FILE, loc = loc)
|
||||
}
|
||||
|
||||
if _, has_perms := get_struct_subtag(args_tag, SUBTAG_PERMS); has_perms {
|
||||
fmt.assertf(allowed_to_define_file_perms, "%T.%s has `%s` defined, but it is not nor does it contain an `os.Handle` type.",
|
||||
fmt.assertf(allowed_to_define_file_perms, "%T.%s has `%s` defined, but it is not nor does it contain an `^os.File` type.",
|
||||
model_type, field.name, SUBTAG_PERMS, loc = loc)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package flags
|
||||
|
||||
import "core:fmt"
|
||||
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.
|
||||
@@ -38,7 +38,7 @@ parse_or_exit :: proc(
|
||||
|
||||
error := parse(model, args, style, true, true, allocator, loc)
|
||||
if error != nil {
|
||||
stderr := os.stream_from_handle(os.stderr)
|
||||
stderr := os.to_stream(os.stderr)
|
||||
|
||||
if len(args) == 0 {
|
||||
// No arguments entered, and there was an error; show the usage,
|
||||
@@ -65,18 +65,18 @@ Inputs:
|
||||
*/
|
||||
@(optimization_mode="favor_size")
|
||||
print_errors :: proc(data_type: typeid, error: Error, program: string, style: Parsing_Style = .Odin) {
|
||||
stderr := os.stream_from_handle(os.stderr)
|
||||
stdout := os.stream_from_handle(os.stdout)
|
||||
stderr := os.to_stream(os.stderr)
|
||||
stdout := os.to_stream(os.stdout)
|
||||
|
||||
switch specific_error in error {
|
||||
case Parse_Error:
|
||||
fmt.wprintfln(stderr, "[%T.%v] %s", specific_error, specific_error.reason, specific_error.message)
|
||||
case Open_File_Error:
|
||||
fmt.wprintfln(stderr, "[%T#%i] Unable to open file with perms 0o%o in mode 0x%x: %s",
|
||||
fmt.wprintfln(stderr, "[%T#%i] Unable to open file with perms 0o%o and flags %v: %s",
|
||||
specific_error,
|
||||
specific_error.errno,
|
||||
specific_error.perms,
|
||||
specific_error.mode,
|
||||
specific_error.flags,
|
||||
specific_error.filename)
|
||||
case Validation_Error:
|
||||
fmt.wprintfln(stderr, "[%T] %s", specific_error, specific_error.message)
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
#+build js
|
||||
package fmt
|
||||
|
||||
import "core:bufio"
|
||||
import "core:io"
|
||||
import "core:os"
|
||||
|
||||
foreign import "odin_env"
|
||||
|
||||
@(private="file")
|
||||
@@ -12,90 +8,77 @@ foreign odin_env {
|
||||
write :: proc "contextless" (fd: u32, p: []byte) ---
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
write_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
|
||||
if mode == .Write {
|
||||
fd := u32(uintptr(stream_data))
|
||||
write(fd, p)
|
||||
return i64(len(p)), nil
|
||||
}
|
||||
return 0, .Unsupported
|
||||
}
|
||||
stdout :: u32(1)
|
||||
stderr :: u32(2)
|
||||
|
||||
@(private="file")
|
||||
stdout := io.Writer{
|
||||
procedure = write_stream_proc,
|
||||
data = rawptr(uintptr(1)),
|
||||
}
|
||||
@(private="file")
|
||||
stderr := io.Writer{
|
||||
procedure = write_stream_proc,
|
||||
data = rawptr(uintptr(2)),
|
||||
}
|
||||
BUF_SIZE :: 1024
|
||||
|
||||
@(private="file")
|
||||
fd_to_writer :: proc(fd: os.Handle, loc := #caller_location) -> io.Writer {
|
||||
switch fd {
|
||||
case 1: return stdout
|
||||
case 2: return stderr
|
||||
case: panic("`fmt.fprint` variant called with invalid file descriptor for JS, only 1 (stdout) and 2 (stderr) are supported", loc)
|
||||
// TODO: Find a way to grow this if necessary
|
||||
buf: [BUF_SIZE]byte
|
||||
|
||||
@(private="file")
|
||||
get_fd :: proc(f: any, loc := #caller_location) -> (fd: u32) {
|
||||
if _fd, _ok := f.(u32); _ok {
|
||||
fd = _fd
|
||||
}
|
||||
if fd != 1 && fd != 2 {
|
||||
panic("`fmt.fprint` variant called with invalid file descriptor for JS, only 1 (stdout) and 2 (stderr) are supported", loc)
|
||||
}
|
||||
return fd
|
||||
}
|
||||
|
||||
// fprint formats using the default print settings and writes to fd
|
||||
fprint :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true, loc := #caller_location) -> int {
|
||||
buf: [1024]byte
|
||||
b: bufio.Writer
|
||||
defer bufio.writer_flush(&b)
|
||||
|
||||
bufio.writer_init_with_buf(&b, fd_to_writer(fd, loc), buf[:])
|
||||
w := bufio.writer_to_writer(&b)
|
||||
return wprint(w, ..args, sep=sep, flush=flush)
|
||||
// flush is ignored
|
||||
fprint :: proc(f: any, args: ..any, sep := " ", flush := true, loc := #caller_location) -> (n: int) {
|
||||
fd := get_fd(f)
|
||||
s := bprint(buf[:], ..args, sep=sep)
|
||||
n = len(s)
|
||||
write(fd, transmute([]byte)s)
|
||||
return n
|
||||
}
|
||||
|
||||
// fprintln formats using the default print settings and writes to fd
|
||||
fprintln :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true, loc := #caller_location) -> int {
|
||||
buf: [1024]byte
|
||||
b: bufio.Writer
|
||||
defer bufio.writer_flush(&b)
|
||||
|
||||
bufio.writer_init_with_buf(&b, fd_to_writer(fd, loc), buf[:])
|
||||
|
||||
w := bufio.writer_to_writer(&b)
|
||||
return wprintln(w, ..args, sep=sep, flush=flush)
|
||||
// fprintln formats using the default print settings and writes to fd, followed by a newline
|
||||
// flush is ignored
|
||||
fprintln :: proc(f: any, args: ..any, sep := " ", flush := true, loc := #caller_location) -> (n: int) {
|
||||
fd := get_fd(f)
|
||||
s := bprintln(buf[:], ..args, sep=sep)
|
||||
n = len(s)
|
||||
write(fd, transmute([]byte)s)
|
||||
return n
|
||||
}
|
||||
|
||||
// fprintf formats according to the specified format string and writes to fd
|
||||
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, newline := false, loc := #caller_location) -> int {
|
||||
buf: [1024]byte
|
||||
b: bufio.Writer
|
||||
defer bufio.writer_flush(&b)
|
||||
|
||||
bufio.writer_init_with_buf(&b, fd_to_writer(fd, loc), buf[:])
|
||||
|
||||
w := bufio.writer_to_writer(&b)
|
||||
return wprintf(w, fmt, ..args, flush=flush, newline=newline)
|
||||
// flush is ignored
|
||||
fprintf :: proc(f: any, fmt: string, args: ..any, flush := true, newline := false, loc := #caller_location) -> (n: int) {
|
||||
fd := get_fd(f)
|
||||
s := bprintf(buf[:], fmt, ..args, newline=newline)
|
||||
n = len(s)
|
||||
write(fd, transmute([]byte)s)
|
||||
return n
|
||||
}
|
||||
|
||||
// fprintfln formats according to the specified format string and writes to fd, followed by a newline.
|
||||
fprintfln :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, loc := #caller_location) -> int {
|
||||
return fprintf(fd, fmt, ..args, flush=flush, newline=true, loc=loc)
|
||||
// flush is ignored
|
||||
fprintfln :: proc(f: any, fmt: string, args: ..any, flush := true, loc := #caller_location) -> int {
|
||||
return fprintf(f, fmt, ..args, flush=flush, newline=true, loc=loc)
|
||||
}
|
||||
|
||||
// print formats using the default print settings and writes to stdout
|
||||
print :: proc(args: ..any, sep := " ", flush := true) -> int { return wprint(w=stdout, args=args, sep=sep, flush=flush) }
|
||||
print :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(stdout, ..args, sep=sep, flush=flush) }
|
||||
// println formats using the default print settings and writes to stdout
|
||||
println :: proc(args: ..any, sep := " ", flush := true) -> int { return wprintln(w=stdout, args=args, sep=sep, flush=flush) }
|
||||
println :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(stdout, ..args, sep=sep, flush=flush) }
|
||||
// printf formats according to the specififed format string and writes to stdout
|
||||
printf :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush) }
|
||||
printf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(stdout, fmt, ..args, flush=flush) }
|
||||
// printfln formats according to the specified format string and writes to stdout, followed by a newline.
|
||||
printfln :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush, newline=true) }
|
||||
printfln :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(stdout, fmt, ..args, flush=flush, newline=true) }
|
||||
|
||||
// eprint formats using the default print settings and writes to stderr
|
||||
eprint :: proc(args: ..any, sep := " ", flush := true) -> int { return wprint(w=stderr, args=args, sep=sep, flush=flush) }
|
||||
eprint :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(stderr, ..args, sep=sep, flush=flush) }
|
||||
// eprintln formats using the default print settings and writes to stderr
|
||||
eprintln :: proc(args: ..any, sep := " ", flush := true) -> int { return wprintln(w=stderr, args=args, sep=sep, flush=flush) }
|
||||
eprintln :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(stderr, ..args, sep=sep, flush=flush) }
|
||||
// eprintf formats according to the specififed format string and writes to stderr
|
||||
eprintf :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stderr, fmt, ..args, flush=flush) }
|
||||
eprintf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(stderr, fmt, ..args, flush=flush) }
|
||||
// eprintfln formats according to the specified format string and writes to stderr, followed by a newline.
|
||||
eprintfln :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush, newline=true) }
|
||||
eprintfln :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(stderr, fmt, ..args, flush=flush, newline=true) }
|
||||
@@ -8,59 +8,61 @@ 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.
|
||||
|
||||
// fprint formats using the default print settings and writes to fd
|
||||
fprint :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true) -> int {
|
||||
fprint :: proc(f: ^os.File, args: ..any, sep := " ", flush := true) -> int {
|
||||
buf: [1024]byte
|
||||
b: bufio.Writer
|
||||
defer bufio.writer_flush(&b)
|
||||
|
||||
bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:])
|
||||
bufio.writer_init_with_buf(&b, os.to_stream(f), buf[:])
|
||||
w := bufio.writer_to_writer(&b)
|
||||
return wprint(w, ..args, sep=sep, flush=flush)
|
||||
}
|
||||
|
||||
// fprintln formats using the default print settings and writes to fd
|
||||
fprintln :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true) -> int {
|
||||
fprintln :: proc(f: ^os.File, args: ..any, sep := " ", flush := true) -> int {
|
||||
buf: [1024]byte
|
||||
b: bufio.Writer
|
||||
defer bufio.writer_flush(&b)
|
||||
|
||||
bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:])
|
||||
bufio.writer_init_with_buf(&b, os.to_stream(f), buf[:])
|
||||
|
||||
w := bufio.writer_to_writer(&b)
|
||||
return wprintln(w, ..args, sep=sep, flush=flush)
|
||||
}
|
||||
// fprintf formats according to the specified format string and writes to fd
|
||||
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, newline := false) -> int {
|
||||
fprintf :: proc(f: ^os.File, fmt: string, args: ..any, flush := true, newline := false) -> int {
|
||||
buf: [1024]byte
|
||||
b: bufio.Writer
|
||||
defer bufio.writer_flush(&b)
|
||||
|
||||
bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:])
|
||||
bufio.writer_init_with_buf(&b, os.to_stream(f), buf[:])
|
||||
|
||||
w := bufio.writer_to_writer(&b)
|
||||
return wprintf(w, fmt, ..args, flush=flush, newline=newline)
|
||||
}
|
||||
// fprintfln formats according to the specified format string and writes to fd, followed by a newline.
|
||||
fprintfln :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true) -> int {
|
||||
return fprintf(fd, fmt, ..args, flush=flush, newline=true)
|
||||
fprintfln :: proc(f: ^os.File, fmt: string, args: ..any, flush := true) -> int {
|
||||
return fprintf(f, fmt, ..args, flush=flush, newline=true)
|
||||
}
|
||||
fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info, flush := true) -> (n: int, err: io.Error) {
|
||||
fprint_type :: proc(f: ^os.File, info: ^runtime.Type_Info, flush := true) -> (n: int, err: io.Error) {
|
||||
buf: [1024]byte
|
||||
b: bufio.Writer
|
||||
defer bufio.writer_flush(&b)
|
||||
|
||||
bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:])
|
||||
bufio.writer_init_with_buf(&b, os.to_stream(f), buf[:])
|
||||
|
||||
w := bufio.writer_to_writer(&b)
|
||||
return wprint_type(w, info, flush=flush)
|
||||
}
|
||||
fprint_typeid :: proc(fd: os.Handle, id: typeid, flush := true) -> (n: int, err: io.Error) {
|
||||
fprint_typeid :: proc(f: ^os.File, id: typeid, flush := true) -> (n: int, err: io.Error) {
|
||||
buf: [1024]byte
|
||||
b: bufio.Writer
|
||||
defer bufio.writer_flush(&b)
|
||||
|
||||
bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:])
|
||||
bufio.writer_init_with_buf(&b, os.to_stream(f), buf[:])
|
||||
|
||||
w := bufio.writer_to_writer(&b)
|
||||
return wprint_typeid(w, id, flush=flush)
|
||||
|
||||
@@ -9,10 +9,10 @@ load :: proc{load_from_file, load_from_bytes, load_from_context}
|
||||
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
data, ok := os.read_entire_file(filename)
|
||||
defer delete(data)
|
||||
data, data_err := os.read_entire_file(filename, allocator)
|
||||
defer delete(data, allocator)
|
||||
|
||||
if ok {
|
||||
if data_err == nil {
|
||||
return load_from_bytes(data, options)
|
||||
} else {
|
||||
return nil, .Unable_To_Read_File
|
||||
@@ -28,7 +28,7 @@ save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocato
|
||||
defer bytes.buffer_destroy(out)
|
||||
|
||||
save_to_buffer(out, img, options) or_return
|
||||
write_ok := os.write_entire_file(output, out.buf[:])
|
||||
write_err := os.write_entire_file(output, out.buf[:])
|
||||
|
||||
return nil if write_ok else .Unable_To_Write_File
|
||||
return nil if write_err == nil else .Unable_To_Write_File
|
||||
}
|
||||
@@ -8,18 +8,16 @@ load :: proc{
|
||||
load_from_file,
|
||||
}
|
||||
|
||||
|
||||
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
data, ok := os.read_entire_file(filename, allocator)
|
||||
data, data_err := os.read_entire_file(filename, allocator)
|
||||
defer delete(data, allocator)
|
||||
if ok {
|
||||
if data_err == nil {
|
||||
return load_from_bytes(data, options, allocator)
|
||||
} else {
|
||||
return nil, .Unable_To_Read_File
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
which :: proc{
|
||||
which_bytes,
|
||||
which_file,
|
||||
|
||||
@@ -8,10 +8,10 @@ load :: proc{load_from_file, load_from_bytes, load_from_context}
|
||||
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
data, ok := os.read_entire_file(filename)
|
||||
defer delete(data)
|
||||
data, data_err := os.read_entire_file(filename, allocator)
|
||||
defer delete(data, allocator)
|
||||
|
||||
if ok {
|
||||
if data_err == nil {
|
||||
return load_from_bytes(data, options)
|
||||
} else {
|
||||
return nil, .Unable_To_Read_File
|
||||
|
||||
@@ -8,20 +8,18 @@ load :: proc {
|
||||
load_from_bytes,
|
||||
}
|
||||
|
||||
|
||||
load_from_file :: proc(filename: string, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
data, ok := os.read_entire_file(filename); defer delete(data)
|
||||
if !ok {
|
||||
data, data_err := os.read_entire_file(filename, allocator); defer delete(data)
|
||||
if data_err == nil {
|
||||
return load_from_bytes(data)
|
||||
} else {
|
||||
err = .Unable_To_Read_File
|
||||
return
|
||||
}
|
||||
|
||||
return load_from_bytes(data)
|
||||
}
|
||||
|
||||
|
||||
save :: proc {
|
||||
save_to_file,
|
||||
save_to_buffer,
|
||||
@@ -33,7 +31,7 @@ save_to_file :: proc(filename: string, img: ^Image, custom_info: Info = {}, allo
|
||||
data: []byte; defer delete(data)
|
||||
data = save_to_buffer(img, custom_info) or_return
|
||||
|
||||
if ok := os.write_entire_file(filename, data); !ok {
|
||||
if save_err := os.write_entire_file(filename, data); save_err != nil {
|
||||
return .Unable_To_Write_File
|
||||
}
|
||||
|
||||
|
||||
@@ -1,348 +0,0 @@
|
||||
/*
|
||||
Reader for `PNG` images.
|
||||
|
||||
The PNG specification is at [[ https://www.w3.org/TR/PNG/ ]].
|
||||
|
||||
Example:
|
||||
package main
|
||||
|
||||
import "core:image"
|
||||
// import "core:image/png"
|
||||
import "core:bytes"
|
||||
import "core:fmt"
|
||||
|
||||
// For PPM writer
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
|
||||
main :: proc() {
|
||||
track := mem.Tracking_Allocator{}
|
||||
mem.tracking_allocator_init(&track, context.allocator)
|
||||
|
||||
context.allocator = mem.tracking_allocator(&track)
|
||||
|
||||
demo()
|
||||
|
||||
if len(track.allocation_map) > 0 {
|
||||
fmt.println("Leaks:")
|
||||
for _, v in track.allocation_map {
|
||||
fmt.printf("\t%v\n\n", v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
demo :: proc() {
|
||||
file: string
|
||||
|
||||
options := image.Options{.return_metadata}
|
||||
err: image.Error
|
||||
img: ^image.Image
|
||||
|
||||
file = "../../../misc/logo-slim.png"
|
||||
|
||||
img, err = load(file, options)
|
||||
defer destroy(img)
|
||||
|
||||
if err != nil {
|
||||
fmt.printf("Trying to read PNG file %v returned %v\n", file, err)
|
||||
} else {
|
||||
fmt.printf("Image: %vx%vx%v, %v-bit.\n", img.width, img.height, img.channels, img.depth)
|
||||
|
||||
if v, ok := img.metadata.(^image.PNG_Info); ok {
|
||||
// Handle ancillary chunks as you wish.
|
||||
// We provide helper functions for a few types.
|
||||
for c in v.chunks {
|
||||
#partial switch c.header.type {
|
||||
case .tIME:
|
||||
if t, t_ok := core_time(c); t_ok {
|
||||
fmt.printf("[tIME]: %v\n", t)
|
||||
}
|
||||
case .gAMA:
|
||||
if gama, gama_ok := gamma(c); gama_ok {
|
||||
fmt.printf("[gAMA]: %v\n", gama)
|
||||
}
|
||||
case .pHYs:
|
||||
if phys, phys_ok := phys(c); phys_ok {
|
||||
if phys.unit == .Meter {
|
||||
xm := f32(img.width) / f32(phys.ppu_x)
|
||||
ym := f32(img.height) / f32(phys.ppu_y)
|
||||
dpi_x, dpi_y := phys_to_dpi(phys)
|
||||
fmt.printf("[pHYs] Image resolution is %v x %v pixels per meter.\n", phys.ppu_x, phys.ppu_y)
|
||||
fmt.printf("[pHYs] Image resolution is %v x %v DPI.\n", dpi_x, dpi_y)
|
||||
fmt.printf("[pHYs] Image dimensions are %v x %v meters.\n", xm, ym)
|
||||
} else {
|
||||
fmt.printf("[pHYs] x: %v, y: %v pixels per unknown unit.\n", phys.ppu_x, phys.ppu_y)
|
||||
}
|
||||
}
|
||||
case .iTXt, .zTXt, .tEXt:
|
||||
res, ok_text := text(c)
|
||||
if ok_text {
|
||||
if c.header.type == .iTXt {
|
||||
fmt.printf("[iTXt] %v (%v:%v): %v\n", res.keyword, res.language, res.keyword_localized, res.text)
|
||||
} else {
|
||||
fmt.printf("[tEXt/zTXt] %v: %v\n", res.keyword, res.text)
|
||||
}
|
||||
}
|
||||
defer text_destroy(res)
|
||||
case .bKGD:
|
||||
fmt.printf("[bKGD] %v\n", img.background)
|
||||
case .eXIf:
|
||||
if res, ok_exif := exif(c); ok_exif {
|
||||
/*
|
||||
Other than checking the signature and byte order, we don't handle Exif data.
|
||||
If you wish to interpret it, pass it to an Exif parser.
|
||||
*/
|
||||
fmt.printf("[eXIf] %v\n", res)
|
||||
}
|
||||
case .PLTE:
|
||||
if plte, plte_ok := plte(c); plte_ok {
|
||||
fmt.printf("[PLTE] %v\n", plte)
|
||||
} else {
|
||||
fmt.printf("[PLTE] Error\n")
|
||||
}
|
||||
case .hIST:
|
||||
if res, ok_hist := hist(c); ok_hist {
|
||||
fmt.printf("[hIST] %v\n", res)
|
||||
}
|
||||
case .cHRM:
|
||||
if res, ok_chrm := chrm(c); ok_chrm {
|
||||
fmt.printf("[cHRM] %v\n", res)
|
||||
}
|
||||
case .sPLT:
|
||||
res, ok_splt := splt(c)
|
||||
if ok_splt {
|
||||
fmt.printf("[sPLT] %v\n", res)
|
||||
}
|
||||
splt_destroy(res)
|
||||
case .sBIT:
|
||||
if res, ok_sbit := sbit(c); ok_sbit {
|
||||
fmt.printf("[sBIT] %v\n", res)
|
||||
}
|
||||
case .iCCP:
|
||||
res, ok_iccp := iccp(c)
|
||||
if ok_iccp {
|
||||
fmt.printf("[iCCP] %v\n", res)
|
||||
}
|
||||
iccp_destroy(res)
|
||||
case .sRGB:
|
||||
if res, ok_srgb := srgb(c); ok_srgb {
|
||||
fmt.printf("[sRGB] Rendering intent: %v\n", res)
|
||||
}
|
||||
case:
|
||||
type := c.header.type
|
||||
name := chunk_type_to_name(&type)
|
||||
fmt.printf("[%v]: %v\n", name, c.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.printf("Done parsing metadata.\n")
|
||||
|
||||
if err == nil && .do_not_decompress_image not_in options && .info not_in options {
|
||||
if ok := write_image_as_ppm("out.ppm", img); ok {
|
||||
fmt.println("Saved decoded image.")
|
||||
} else {
|
||||
fmt.println("Error saving out.ppm.")
|
||||
fmt.println(img)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Crappy PPM writer used during testing. Don't use in production.
|
||||
write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: bool) {
|
||||
|
||||
_bg :: proc(bg: Maybe([3]u16), x, y: int, high := true) -> (res: [3]u16) {
|
||||
if v, ok := bg.?; ok {
|
||||
res = v
|
||||
} else {
|
||||
if high {
|
||||
l := u16(30 * 256 + 30)
|
||||
|
||||
if (x & 4 == 0) ~ (y & 4 == 0) {
|
||||
res = [3]u16{l, 0, l}
|
||||
} else {
|
||||
res = [3]u16{l >> 1, 0, l >> 1}
|
||||
}
|
||||
} else {
|
||||
if (x & 4 == 0) ~ (y & 4 == 0) {
|
||||
res = [3]u16{30, 30, 30}
|
||||
} else {
|
||||
res = [3]u16{15, 15, 15}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// profiler.timed_proc();
|
||||
using image
|
||||
using os
|
||||
|
||||
flags: int = O_WRONLY|O_CREATE|O_TRUNC
|
||||
|
||||
img := image
|
||||
|
||||
// PBM 16-bit images are big endian
|
||||
when ODIN_ENDIAN == .Little {
|
||||
if img.depth == 16 {
|
||||
// The pixel components are in Big Endian. Let's byteswap back.
|
||||
input := mem.slice_data_cast([]u16, img.pixels.buf[:])
|
||||
output := mem.slice_data_cast([]u16be, img.pixels.buf[:])
|
||||
#no_bounds_check for v, i in input {
|
||||
output[i] = u16be(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pix := bytes.buffer_to_bytes(&img.pixels)
|
||||
|
||||
if len(pix) == 0 || len(pix) < image.width * image.height * int(image.channels) {
|
||||
return false
|
||||
}
|
||||
|
||||
mode: int = 0
|
||||
when ODIN_OS == .Linux || ODIN_OS == .Darwin {
|
||||
// NOTE(justasd): 644 (owner read, write; group read; others read)
|
||||
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
|
||||
}
|
||||
|
||||
fd, err := open(filename, flags, mode)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer close(fd)
|
||||
|
||||
write_string(fd,
|
||||
fmt.tprintf("P6\n%v %v\n%v\n", width, height, uint(1 << uint(depth) - 1)),
|
||||
)
|
||||
|
||||
if channels == 3 {
|
||||
// We don't handle transparency here...
|
||||
write_ptr(fd, raw_data(pix), len(pix))
|
||||
} else {
|
||||
bpp := depth == 16 ? 2 : 1
|
||||
bytes_needed := width * height * 3 * bpp
|
||||
|
||||
op := bytes.Buffer{}
|
||||
bytes.buffer_init_allocator(&op, bytes_needed, bytes_needed)
|
||||
defer bytes.buffer_destroy(&op)
|
||||
|
||||
if channels == 1 {
|
||||
if depth == 16 {
|
||||
assert(len(pix) == width * height * 2)
|
||||
p16 := mem.slice_data_cast([]u16, pix)
|
||||
o16 := mem.slice_data_cast([]u16, op.buf[:])
|
||||
#no_bounds_check for len(p16) != 0 {
|
||||
r := u16(p16[0])
|
||||
o16[0] = r
|
||||
o16[1] = r
|
||||
o16[2] = r
|
||||
p16 = p16[1:]
|
||||
o16 = o16[3:]
|
||||
}
|
||||
} else {
|
||||
o := 0
|
||||
for i := 0; i < len(pix); i += 1 {
|
||||
r := pix[i]
|
||||
op.buf[o ] = r
|
||||
op.buf[o+1] = r
|
||||
op.buf[o+2] = r
|
||||
o += 3
|
||||
}
|
||||
}
|
||||
write_ptr(fd, raw_data(op.buf), len(op.buf))
|
||||
} else if channels == 2 {
|
||||
if depth == 16 {
|
||||
p16 := mem.slice_data_cast([]u16, pix)
|
||||
o16 := mem.slice_data_cast([]u16, op.buf[:])
|
||||
|
||||
bgcol := img.background
|
||||
|
||||
#no_bounds_check for len(p16) != 0 {
|
||||
r := f64(u16(p16[0]))
|
||||
bg: f64
|
||||
if bgcol != nil {
|
||||
v := bgcol.([3]u16)[0]
|
||||
bg = f64(v)
|
||||
}
|
||||
a := f64(u16(p16[1])) / 65535.0
|
||||
l := (a * r) + (1 - a) * bg
|
||||
|
||||
o16[0] = u16(l)
|
||||
o16[1] = u16(l)
|
||||
o16[2] = u16(l)
|
||||
|
||||
p16 = p16[2:]
|
||||
o16 = o16[3:]
|
||||
}
|
||||
} else {
|
||||
o := 0
|
||||
for i := 0; i < len(pix); i += 2 {
|
||||
r := pix[i]; a := pix[i+1]; a1 := f32(a) / 255.0
|
||||
c := u8(f32(r) * a1)
|
||||
op.buf[o ] = c
|
||||
op.buf[o+1] = c
|
||||
op.buf[o+2] = c
|
||||
o += 3
|
||||
}
|
||||
}
|
||||
write_ptr(fd, raw_data(op.buf), len(op.buf))
|
||||
} else if channels == 4 {
|
||||
if depth == 16 {
|
||||
p16 := mem.slice_data_cast([]u16be, pix)
|
||||
o16 := mem.slice_data_cast([]u16be, op.buf[:])
|
||||
|
||||
#no_bounds_check for len(p16) != 0 {
|
||||
|
||||
bg := _bg(img.background, 0, 0)
|
||||
r := f32(p16[0])
|
||||
g := f32(p16[1])
|
||||
b := f32(p16[2])
|
||||
a := f32(p16[3]) / 65535.0
|
||||
|
||||
lr := (a * r) + (1 - a) * f32(bg[0])
|
||||
lg := (a * g) + (1 - a) * f32(bg[1])
|
||||
lb := (a * b) + (1 - a) * f32(bg[2])
|
||||
|
||||
o16[0] = u16be(lr)
|
||||
o16[1] = u16be(lg)
|
||||
o16[2] = u16be(lb)
|
||||
|
||||
p16 = p16[4:]
|
||||
o16 = o16[3:]
|
||||
}
|
||||
} else {
|
||||
o := 0
|
||||
|
||||
for i := 0; i < len(pix); i += 4 {
|
||||
|
||||
x := (i / 4) % width
|
||||
y := i / width / 4
|
||||
|
||||
_b := _bg(img.background, x, y, false)
|
||||
bgcol := [3]u8{u8(_b[0]), u8(_b[1]), u8(_b[2])}
|
||||
|
||||
r := f32(pix[i])
|
||||
g := f32(pix[i+1])
|
||||
b := f32(pix[i+2])
|
||||
a := f32(pix[i+3]) / 255.0
|
||||
|
||||
lr := u8(f32(r) * a + (1 - a) * f32(bgcol[0]))
|
||||
lg := u8(f32(g) * a + (1 - a) * f32(bgcol[1]))
|
||||
lb := u8(f32(b) * a + (1 - a) * f32(bgcol[2]))
|
||||
op.buf[o ] = lr
|
||||
op.buf[o+1] = lg
|
||||
op.buf[o+2] = lb
|
||||
o += 3
|
||||
}
|
||||
}
|
||||
write_ptr(fd, raw_data(op.buf), len(op.buf))
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
*/
|
||||
package png
|
||||
136
core/image/png/example.odin
Normal file
136
core/image/png/example.odin
Normal file
@@ -0,0 +1,136 @@
|
||||
#+build ignore
|
||||
package png_example
|
||||
|
||||
import "core:image"
|
||||
import "core:image/png"
|
||||
import "core:image/tga"
|
||||
import "core:fmt"
|
||||
import "core:mem"
|
||||
|
||||
demo :: proc() {
|
||||
options := image.Options{.return_metadata}
|
||||
err: image.Error
|
||||
img: ^image.Image
|
||||
|
||||
PNG_FILE :: ODIN_ROOT + "misc/logo-slim.png"
|
||||
|
||||
img, err = png.load(PNG_FILE, options)
|
||||
defer png.destroy(img)
|
||||
|
||||
if err != nil {
|
||||
fmt.eprintfln("Trying to read PNG file %v returned %v.", PNG_FILE, err)
|
||||
} else {
|
||||
fmt.printfln("Image: %vx%vx%v, %v-bit.", img.width, img.height, img.channels, img.depth)
|
||||
|
||||
if v, ok := img.metadata.(^image.PNG_Info); ok {
|
||||
// Handle ancillary chunks as you wish.
|
||||
// We provide helper functions for a few types.
|
||||
for c in v.chunks {
|
||||
#partial switch c.header.type {
|
||||
case .tIME:
|
||||
if t, t_ok := png.core_time(c); t_ok {
|
||||
fmt.printfln("[tIME]: %v", t)
|
||||
}
|
||||
case .gAMA:
|
||||
if gama, gama_ok := png.gamma(c); gama_ok {
|
||||
fmt.printfln("[gAMA]: %v", gama)
|
||||
}
|
||||
case .pHYs:
|
||||
if phys, phys_ok := png.phys(c); phys_ok {
|
||||
if phys.unit == .Meter {
|
||||
xm := f32(img.width) / f32(phys.ppu_x)
|
||||
ym := f32(img.height) / f32(phys.ppu_y)
|
||||
dpi_x, dpi_y := png.phys_to_dpi(phys)
|
||||
fmt.printfln("[pHYs] Image resolution is %v x %v pixels per meter.", phys.ppu_x, phys.ppu_y)
|
||||
fmt.printfln("[pHYs] Image resolution is %v x %v DPI.", dpi_x, dpi_y)
|
||||
fmt.printfln("[pHYs] Image dimensions are %v x %v meters.", xm, ym)
|
||||
} else {
|
||||
fmt.printfln("[pHYs] x: %v, y: %v pixels per unknown unit.", phys.ppu_x, phys.ppu_y)
|
||||
}
|
||||
}
|
||||
case .iTXt, .zTXt, .tEXt:
|
||||
res, ok_text := png.text(c)
|
||||
if ok_text {
|
||||
if c.header.type == .iTXt {
|
||||
fmt.printfln("[iTXt] %v (%v:%v): %v", res.keyword, res.language, res.keyword_localized, res.text)
|
||||
} else {
|
||||
fmt.printfln("[tEXt/zTXt] %v: %v", res.keyword, res.text)
|
||||
}
|
||||
}
|
||||
defer png.text_destroy(res)
|
||||
case .bKGD:
|
||||
fmt.printfln("[bKGD] %v", img.background)
|
||||
case .eXIf:
|
||||
if res, ok_exif := png.exif(c); ok_exif {
|
||||
/*
|
||||
Other than checking the signature and byte order, we don't handle Exif data.
|
||||
If you wish to interpret it, pass it to an Exif parser.
|
||||
*/
|
||||
fmt.printfln("[eXIf] %v", res)
|
||||
}
|
||||
case .PLTE:
|
||||
if plte, plte_ok := png.plte(c); plte_ok {
|
||||
fmt.printfln("[PLTE] %v", plte)
|
||||
} else {
|
||||
fmt.printfln("[PLTE] Error")
|
||||
}
|
||||
case .hIST:
|
||||
if res, ok_hist := png.hist(c); ok_hist {
|
||||
fmt.printfln("[hIST] %v", res)
|
||||
}
|
||||
case .cHRM:
|
||||
if res, ok_chrm := png.chrm(c); ok_chrm {
|
||||
fmt.printfln("[cHRM] %v", res)
|
||||
}
|
||||
case .sPLT:
|
||||
res, ok_splt := png.splt(c)
|
||||
if ok_splt {
|
||||
fmt.printfln("[sPLT] %v", res)
|
||||
}
|
||||
png.splt_destroy(res)
|
||||
case .sBIT:
|
||||
if res, ok_sbit := png.sbit(c); ok_sbit {
|
||||
fmt.printfln("[sBIT] %v", res)
|
||||
}
|
||||
case .iCCP:
|
||||
res, ok_iccp := png.iccp(c)
|
||||
if ok_iccp {
|
||||
fmt.printfln("[iCCP] %v", res)
|
||||
}
|
||||
png.iccp_destroy(res)
|
||||
case .sRGB:
|
||||
if res, ok_srgb := png.srgb(c); ok_srgb {
|
||||
fmt.printfln("[sRGB] Rendering intent: %v", res)
|
||||
}
|
||||
case:
|
||||
type := c.header.type
|
||||
name := png.chunk_type_to_name(&type)
|
||||
fmt.printfln("[%v]: %v", name, c.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.printfln("Done parsing metadata.")
|
||||
|
||||
if err == nil && .do_not_decompress_image not_in options && .info not_in options {
|
||||
if err = tga.save("out.tga", img); err == nil {
|
||||
fmt.println("Saved decoded image.")
|
||||
} else {
|
||||
fmt.eprintfln("Error %v saving out.ppm.", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
track: mem.Tracking_Allocator
|
||||
mem.tracking_allocator_init(&track, context.allocator)
|
||||
defer mem.tracking_allocator_destroy(&track)
|
||||
context.allocator = mem.tracking_allocator(&track)
|
||||
|
||||
demo()
|
||||
|
||||
for _, leak in track.allocation_map {
|
||||
fmt.printf("%v leaked %m", leak.location, leak.size)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
#+feature using-stmt
|
||||
// Reader for `PNG` images.
|
||||
// The PNG specification is at [[ https://www.w3.org/TR/PNG/ ]].
|
||||
package png
|
||||
|
||||
/*
|
||||
|
||||
@@ -8,12 +8,12 @@ load :: proc{load_from_file, load_from_bytes, load_from_context}
|
||||
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
data, ok := os.read_entire_file(filename)
|
||||
defer delete(data)
|
||||
data, data_err := os.read_entire_file(filename, allocator)
|
||||
defer delete(data, allocator)
|
||||
|
||||
if ok {
|
||||
if data_err == nil {
|
||||
return load_from_bytes(data, options)
|
||||
} else {
|
||||
return nil, .Unable_To_Read_File
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,22 @@ package qoi
|
||||
import "core:os"
|
||||
import "core:bytes"
|
||||
|
||||
save :: proc{save_to_buffer, save_to_file}
|
||||
load :: proc{load_from_file, load_from_bytes, load_from_context}
|
||||
|
||||
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
data, data_err := os.read_entire_file(filename, allocator)
|
||||
defer delete(data, allocator)
|
||||
|
||||
if data_err == nil {
|
||||
return load_from_bytes(data, options)
|
||||
} else {
|
||||
return nil, .Unable_To_Read_File
|
||||
}
|
||||
}
|
||||
|
||||
save :: proc{save_to_buffer, save_to_file}
|
||||
|
||||
save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
|
||||
context.allocator = allocator
|
||||
@@ -14,24 +28,7 @@ save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocato
|
||||
defer bytes.buffer_destroy(out)
|
||||
|
||||
save_to_buffer(out, img, options) or_return
|
||||
write_ok := os.write_entire_file(output, out.buf[:])
|
||||
write_err := os.write_entire_file(output, out.buf[:])
|
||||
|
||||
return nil if write_ok else .Unable_To_Write_File
|
||||
}
|
||||
|
||||
|
||||
load :: proc{load_from_file, load_from_bytes, load_from_context}
|
||||
|
||||
|
||||
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
data, ok := os.read_entire_file(filename)
|
||||
defer delete(data)
|
||||
|
||||
if ok {
|
||||
return load_from_bytes(data, options)
|
||||
} else {
|
||||
return nil, .Unable_To_Read_File
|
||||
}
|
||||
return nil if write_err == nil else .Unable_To_Write_File
|
||||
}
|
||||
@@ -4,6 +4,21 @@ package tga
|
||||
import "core:os"
|
||||
import "core:bytes"
|
||||
|
||||
load :: proc{load_from_file, load_from_bytes, load_from_context}
|
||||
|
||||
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
data, data_err := os.read_entire_file(filename, allocator)
|
||||
defer delete(data)
|
||||
|
||||
if data_err == nil {
|
||||
return load_from_bytes(data, options)
|
||||
} else {
|
||||
return nil, .Unable_To_Read_File
|
||||
}
|
||||
}
|
||||
|
||||
save :: proc{save_to_buffer, save_to_file}
|
||||
|
||||
save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
|
||||
@@ -13,22 +28,7 @@ save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocato
|
||||
defer bytes.buffer_destroy(out)
|
||||
|
||||
save_to_buffer(out, img, options) or_return
|
||||
write_ok := os.write_entire_file(output, out.buf[:])
|
||||
write_err := os.write_entire_file(output, out.buf[:])
|
||||
|
||||
return nil if write_ok else .Unable_To_Write_File
|
||||
}
|
||||
|
||||
load :: proc{load_from_file, load_from_bytes, load_from_context}
|
||||
|
||||
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
data, ok := os.read_entire_file(filename)
|
||||
defer delete(data)
|
||||
|
||||
if ok {
|
||||
return load_from_bytes(data, options)
|
||||
} else {
|
||||
return nil, .Unable_To_Read_File
|
||||
}
|
||||
return nil if write_err == nil else .Unable_To_Write_File
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#+build !freestanding
|
||||
#+build !orca
|
||||
#+build !js
|
||||
package log
|
||||
|
||||
import "base:runtime"
|
||||
@@ -35,7 +36,7 @@ Default_File_Logger_Opts :: Options{
|
||||
|
||||
|
||||
File_Console_Logger_Data :: struct {
|
||||
file_handle: os.Handle,
|
||||
file_handle: ^os.File,
|
||||
ident: string,
|
||||
}
|
||||
|
||||
@@ -66,16 +67,16 @@ init_standard_stream_status :: proc "contextless" () {
|
||||
}
|
||||
}
|
||||
|
||||
create_file_logger :: proc(h: os.Handle, lowest := Level.Debug, opt := Default_File_Logger_Opts, ident := "", allocator := context.allocator) -> Logger {
|
||||
create_file_logger :: proc(f: ^os.File, lowest := Level.Debug, opt := Default_File_Logger_Opts, ident := "", allocator := context.allocator) -> Logger {
|
||||
data := new(File_Console_Logger_Data, allocator)
|
||||
data.file_handle = h
|
||||
data.file_handle = f
|
||||
data.ident = ident
|
||||
return Logger{file_logger_proc, data, lowest, opt}
|
||||
}
|
||||
|
||||
destroy_file_logger :: proc(log: Logger, allocator := context.allocator) {
|
||||
data := cast(^File_Console_Logger_Data)log.data
|
||||
if data.file_handle != os.INVALID_HANDLE {
|
||||
if data.file_handle != nil {
|
||||
os.close(data.file_handle)
|
||||
}
|
||||
free(data, allocator)
|
||||
@@ -83,7 +84,7 @@ destroy_file_logger :: proc(log: Logger, allocator := context.allocator) {
|
||||
|
||||
create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logger_Opts, ident := "", allocator := context.allocator) -> Logger {
|
||||
data := new(File_Console_Logger_Data, allocator)
|
||||
data.file_handle = os.INVALID_HANDLE
|
||||
data.file_handle = nil
|
||||
data.ident = ident
|
||||
return Logger{console_logger_proc, data, lowest, opt}
|
||||
}
|
||||
@@ -93,7 +94,7 @@ destroy_console_logger :: proc(log: Logger, allocator := context.allocator) {
|
||||
}
|
||||
|
||||
@(private)
|
||||
_file_console_logger_proc :: proc(h: os.Handle, ident: string, level: Level, text: string, options: Options, location: runtime.Source_Code_Location) {
|
||||
_file_console_logger_proc :: proc(h: ^os.File, ident: string, level: Level, text: string, options: Options, location: runtime.Source_Code_Location) {
|
||||
backing: [1024]byte //NOTE(Hoej): 1024 might be too much for a header backing, unless somebody has really long paths.
|
||||
buf := strings.builder_from_bytes(backing[:])
|
||||
|
||||
@@ -106,9 +107,7 @@ _file_console_logger_proc :: proc(h: os.Handle, ident: string, level: Level, tex
|
||||
do_location_header(options, &buf, location)
|
||||
|
||||
if .Thread_Id in options {
|
||||
// NOTE(Oskar): not using context.thread_id here since that could be
|
||||
// incorrect when replacing context for a thread.
|
||||
fmt.sbprintf(&buf, "[{}] ", os.current_thread_id())
|
||||
fmt.sbprintf(&buf, "[{}] ", os.get_current_thread_id())
|
||||
}
|
||||
|
||||
if ident != "" {
|
||||
@@ -126,7 +125,7 @@ file_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, option
|
||||
console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
|
||||
options := options
|
||||
data := cast(^File_Console_Logger_Data)logger_data
|
||||
h: os.Handle = ---
|
||||
h: ^os.File = nil
|
||||
if level < Level.Error {
|
||||
h = os.stdout
|
||||
options -= global_subtract_stdout_options
|
||||
@@ -216,4 +215,4 @@ do_location_header :: proc(opts: Options, buf: ^strings.Builder, location := #ca
|
||||
}
|
||||
|
||||
fmt.sbprint(buf, "] ")
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,8 @@ package math_big
|
||||
- Also look at extracting and splatting several digits at once.
|
||||
*/
|
||||
|
||||
import "base:intrinsics"
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "base:intrinsics"
|
||||
import "core:mem"
|
||||
|
||||
/*
|
||||
This version of `itoa` allocates on behalf of the caller. The caller must free the string.
|
||||
@@ -386,64 +385,7 @@ radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false, allocator := con
|
||||
return size, nil
|
||||
}
|
||||
|
||||
/*
|
||||
We might add functions to read and write byte-encoded Ints from/to files, using `int_to_bytes_*` functions.
|
||||
|
||||
LibTomMath allows exporting/importing to/from a file in ASCII, but it doesn't support a much more compact representation in binary, even though it has several pack functions int_to_bytes_* (which I expanded upon and wrote Python interoperable versions of as well), and (un)pack, which is GMP compatible.
|
||||
Someone could implement their own read/write binary int procedures, of course.
|
||||
|
||||
Could be worthwhile to add a canonical binary file representation with an optional small header that says it's an Odin big.Int, big.Rat or Big.Float, byte count for each component that follows, flag for big/little endian and a flag that says a checksum exists at the end of the file.
|
||||
For big.Rat and big.Float the header couldn't be optional, because we'd have no way to distinguish where the components end.
|
||||
*/
|
||||
|
||||
/*
|
||||
Read an Int from an ASCII file.
|
||||
*/
|
||||
internal_int_read_from_ascii_file :: proc(a: ^Int, filename: string, radix := i8(10), allocator := context.allocator) -> (err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
/*
|
||||
We can either read the entire file at once, or read a bunch at a time and keep multiplying by the radix.
|
||||
For now, we'll read the entire file. Eventually we'll replace this with a copy that duplicates the logic
|
||||
of `atoi` so we don't need to read the entire file.
|
||||
*/
|
||||
|
||||
res, ok := os.read_entire_file(filename, allocator)
|
||||
defer delete(res, allocator)
|
||||
|
||||
if !ok {
|
||||
return .Cannot_Read_File
|
||||
}
|
||||
|
||||
as := string(res)
|
||||
return atoi(a, as, radix)
|
||||
}
|
||||
|
||||
/*
|
||||
Write an Int to an ASCII file.
|
||||
*/
|
||||
internal_int_write_to_ascii_file :: proc(a: ^Int, filename: string, radix := i8(10), allocator := context.allocator) -> (err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
/*
|
||||
For now we'll convert the Int using itoa and writing the result in one go.
|
||||
If we want to preserve memory we could duplicate the itoa logic and write backwards.
|
||||
*/
|
||||
|
||||
as := itoa(a, radix) or_return
|
||||
defer delete(as)
|
||||
|
||||
l := len(as)
|
||||
assert(l > 0)
|
||||
|
||||
data := transmute([]u8)mem.Raw_Slice{
|
||||
data = raw_data(as),
|
||||
len = l,
|
||||
}
|
||||
|
||||
ok := os.write_entire_file(filename, data, truncate=true)
|
||||
return nil if ok else .Cannot_Write_File
|
||||
}
|
||||
|
||||
/*
|
||||
Calculate the size needed for `internal_int_pack`.
|
||||
|
||||
79
core/math/big/radix_os.odin
Normal file
79
core/math/big/radix_os.odin
Normal file
@@ -0,0 +1,79 @@
|
||||
#+build !freestanding
|
||||
#+build !js
|
||||
package math_big
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's license.
|
||||
|
||||
An arbitrary precision mathematics implementation in Odin.
|
||||
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
|
||||
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
|
||||
|
||||
This file contains radix conversions, `string_to_int` (atoi) and `int_to_string` (itoa).
|
||||
|
||||
TODO:
|
||||
- Use Barrett reduction for non-powers-of-two.
|
||||
- Also look at extracting and splatting several digits at once.
|
||||
*/
|
||||
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
|
||||
/*
|
||||
We might add functions to read and write byte-encoded Ints from/to files, using `int_to_bytes_*` functions.
|
||||
|
||||
LibTomMath allows exporting/importing to/from a file in ASCII, but it doesn't support a much more compact representation in binary, even though it has several pack functions int_to_bytes_* (which I expanded upon and wrote Python interoperable versions of as well), and (un)pack, which is GMP compatible.
|
||||
Someone could implement their own read/write binary int procedures, of course.
|
||||
|
||||
Could be worthwhile to add a canonical binary file representation with an optional small header that says it's an Odin big.Int, big.Rat or Big.Float, byte count for each component that follows, flag for big/little endian and a flag that says a checksum exists at the end of the file.
|
||||
For big.Rat and big.Float the header couldn't be optional, because we'd have no way to distinguish where the components end.
|
||||
*/
|
||||
|
||||
/*
|
||||
Read an Int from an ASCII file.
|
||||
*/
|
||||
internal_int_read_from_ascii_file :: proc(a: ^Int, filename: string, radix := i8(10), allocator := context.allocator) -> (err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
/*
|
||||
We can either read the entire file at once, or read a bunch at a time and keep multiplying by the radix.
|
||||
For now, we'll read the entire file. Eventually we'll replace this with a copy that duplicates the logic
|
||||
of `atoi` so we don't need to read the entire file.
|
||||
*/
|
||||
res, res_err := os.read_entire_file(filename, allocator)
|
||||
defer delete(res, allocator)
|
||||
|
||||
if res_err != nil {
|
||||
return .Cannot_Read_File
|
||||
}
|
||||
|
||||
as := string(res)
|
||||
return atoi(a, as, radix)
|
||||
}
|
||||
|
||||
/*
|
||||
Write an Int to an ASCII file.
|
||||
*/
|
||||
internal_int_write_to_ascii_file :: proc(a: ^Int, filename: string, radix := i8(10), allocator := context.allocator) -> (err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
/*
|
||||
For now we'll convert the Int using itoa and writing the result in one go.
|
||||
If we want to preserve memory we could duplicate the itoa logic and write backwards.
|
||||
*/
|
||||
|
||||
as := itoa(a, radix) or_return
|
||||
defer delete(as)
|
||||
|
||||
l := len(as)
|
||||
assert(l > 0)
|
||||
|
||||
data := transmute([]u8)mem.Raw_Slice{
|
||||
data = raw_data(as),
|
||||
len = l,
|
||||
}
|
||||
|
||||
write_err := os.write_entire_file(filename, data, truncate=true)
|
||||
return nil if write_err == nil else .Cannot_Write_File
|
||||
}
|
||||
16
core/mem/virtual/common.odin
Normal file
16
core/mem/virtual/common.odin
Normal file
@@ -0,0 +1,16 @@
|
||||
package mem_virtual
|
||||
|
||||
Map_File_Error :: enum {
|
||||
None,
|
||||
Open_Failure,
|
||||
Stat_Failure,
|
||||
Negative_Size,
|
||||
Too_Large_Size,
|
||||
Map_Failure,
|
||||
}
|
||||
|
||||
Map_File_Flag :: enum u32 {
|
||||
Read,
|
||||
Write,
|
||||
}
|
||||
Map_File_Flags :: distinct bit_set[Map_File_Flag; u32]
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
A platform agnostic way to reserve/commit/decommit virtual memory.
|
||||
|
||||
|
||||
virtual.Arena usage
|
||||
|
||||
Example:
|
||||
@@ -27,14 +26,14 @@ Example:
|
||||
// See arena_init_buffer for an arena that does not use virtual memory,
|
||||
// instead it relies on you feeding it a buffer.
|
||||
|
||||
f1, f1_ok := os.read_entire_file("file1.txt", arena_alloc)
|
||||
ensure(f1_ok)
|
||||
f1, f1_err := os.read_entire_file("file1.txt", arena_alloc)
|
||||
ensure(f1_err == nil)
|
||||
|
||||
f2, f2_ok := os.read_entire_file("file2.txt", arena_alloc)
|
||||
ensure(f2_ok)
|
||||
f2, f2_err := os.read_entire_file("file2.txt", arena_alloc)
|
||||
ensure(f2_err == nil)
|
||||
|
||||
f3, f3_ok := os.read_entire_file("file3.txt", arena_alloc)
|
||||
ensure(f3_ok)
|
||||
f3, f3_err := os.read_entire_file("file3.txt", arena_alloc)
|
||||
ensure(f3_err == nil)
|
||||
|
||||
res := make([]string, 3, arena_alloc)
|
||||
res[0] = string(f1)
|
||||
@@ -56,7 +55,21 @@ Example:
|
||||
vmem.arena_destroy(&arena)
|
||||
}
|
||||
|
||||
virtual.map_file usage
|
||||
|
||||
Example:
|
||||
// Source: https://github.com/odin-lang/examples/blob/master/arena_allocator/arena_allocator.odin
|
||||
import "core:fmt"
|
||||
|
||||
// virtual package implements cross-platform file memory mapping
|
||||
import vmem "core:mem/virtual"
|
||||
|
||||
main :: proc() {
|
||||
data, err := virtual.map_file_from_path(#file, {.Read})
|
||||
defer virtual.unmap_file(data)
|
||||
fmt.printfln("Error: %v", err)
|
||||
fmt.printfln("Data: %s", data)
|
||||
}
|
||||
*/
|
||||
package mem_virtual
|
||||
|
||||
|
||||
@@ -1,39 +1,25 @@
|
||||
#+build !freestanding
|
||||
#+build !js
|
||||
package mem_virtual
|
||||
|
||||
import "core:os"
|
||||
|
||||
Map_File_Error :: enum {
|
||||
None,
|
||||
Open_Failure,
|
||||
Stat_Failure,
|
||||
Negative_Size,
|
||||
Too_Large_Size,
|
||||
Map_Failure,
|
||||
}
|
||||
|
||||
Map_File_Flag :: enum u32 {
|
||||
Read,
|
||||
Write,
|
||||
}
|
||||
Map_File_Flags :: distinct bit_set[Map_File_Flag; u32]
|
||||
|
||||
map_file :: proc{
|
||||
map_file_from_path,
|
||||
map_file_from_file_descriptor,
|
||||
map_file_from_file,
|
||||
}
|
||||
|
||||
map_file_from_path :: proc(filename: string, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {
|
||||
fd, err := os.open(filename, os.O_RDWR)
|
||||
f, err := os.open(filename, os.O_RDWR)
|
||||
if err != nil {
|
||||
return nil, .Open_Failure
|
||||
}
|
||||
defer os.close(fd)
|
||||
|
||||
return map_file_from_file_descriptor(uintptr(fd), flags)
|
||||
defer os.close(f)
|
||||
return map_file_from_file(f, flags)
|
||||
}
|
||||
|
||||
map_file_from_file_descriptor :: proc(fd: uintptr, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {
|
||||
size, os_err := os.file_size(os.Handle(fd))
|
||||
map_file_from_file :: proc(f: ^os.File, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {
|
||||
size, os_err := os.file_size(f)
|
||||
if os_err != nil {
|
||||
return nil, .Stat_Failure
|
||||
}
|
||||
@@ -43,5 +29,12 @@ map_file_from_file_descriptor :: proc(fd: uintptr, flags: Map_File_Flags) -> (da
|
||||
if size != i64(int(size)) {
|
||||
return nil, .Too_Large_Size
|
||||
}
|
||||
fd := os.fd(f)
|
||||
return _map_file(fd, size, flags)
|
||||
}
|
||||
|
||||
unmap_file :: proc(data: []byte) {
|
||||
if raw_data(data) != nil {
|
||||
_unmap_file(data)
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,6 @@ _platform_memory_init :: proc "contextless" () {
|
||||
assert_contextless(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0)
|
||||
}
|
||||
|
||||
|
||||
_map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {
|
||||
prot: linux.Mem_Protection
|
||||
if .Read in flags {
|
||||
@@ -66,3 +65,7 @@ _map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags)
|
||||
}
|
||||
return ([^]byte)(addr)[:size], nil
|
||||
}
|
||||
|
||||
_unmap_file :: proc "contextless" (data: []byte) {
|
||||
_release(raw_data(data), uint(len(data)))
|
||||
}
|
||||
@@ -26,8 +26,13 @@ _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags)
|
||||
}
|
||||
|
||||
_platform_memory_init :: proc "contextless" () {
|
||||
|
||||
}
|
||||
|
||||
_map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {
|
||||
_map_file :: proc "contextless" (f: any, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {
|
||||
return nil, .Map_Failure
|
||||
}
|
||||
|
||||
_unmap_file :: proc "contextless" (data: []byte) {
|
||||
|
||||
}
|
||||
@@ -47,3 +47,7 @@ _map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags)
|
||||
}
|
||||
return ([^]byte)(addr)[:size], nil
|
||||
}
|
||||
|
||||
_unmap_file :: proc "contextless" (data: []byte) {
|
||||
_release(raw_data(data), uint(len(data)))
|
||||
}
|
||||
@@ -82,6 +82,8 @@ foreign Kernel32 {
|
||||
dwFileOffsetLow: u32,
|
||||
dwNumberOfBytesToMap: uint,
|
||||
) -> rawptr ---
|
||||
|
||||
UnmapViewOfFile :: proc(lpBaseAddress: rawptr) -> b32 ---
|
||||
}
|
||||
|
||||
@(no_sanitize_address)
|
||||
@@ -185,3 +187,8 @@ _map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags)
|
||||
file_data := MapViewOfFile(handle, desired_access, 0, 0, uint(size))
|
||||
return ([^]byte)(file_data)[:size], nil
|
||||
}
|
||||
|
||||
@(no_sanitize_address)
|
||||
_unmap_file :: proc "contextless" (data: []byte) {
|
||||
UnmapViewOfFile(raw_data(data))
|
||||
}
|
||||
@@ -23,7 +23,6 @@ package net
|
||||
|
||||
@(require)
|
||||
import "base:runtime"
|
||||
|
||||
import "core:bufio"
|
||||
import "core:io"
|
||||
import "core:math/rand"
|
||||
|
||||
@@ -7,7 +7,8 @@ import "core:os"
|
||||
load_resolv_conf :: proc(resolv_conf_path: string, allocator := context.allocator) -> (name_servers: []Endpoint, ok: bool) {
|
||||
context.allocator = allocator
|
||||
|
||||
res := os.read_entire_file_from_filename(resolv_conf_path) or_return
|
||||
res, err := os.read_entire_file(resolv_conf_path, allocator)
|
||||
if err != nil { return }
|
||||
defer delete(res)
|
||||
resolv_str := string(res)
|
||||
|
||||
@@ -15,10 +16,9 @@ load_resolv_conf :: proc(resolv_conf_path: string, allocator := context.allocato
|
||||
}
|
||||
|
||||
load_hosts :: proc(hosts_file_path: string, allocator := context.allocator) -> (hosts: []DNS_Host_Entry, ok: bool) {
|
||||
hosts_file, err := os.open(hosts_file_path)
|
||||
handle, err := os.open(hosts_file_path)
|
||||
if err != nil { return }
|
||||
defer os.close(hosts_file)
|
||||
|
||||
return parse_hosts(os.stream_from_handle(hosts_file), allocator)
|
||||
}
|
||||
defer os.close(handle)
|
||||
|
||||
return parse_hosts(os.to_stream(handle), allocator)
|
||||
}
|
||||
@@ -11,8 +11,8 @@ import "core:strings"
|
||||
collect_package :: proc(path: string) -> (pkg: ^ast.Package, success: bool) {
|
||||
NO_POS :: tokenizer.Pos{}
|
||||
|
||||
pkg_path, pkg_path_ok := filepath.abs(path)
|
||||
if !pkg_path_ok {
|
||||
pkg_path, pkg_path_err := os.get_absolute_path(path, context.allocator)
|
||||
if pkg_path_err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -28,14 +28,13 @@ collect_package :: proc(path: string) -> (pkg: ^ast.Package, success: bool) {
|
||||
pkg.fullpath = pkg_path
|
||||
|
||||
for match in matches {
|
||||
src: []byte
|
||||
fullpath, ok := filepath.abs(match)
|
||||
if !ok {
|
||||
fullpath, fullpath_err := os.get_absolute_path(match, context.allocator)
|
||||
if fullpath_err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
src, ok = os.read_entire_file(fullpath)
|
||||
if !ok {
|
||||
src, src_err := os.read_entire_file(fullpath, context.allocator)
|
||||
if src_err != nil {
|
||||
delete(fullpath)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#+private
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
import "core:slice"
|
||||
@@ -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")
|
||||
24
core/os/dir_js.odin
Normal file
24
core/os/dir_js.odin
Normal file
@@ -0,0 +1,24 @@
|
||||
#+build js wasm32, js wasm64p32
|
||||
#+private
|
||||
package os
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
Read_Directory_Iterator_Impl :: struct {
|
||||
fullpath: [dynamic]byte,
|
||||
buf: []byte,
|
||||
off: int,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
|
||||
return {}, -1, false
|
||||
}
|
||||
|
||||
_read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
|
||||
|
||||
}
|
||||
|
||||
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#+private
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "core:sys/linux"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#+private
|
||||
#+build darwin, netbsd, freebsd, openbsd
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "core:sys/posix"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#+private
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "core:sys/darwin"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "core:container/queue"
|
||||
|
||||
@@ -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")
|
||||
@@ -227,4 +227,4 @@ walker_walk :: proc(w: ^Walker) -> (fi: File_Info, ok: bool) {
|
||||
}
|
||||
|
||||
return info, iter_ok
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
#+private
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
import "core:slice"
|
||||
import "base:intrinsics"
|
||||
import "core:sys/wasm/wasi"
|
||||
@@ -1,114 +1,144 @@
|
||||
#+private
|
||||
package os
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
6
core/os/doc.odin
Normal file
6
core/os/doc.odin
Normal file
@@ -0,0 +1,6 @@
|
||||
// package os_old provides a platform-independent interface to operating system functionality.
|
||||
// The design is UNIX-like but with Odin-like error handling. Failing calls return values with a specific error type rather than error number.
|
||||
//
|
||||
// The package os_old interface is intended to be uniform across all operating systems.
|
||||
// Features not generally available appear in the system-specific packages under core:sys/*.
|
||||
package os
|
||||
@@ -1,4 +1,4 @@
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
import "core:strings"
|
||||
42
core/os/env_js.odin
Normal file
42
core/os/env_js.odin
Normal file
@@ -0,0 +1,42 @@
|
||||
#+build js wasm32, js wasm64p32
|
||||
#+private
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
build_env :: proc() -> (err: Error) {
|
||||
return
|
||||
}
|
||||
|
||||
// delete_string_if_not_original :: proc(str: string) {
|
||||
|
||||
// }
|
||||
|
||||
@(require_results)
|
||||
_lookup_env_alloc :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
_lookup_env_buf :: proc(buf: []u8, key: string) -> (value: string, error: Error) {
|
||||
return "", .Unsupported
|
||||
}
|
||||
_lookup_env :: proc{_lookup_env_alloc, _lookup_env_buf}
|
||||
|
||||
@(require_results)
|
||||
_set_env :: proc(key, value: string) -> (err: Error) {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_unset_env :: proc(key: string) -> bool {
|
||||
return true
|
||||
}
|
||||
|
||||
_clear_env :: proc() {
|
||||
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error) {
|
||||
return {}, .Unsupported
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#+private
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
import "base:intrinsics"
|
||||
@@ -1,6 +1,6 @@
|
||||
#+private
|
||||
#+build darwin, netbsd, freebsd, openbsd
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#+private
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@@ -1,30 +1,37 @@
|
||||
#+private
|
||||
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) {
|
||||
_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
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package os
|
||||
|
||||
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
|
||||
|
||||
13
core/os/errors_js.odin
Normal file
13
core/os/errors_js.odin
Normal file
@@ -0,0 +1,13 @@
|
||||
#+build js wasm32, js wasm64p32
|
||||
#+private
|
||||
package os
|
||||
|
||||
_Platform_Error :: enum i32 {}
|
||||
|
||||
_error_string :: proc(errno: i32) -> string {
|
||||
return "<unknown platform error>"
|
||||
}
|
||||
|
||||
_get_platform_error :: proc(errno: _Platform_Error) -> Error {
|
||||
return Platform_Error(errno)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#+private
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "core:sys/linux"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#+private
|
||||
#+build darwin, netbsd, freebsd, openbsd
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "core:sys/posix"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#+private
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#+private
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
import "core:slice"
|
||||
@@ -1,4 +1,4 @@
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "core:io"
|
||||
import "core:time"
|
||||
@@ -538,6 +538,11 @@ is_directory :: proc(path: string) -> bool {
|
||||
/*
|
||||
`copy_file` copies a file from `src_path` to `dst_path` and returns an error if any was encountered.
|
||||
*/
|
||||
@(require_results)
|
||||
is_tty :: proc "contextless" (f: ^File) -> bool {
|
||||
return _is_tty(f)
|
||||
}
|
||||
|
||||
copy_file :: proc(dst_path, src_path: string) -> Error {
|
||||
when #defined(_copy_file_native) {
|
||||
return _copy_file_native(dst_path, src_path)
|
||||
@@ -562,4 +567,4 @@ _copy_file :: proc(dst_path, src_path: string) -> Error {
|
||||
|
||||
_, err := io.copy(to_writer(dst), to_reader(src))
|
||||
return err
|
||||
}
|
||||
}
|
||||
110
core/os/file_js.odin
Normal file
110
core/os/file_js.odin
Normal file
@@ -0,0 +1,110 @@
|
||||
#+build js wasm32, js wasm64p32
|
||||
#+private
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
import "core:io"
|
||||
import "core:time"
|
||||
|
||||
File_Impl :: distinct rawptr
|
||||
|
||||
_open :: proc(name: string, flags: File_Flags, perm: Permissions) -> (f: ^File, err: Error) {
|
||||
return nil, .Unsupported
|
||||
}
|
||||
|
||||
_new_file :: proc(handle: uintptr, name: string, allocator: runtime.Allocator) -> (f: ^File, err: Error) {
|
||||
return nil, .Unsupported
|
||||
}
|
||||
|
||||
_clone :: proc(f: ^File) -> (clone: ^File, err: Error) {
|
||||
return nil, .Unsupported
|
||||
}
|
||||
|
||||
_close :: proc(f: ^File_Impl) -> (err: Error) {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_fd :: proc(f: ^File) -> uintptr {
|
||||
return 0
|
||||
}
|
||||
|
||||
_is_tty :: proc "contextless" (f: ^File) -> bool {
|
||||
return true
|
||||
}
|
||||
|
||||
_name :: proc(f: ^File) -> string {
|
||||
return ""
|
||||
}
|
||||
|
||||
_sync :: proc(f: ^File) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_truncate :: proc(f: ^File, size: i64) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_remove :: proc(name: string) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_rename :: proc(old_path, new_path: string) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_link :: proc(old_name, new_name: string) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_symlink :: proc(old_name, new_name: string) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, err: Error) {
|
||||
return "", .Unsupported
|
||||
}
|
||||
|
||||
_chdir :: proc(name: string) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_fchdir :: proc(f: ^File) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_fchmod :: proc(f: ^File, mode: Permissions) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_chmod :: proc(name: string, mode: Permissions) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_fchown :: proc(f: ^File, uid, gid: int) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_chown :: proc(name: string, uid, gid: int) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_lchown :: proc(name: string, uid, gid: int) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_exists :: proc(path: string) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
|
||||
return 0, .Empty
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
#+private
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
import "core:io"
|
||||
import "core:time"
|
||||
import "core:sync"
|
||||
import "core:sys/linux"
|
||||
import "core:sys/posix"
|
||||
|
||||
// Most implementations will EINVAL at some point when doing big writes.
|
||||
// In practice a read/write call would probably never read/write these big buffers all at once,
|
||||
@@ -174,6 +175,17 @@ _fd :: proc(f: ^File) -> uintptr {
|
||||
return uintptr(impl.fd)
|
||||
}
|
||||
|
||||
_is_tty :: proc "contextless" (f: ^File) -> bool {
|
||||
if f == nil || f.impl == nil {
|
||||
return false
|
||||
}
|
||||
impl := (^File_Impl)(f.impl)
|
||||
|
||||
// TODO: Replace `posix.isatty` with `tcgetattr(fd, &termios) == 0`
|
||||
is_tty := posix.isatty(posix.FD(impl.fd))
|
||||
return bool(is_tty)
|
||||
}
|
||||
|
||||
_name :: proc(f: ^File) -> string {
|
||||
return (^File_Impl)(f.impl).name if f != nil && f.impl != nil else ""
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#+private
|
||||
#+build darwin, netbsd, freebsd, openbsd
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@@ -161,6 +161,13 @@ __fd :: proc(f: ^File) -> posix.FD {
|
||||
return -1
|
||||
}
|
||||
|
||||
_is_tty :: proc "contextless" (f: ^File) -> bool {
|
||||
context = runtime.default_context()
|
||||
fd := _fd(f)
|
||||
is_tty := posix.isatty(posix.FD(fd))
|
||||
return bool(is_tty)
|
||||
}
|
||||
|
||||
_name :: proc(f: ^File) -> string {
|
||||
if f != nil && f.impl != nil {
|
||||
return (^File_Impl)(f.impl).name
|
||||
@@ -1,5 +1,5 @@
|
||||
#+private
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@@ -43,4 +43,4 @@ _copy_file_native :: proc(dst_path, src_path: string) -> (err: Error) {
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#+private
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#+private
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#+private
|
||||
#+build openbsd
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
@@ -1,4 +1,4 @@
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
import "core:strconv"
|
||||
@@ -156,9 +156,12 @@ read_entire_file :: proc{
|
||||
*/
|
||||
@(require_results)
|
||||
read_entire_file_from_path :: proc(name: string, allocator: runtime.Allocator, loc := #caller_location) -> (data: []byte, err: Error) {
|
||||
f := open(name) or_return
|
||||
f, ferr := open(name)
|
||||
if ferr != nil {
|
||||
return nil, ferr
|
||||
}
|
||||
defer close(f)
|
||||
return read_entire_file_from_file(f, allocator, loc)
|
||||
return read_entire_file_from_file(f=f, allocator=allocator, loc=loc)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1,5 +1,6 @@
|
||||
#+feature global-context
|
||||
#+private
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@@ -40,7 +41,6 @@ init_std_files :: proc "contextless" () {
|
||||
data = impl,
|
||||
procedure = _file_stream_proc,
|
||||
}
|
||||
impl.file.fstat = _fstat
|
||||
return &impl.file
|
||||
}
|
||||
|
||||
@@ -221,7 +221,6 @@ _new_file :: proc(handle: uintptr, name: string, allocator: runtime.Allocator) -
|
||||
data = impl,
|
||||
procedure = _file_stream_proc,
|
||||
}
|
||||
impl.file.fstat = _fstat
|
||||
|
||||
return &impl.file, nil
|
||||
}
|
||||
@@ -273,6 +272,10 @@ __fd :: proc(f: ^File) -> wasi.fd_t {
|
||||
return -1
|
||||
}
|
||||
|
||||
_is_tty :: proc "contextless" (f: ^File) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
_name :: proc(f: ^File) -> string {
|
||||
if f != nil && f.impl != nil {
|
||||
return (^File_Impl)(f.impl).name
|
||||
@@ -429,7 +432,7 @@ _exists :: proc(path: string) -> bool {
|
||||
return true
|
||||
}
|
||||
|
||||
_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
|
||||
_file_stream_proc :: proc(stream_data: rawptr, mode: File_Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From, allocator: runtime.Allocator) -> (n: i64, err: Error) {
|
||||
f := (^File_Impl)(stream_data)
|
||||
fd := f.fd
|
||||
|
||||
@@ -557,6 +560,10 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
|
||||
case .Query:
|
||||
return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Destroy, .Query})
|
||||
|
||||
case .Fstat:
|
||||
err = file_stream_fstat_utility(f, p, allocator)
|
||||
return
|
||||
|
||||
case:
|
||||
return 0, .Unsupported
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#+private
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@@ -246,6 +246,11 @@ _fd :: proc "contextless" (f: ^File) -> uintptr {
|
||||
return uintptr((^File_Impl)(f.impl).fd)
|
||||
}
|
||||
|
||||
_is_tty :: proc "contextless" (f: ^File) -> bool {
|
||||
fd := _fd(f)
|
||||
return win32.GetFileType(win32.HANDLE(fd)) == win32.FILE_TYPE_CHAR
|
||||
}
|
||||
|
||||
_destroy :: proc(f: ^File_Impl) -> Error {
|
||||
if f == nil {
|
||||
return nil
|
||||
@@ -1,4 +1,4 @@
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
7
core/os/heap_js.odin
Normal file
7
core/os/heap_js.odin
Normal file
@@ -0,0 +1,7 @@
|
||||
#+build js wasm32, js wasm64p32
|
||||
#+private
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
_heap_allocator_proc :: runtime.wasm_allocator_proc
|
||||
@@ -1,5 +1,5 @@
|
||||
#+private
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#+private
|
||||
#+build darwin, netbsd, freebsd, openbsd
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#+private
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#+private
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "core:mem"
|
||||
import win32 "core:sys/windows"
|
||||
@@ -1,5 +1,5 @@
|
||||
#+private
|
||||
package os2
|
||||
package os
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
@@ -1,5 +1,5 @@
|
||||
#+build darwin, linux, netbsd, freebsd, openbsd, haiku
|
||||
package os
|
||||
package os_old
|
||||
|
||||
import "core:strings"
|
||||
|
||||
114
core/os/old/dir_windows.odin
Normal file
114
core/os/old/dir_windows.odin
Normal file
@@ -0,0 +1,114 @@
|
||||
package os_old
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:strings"
|
||||
import "base:runtime"
|
||||
|
||||
@(require_results)
|
||||
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Error) {
|
||||
@(require_results)
|
||||
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) {
|
||||
// Ignore "." and ".."
|
||||
if d.cFileName[0] == '.' && d.cFileName[1] == 0 {
|
||||
return
|
||||
}
|
||||
if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 {
|
||||
return
|
||||
}
|
||||
path := strings.concatenate({base_path, `\`, win32.utf16_to_utf8(d.cFileName[:]) or_else ""})
|
||||
fi.fullpath = path
|
||||
fi.name = basename(path)
|
||||
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
||||
|
||||
if d.dwFileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
|
||||
fi.mode |= 0o444
|
||||
} else {
|
||||
fi.mode |= 0o666
|
||||
}
|
||||
|
||||
is_sym := false
|
||||
if d.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_Point == 0 {
|
||||
is_sym = false
|
||||
} else {
|
||||
is_sym = d.dwReserved0 == win32.IO_REPARSE_TAG_SYMLINK || d.dwReserved0 == win32.IO_REPARSE_TAG_MOUNT_POINT
|
||||
}
|
||||
|
||||
if is_sym {
|
||||
fi.mode |= File_Mode_Sym_Link
|
||||
} else {
|
||||
if d.dwFileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
fi.mode |= 0o111 | File_Mode_Dir
|
||||
}
|
||||
|
||||
// fi.mode |= file_type_mode(h);
|
||||
}
|
||||
|
||||
windows_set_file_info_times(&fi, d)
|
||||
|
||||
fi.is_dir = fi.mode & File_Mode_Dir != 0
|
||||
return
|
||||
}
|
||||
|
||||
if fd == 0 {
|
||||
return nil, ERROR_INVALID_HANDLE
|
||||
}
|
||||
|
||||
context.allocator = allocator
|
||||
|
||||
h := win32.HANDLE(fd)
|
||||
|
||||
dir_fi, _ := file_info_from_get_file_information_by_handle("", h)
|
||||
if !dir_fi.is_dir {
|
||||
return nil, .Not_Dir
|
||||
}
|
||||
|
||||
n := n
|
||||
size := n
|
||||
if n <= 0 {
|
||||
n = -1
|
||||
size = 100
|
||||
}
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||
|
||||
wpath := cleanpath_from_handle_u16(fd, context.temp_allocator) or_return
|
||||
if len(wpath) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
dfi := make([dynamic]File_Info, 0, size) or_return
|
||||
|
||||
wpath_search := make([]u16, len(wpath)+3, context.temp_allocator) or_return
|
||||
copy(wpath_search, wpath)
|
||||
wpath_search[len(wpath)+0] = '\\'
|
||||
wpath_search[len(wpath)+1] = '*'
|
||||
wpath_search[len(wpath)+2] = 0
|
||||
|
||||
path := cleanpath_from_buf(wpath)
|
||||
defer delete(path)
|
||||
|
||||
find_data := &win32.WIN32_FIND_DATAW{}
|
||||
find_handle := win32.FindFirstFileW(cstring16(raw_data(wpath_search)), find_data)
|
||||
if find_handle == win32.INVALID_HANDLE_VALUE {
|
||||
err = get_last_error()
|
||||
return dfi[:], err
|
||||
}
|
||||
defer win32.FindClose(find_handle)
|
||||
for n != 0 {
|
||||
fi: File_Info
|
||||
fi = find_data_to_file_info(path, find_data)
|
||||
if fi.name != "" {
|
||||
append(&dfi, fi)
|
||||
n -= 1
|
||||
}
|
||||
|
||||
if !win32.FindNextFileW(find_handle, find_data) {
|
||||
e := get_last_error()
|
||||
if e == ERROR_NO_MORE_FILES {
|
||||
break
|
||||
}
|
||||
return dfi[:], e
|
||||
}
|
||||
}
|
||||
|
||||
return dfi[:], nil
|
||||
}
|
||||
140
core/os/old/env_windows.odin
Normal file
140
core/os/old/env_windows.odin
Normal file
@@ -0,0 +1,140 @@
|
||||
package os_old
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "base:runtime"
|
||||
|
||||
// lookup_env gets the value of the environment variable named by the key
|
||||
// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true
|
||||
// Otherwise the returned value will be empty and the boolean will be false
|
||||
// NOTE: the value will be allocated with the supplied allocator
|
||||
@(require_results)
|
||||
lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
if key == "" {
|
||||
return
|
||||
}
|
||||
wkey := win32.utf8_to_wstring(key)
|
||||
n := win32.GetEnvironmentVariableW(wkey, nil, 0)
|
||||
if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND {
|
||||
return "", false
|
||||
}
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||
|
||||
b, _ := make([dynamic]u16, n, context.temp_allocator)
|
||||
n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
|
||||
if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND {
|
||||
return "", false
|
||||
}
|
||||
value, _ = win32.utf16_to_utf8(b[:n], allocator)
|
||||
found = true
|
||||
return
|
||||
}
|
||||
|
||||
// This version of `lookup_env` doesn't allocate and instead requires the user to provide a buffer.
|
||||
// Note that it is limited to environment names and values of 512 utf-16 values each
|
||||
// due to the necessary utf-8 <> utf-16 conversion.
|
||||
@(require_results)
|
||||
lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
|
||||
key_buf: [513]u16
|
||||
wkey := win32.utf8_to_wstring(key_buf[:], key)
|
||||
if wkey == nil {
|
||||
return "", .Buffer_Full
|
||||
}
|
||||
|
||||
n2 := win32.GetEnvironmentVariableW(wkey, nil, 0)
|
||||
if n2 == 0 {
|
||||
return "", .Env_Var_Not_Found
|
||||
}
|
||||
|
||||
val_buf: [513]u16
|
||||
n2 = win32.GetEnvironmentVariableW(wkey, raw_data(val_buf[:]), u32(len(val_buf[:])))
|
||||
if n2 == 0 {
|
||||
return "", .Env_Var_Not_Found
|
||||
} else if int(n2) > len(buf) {
|
||||
return "", .Buffer_Full
|
||||
}
|
||||
|
||||
value = win32.utf16_to_utf8(buf, val_buf[:n2])
|
||||
|
||||
return value, nil
|
||||
}
|
||||
lookup_env :: proc{lookup_env_alloc, lookup_env_buffer}
|
||||
|
||||
// get_env retrieves the value of the environment variable named by the key
|
||||
// It returns the value, which will be empty if the variable is not present
|
||||
// To distinguish between an empty value and an unset value, use lookup_env
|
||||
// NOTE: the value will be allocated with the supplied allocator
|
||||
@(require_results)
|
||||
get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_env_buf :: proc(buf: []u8, key: string) -> (value: string) {
|
||||
value, _ = lookup_env(buf, key)
|
||||
return
|
||||
}
|
||||
get_env :: proc{get_env_alloc, get_env_buf}
|
||||
|
||||
|
||||
// set_env sets the value of the environment variable named by the key
|
||||
set_env :: proc(key, value: string) -> Error {
|
||||
k := win32.utf8_to_wstring(key)
|
||||
v := win32.utf8_to_wstring(value)
|
||||
|
||||
if !win32.SetEnvironmentVariableW(k, v) {
|
||||
return get_last_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// unset_env unsets a single environment variable
|
||||
unset_env :: proc(key: string) -> Error {
|
||||
k := win32.utf8_to_wstring(key)
|
||||
if !win32.SetEnvironmentVariableW(k, nil) {
|
||||
return get_last_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// environ returns a copy of strings representing the environment, in the form "key=value"
|
||||
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator
|
||||
@(require_results)
|
||||
environ :: proc(allocator := context.allocator) -> []string {
|
||||
envs := ([^]win32.WCHAR)(win32.GetEnvironmentStringsW())
|
||||
if envs == nil {
|
||||
return nil
|
||||
}
|
||||
defer win32.FreeEnvironmentStringsW(envs)
|
||||
|
||||
r, err := make([dynamic]string, 0, 50, allocator)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for from, i := 0, 0; true; i += 1 {
|
||||
if c := envs[i]; c == 0 {
|
||||
if i <= from {
|
||||
break
|
||||
}
|
||||
append(&r, win32.utf16_to_utf8(envs[from:i], allocator) or_else "")
|
||||
from = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
return r[:]
|
||||
}
|
||||
|
||||
|
||||
// clear_env deletes all environment variables
|
||||
clear_env :: proc() {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
envs := environ(context.temp_allocator)
|
||||
for env in envs {
|
||||
for j in 1..<len(env) {
|
||||
if env[j] == '=' {
|
||||
unset_env(env[0:j])
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
318
core/os/old/errors.odin
Normal file
318
core/os/old/errors.odin
Normal file
@@ -0,0 +1,318 @@
|
||||
package os_old
|
||||
|
||||
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
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// Cross-platform `OS` interactions like file `I/O`.
|
||||
package os
|
||||
package os_old
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
@@ -1,4 +1,4 @@
|
||||
package os
|
||||
package os_old
|
||||
|
||||
foreign import dl "system:dl"
|
||||
foreign import libc "system:System"
|
||||
@@ -598,7 +598,7 @@ foreign libc {
|
||||
@(link_name="fstat64") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> c.int ---
|
||||
@(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t ---
|
||||
@(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int ---
|
||||
@(link_name="fsync") _unix_fsync :: proc(handle: Handle) -> c.int ---
|
||||
@(link_name="fsync") _unix_fsync :: proc(handle: Handle) -> c.int ---
|
||||
@(link_name="dup") _unix_dup :: proc(handle: Handle) -> Handle ---
|
||||
|
||||
@(link_name="fdopendir$INODE64") _unix_fdopendir_amd64 :: proc(fd: Handle) -> Dir ---
|
||||
@@ -1,4 +1,4 @@
|
||||
package os
|
||||
package os_old
|
||||
|
||||
import "core:sys/es"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package os
|
||||
package os_old
|
||||
|
||||
foreign import dl "system:dl"
|
||||
foreign import libc "system:c"
|
||||
4
core/os/old/os_freestanding.odin
Normal file
4
core/os/old/os_freestanding.odin
Normal file
@@ -0,0 +1,4 @@
|
||||
#+build freestanding
|
||||
package os_old
|
||||
|
||||
#panic("package os_old does not support a freestanding target")
|
||||
@@ -1,4 +1,4 @@
|
||||
package os
|
||||
package os_old
|
||||
|
||||
foreign import lib "system:c"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#+build js
|
||||
package os
|
||||
package os_old
|
||||
|
||||
foreign import "odin_env"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package os
|
||||
package os_old
|
||||
|
||||
foreign import dl "system:dl"
|
||||
foreign import libc "system:c"
|
||||
@@ -7,19 +7,8 @@ import "base:runtime"
|
||||
import "core:strings"
|
||||
import "core:c"
|
||||
import "core:strconv"
|
||||
|
||||
// NOTE(flysand): For compatibility we'll make core:os package
|
||||
// depend on the old (scheduled for removal) linux package.
|
||||
// Seeing that there are plans for os2, I'm imagining that *that*
|
||||
// package should inherit the new sys functionality.
|
||||
// The reasons for these are as follows:
|
||||
// 1. It's very hard to update this package without breaking *a lot* of code.
|
||||
// 2. os2 is not stable anyways, so we can break compatibility all we want
|
||||
// It might be weird to bring up compatibility when Odin in it's nature isn't
|
||||
// all that about compatibility. But we don't want to push experimental changes
|
||||
// and have people's code break while it's still work in progress.
|
||||
import unix "core:sys/unix"
|
||||
import linux "core:sys/linux"
|
||||
import "core:sys/unix"
|
||||
import "core:sys/linux"
|
||||
|
||||
Handle :: distinct i32
|
||||
Pid :: distinct i32
|
||||
@@ -1,4 +1,4 @@
|
||||
package os
|
||||
package os_old
|
||||
|
||||
foreign import dl "system:dl"
|
||||
foreign import libc "system:c"
|
||||
@@ -1,4 +1,4 @@
|
||||
package os
|
||||
package os_old
|
||||
|
||||
foreign import libc "system:c"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package os
|
||||
package os_old
|
||||
|
||||
import "core:sys/wasm/wasi"
|
||||
import "base:runtime"
|
||||
@@ -1,5 +1,5 @@
|
||||
#+build windows
|
||||
package os
|
||||
package os_old
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "base:runtime"
|
||||
33
core/os/old/stat.odin
Normal file
33
core/os/old/stat.odin
Normal file
@@ -0,0 +1,33 @@
|
||||
package os_old
|
||||
|
||||
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)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user