mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-30 03:10:06 +00:00
Merge branch 'master' of github.com:odin-lang/Odin into posix-linux
This commit is contained in:
@@ -11,7 +11,7 @@ Write a UUID in the 8-4-4-4-12 format.
|
||||
This procedure performs error checking with every byte written.
|
||||
|
||||
If you can guarantee beforehand that your stream has enough space to hold the
|
||||
UUID (32 bytes), then it is better to use `unsafe_write` instead as that will
|
||||
UUID (36 bytes), then it is better to use `unsafe_write` instead as that will
|
||||
be faster.
|
||||
|
||||
Inputs:
|
||||
@@ -22,7 +22,7 @@ Returns:
|
||||
- error: An `io` error, if one occurred, otherwise `nil`.
|
||||
*/
|
||||
write :: proc(w: io.Writer, id: Identifier) -> (error: io.Error) #no_bounds_check {
|
||||
write_octet :: proc (w: io.Writer, octet: u8) -> io.Error #no_bounds_check {
|
||||
write_octet :: proc(w: io.Writer, octet: u8) -> io.Error #no_bounds_check {
|
||||
high_nibble := octet >> 4
|
||||
low_nibble := octet & 0xF
|
||||
|
||||
@@ -31,15 +31,15 @@ write :: proc(w: io.Writer, id: Identifier) -> (error: io.Error) #no_bounds_chec
|
||||
return nil
|
||||
}
|
||||
|
||||
for index in 0 ..< 4 { write_octet(w, id[index]) or_return }
|
||||
for index in 0 ..< 4 {write_octet(w, id[index]) or_return}
|
||||
io.write_byte(w, '-') or_return
|
||||
for index in 4 ..< 6 { write_octet(w, id[index]) or_return }
|
||||
for index in 4 ..< 6 {write_octet(w, id[index]) or_return}
|
||||
io.write_byte(w, '-') or_return
|
||||
for index in 6 ..< 8 { write_octet(w, id[index]) or_return }
|
||||
for index in 6 ..< 8 {write_octet(w, id[index]) or_return}
|
||||
io.write_byte(w, '-') or_return
|
||||
for index in 8 ..< 10 { write_octet(w, id[index]) or_return }
|
||||
for index in 8 ..< 10 {write_octet(w, id[index]) or_return}
|
||||
io.write_byte(w, '-') or_return
|
||||
for index in 10 ..< 16 { write_octet(w, id[index]) or_return }
|
||||
for index in 10 ..< 16 {write_octet(w, id[index]) or_return}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -54,7 +54,7 @@ Inputs:
|
||||
- id: The identifier to convert.
|
||||
*/
|
||||
unsafe_write :: proc(w: io.Writer, id: Identifier) #no_bounds_check {
|
||||
write_octet :: proc (w: io.Writer, octet: u8) #no_bounds_check {
|
||||
write_octet :: proc(w: io.Writer, octet: u8) #no_bounds_check {
|
||||
high_nibble := octet >> 4
|
||||
low_nibble := octet & 0xF
|
||||
|
||||
@@ -62,15 +62,15 @@ unsafe_write :: proc(w: io.Writer, id: Identifier) #no_bounds_check {
|
||||
io.write_byte(w, strconv.digits[low_nibble])
|
||||
}
|
||||
|
||||
for index in 0 ..< 4 { write_octet(w, id[index]) }
|
||||
for index in 0 ..< 4 {write_octet(w, id[index])}
|
||||
io.write_byte(w, '-')
|
||||
for index in 4 ..< 6 { write_octet(w, id[index]) }
|
||||
for index in 4 ..< 6 {write_octet(w, id[index])}
|
||||
io.write_byte(w, '-')
|
||||
for index in 6 ..< 8 { write_octet(w, id[index]) }
|
||||
for index in 6 ..< 8 {write_octet(w, id[index])}
|
||||
io.write_byte(w, '-')
|
||||
for index in 8 ..< 10 { write_octet(w, id[index]) }
|
||||
for index in 8 ..< 10 {write_octet(w, id[index])}
|
||||
io.write_byte(w, '-')
|
||||
for index in 10 ..< 16 { write_octet(w, id[index]) }
|
||||
for index in 10 ..< 16 {write_octet(w, id[index])}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -106,7 +106,7 @@ Convert a UUID to a string in the 8-4-4-4-12 format.
|
||||
|
||||
Inputs:
|
||||
- id: The identifier to convert.
|
||||
- buffer: A byte buffer to store the result. Must be at least 32 bytes large.
|
||||
- buffer: A byte buffer to store the result. Must be at least 36 bytes large.
|
||||
- loc: The caller location for debugging purposes (default: #caller_location)
|
||||
|
||||
Returns:
|
||||
@@ -119,7 +119,11 @@ to_string_buffer :: proc(
|
||||
) -> (
|
||||
str: string,
|
||||
) {
|
||||
assert(len(buffer) >= EXPECTED_LENGTH, "The buffer provided is not at least 32 bytes large.", loc)
|
||||
assert(
|
||||
len(buffer) >= EXPECTED_LENGTH,
|
||||
"The buffer provided is not at least 36 bytes large.",
|
||||
loc,
|
||||
)
|
||||
builder := strings.builder_from_bytes(buffer)
|
||||
unsafe_write(strings.to_writer(&builder), id)
|
||||
return strings.to_string(builder)
|
||||
@@ -129,3 +133,4 @@ to_string :: proc {
|
||||
to_string_allocated,
|
||||
to_string_buffer,
|
||||
}
|
||||
|
||||
|
||||
@@ -19,15 +19,15 @@ xxh_u64 :: u64
|
||||
XXH64_DEFAULT_SEED :: XXH64_hash(0)
|
||||
|
||||
XXH64_state :: struct {
|
||||
total_len: XXH64_hash, /*!< Total length hashed. This is always 64-bit. */
|
||||
v1: XXH64_hash, /*!< First accumulator lane */
|
||||
v2: XXH64_hash, /*!< Second accumulator lane */
|
||||
v3: XXH64_hash, /*!< Third accumulator lane */
|
||||
v4: XXH64_hash, /*!< Fourth accumulator lane */
|
||||
mem64: [4]XXH64_hash, /*!< Internal buffer for partial reads. Treated as unsigned char[32]. */
|
||||
memsize: XXH32_hash, /*!< Amount of data in @ref mem64 */
|
||||
reserved32: XXH32_hash, /*!< Reserved field, needed for padding anyways*/
|
||||
reserved64: XXH64_hash, /*!< Reserved field. Do not read or write to it, it may be removed. */
|
||||
total_len: XXH64_hash, /*!< Total length hashed. This is always 64-bit. */
|
||||
v1: XXH64_hash, /*!< First accumulator lane */
|
||||
v2: XXH64_hash, /*!< Second accumulator lane */
|
||||
v3: XXH64_hash, /*!< Third accumulator lane */
|
||||
v4: XXH64_hash, /*!< Fourth accumulator lane */
|
||||
mem64: [4]XXH64_hash, /*!< Internal buffer for partial reads. Treated as unsigned char[32]. */
|
||||
memsize: XXH32_hash, /*!< Amount of data in @ref mem64 */
|
||||
reserved32: XXH32_hash, /*!< Reserved field, needed for padding anyways*/
|
||||
reserved64: XXH64_hash, /*!< Reserved field. Do not read or write to it, it may be removed. */
|
||||
}
|
||||
|
||||
XXH64_canonical :: struct {
|
||||
|
||||
@@ -1393,6 +1393,7 @@ expand_grayscale :: proc(img: ^Image, allocator := context.allocator) -> (ok: bo
|
||||
for p in inp {
|
||||
out[0].rgb = p.r // Gray component.
|
||||
out[0].a = p.g // Alpha component.
|
||||
out = out[1:]
|
||||
}
|
||||
|
||||
case:
|
||||
@@ -1417,6 +1418,7 @@ expand_grayscale :: proc(img: ^Image, allocator := context.allocator) -> (ok: bo
|
||||
for p in inp {
|
||||
out[0].rgb = p.r // Gray component.
|
||||
out[0].a = p.g // Alpha component.
|
||||
out = out[1:]
|
||||
}
|
||||
|
||||
case:
|
||||
|
||||
@@ -195,7 +195,7 @@ Error_String :: #sparse[Error]string{
|
||||
}
|
||||
|
||||
Primality_Flag :: enum u8 {
|
||||
Blum_Blum_Shub = 0, // Make prime congruent to 3 mod 4
|
||||
Blum_Blum_Shub = 0, // Make prime congruent to 3 mod 4
|
||||
Safe = 1, // Make sure (p-1)/2 is prime as well (implies .Blum_Blum_Shub)
|
||||
Second_MSB_On = 3, // Make the 2nd highest bit one
|
||||
}
|
||||
|
||||
+2
-2
@@ -178,11 +178,11 @@ make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocato
|
||||
}
|
||||
@(require_results)
|
||||
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
|
||||
return runtime.make_dynamic_array(T, len, allocator, loc)
|
||||
return runtime.make_dynamic_array_len_cap(T, len, len, allocator, loc)
|
||||
}
|
||||
@(require_results)
|
||||
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) {
|
||||
return runtime.make_dynamic_array(T, len, cap, allocator, loc)
|
||||
return runtime.make_dynamic_array_len_cap(T, len, cap, allocator, loc)
|
||||
}
|
||||
@(require_results)
|
||||
make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = 1<<runtime.MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> (m: T, err: Allocator_Error) {
|
||||
|
||||
+18
-14
@@ -128,33 +128,37 @@ _get_dns_records_os :: proc(hostname: string, type: DNS_Record_Type, allocator :
|
||||
append(&recs, record)
|
||||
|
||||
case .SRV:
|
||||
target := strings.clone(string(r.Data.SRV.pNameTarget)) // The target hostname/address that the service can be found on
|
||||
priority := int(r.Data.SRV.wPriority)
|
||||
weight := int(r.Data.SRV.wWeight)
|
||||
port := int(r.Data.SRV.wPort)
|
||||
|
||||
// NOTE(tetra): Srv record name should be of the form '_servicename._protocol.hostname'
|
||||
// The record name is the name of the record.
|
||||
// Not to be confused with the _target_ of the record, which is--in combination with the port--what we're looking up
|
||||
// by making this request in the first place.
|
||||
|
||||
// NOTE(Jeroen): Service Name and Protocol Name can probably just be string slices into the record name.
|
||||
// It's already cloned, after all. I wouldn't put them on the temp allocator like this.
|
||||
service_name, protocol_name: string
|
||||
|
||||
parts := strings.split_n(base_record.record_name, ".", 3, context.temp_allocator)
|
||||
if len(parts) != 3 {
|
||||
s := base_record.record_name
|
||||
i := strings.index_byte(s, '.')
|
||||
if i > -1 {
|
||||
service_name = s[:i]
|
||||
s = s[len(service_name) + 1:]
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
i = strings.index_byte(s, '.')
|
||||
if i > -1 {
|
||||
protocol_name = s[:i]
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
service_name, protocol_name := parts[0], parts[1]
|
||||
|
||||
append(&recs, DNS_Record_SRV {
|
||||
base = base_record,
|
||||
target = target,
|
||||
port = port,
|
||||
target = strings.clone(string(r.Data.SRV.pNameTarget)), // The target hostname/address that the service can be found on
|
||||
port = int(r.Data.SRV.wPort),
|
||||
service_name = service_name,
|
||||
protocol_name = protocol_name,
|
||||
priority = priority,
|
||||
weight = weight,
|
||||
priority = int(r.Data.SRV.wPriority),
|
||||
weight = int(r.Data.SRV.wWeight),
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@@ -61,3 +61,8 @@ TEMP_ALLOCATOR_GUARD :: #force_inline proc(loc := #caller_location) -> (runtime.
|
||||
global_default_temp_allocator_index = (global_default_temp_allocator_index+1)%MAX_TEMP_ARENA_COUNT
|
||||
return tmp, loc
|
||||
}
|
||||
|
||||
@(init, private)
|
||||
init_thread_local_cleaner :: proc() {
|
||||
runtime.add_thread_local_cleaner(temp_allocator_fini)
|
||||
}
|
||||
|
||||
@@ -125,9 +125,6 @@ read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (d
|
||||
has_size = true
|
||||
size = int(size64)
|
||||
}
|
||||
} else if serr != .No_Size {
|
||||
err = serr
|
||||
return
|
||||
}
|
||||
|
||||
if has_size && size > 0 {
|
||||
|
||||
+14
-13
@@ -166,15 +166,15 @@ Process_Info :: struct {
|
||||
|
||||
This procedure obtains an information, specified by `selection` parameter of
|
||||
a process given by `pid`.
|
||||
|
||||
Use `free_process_info` to free the memory allocated by this procedure. In
|
||||
case the function returns an error it may only have been an error for one part
|
||||
of the information and you would still need to call it to free the other parts.
|
||||
|
||||
Use `free_process_info` to free the memory allocated by this procedure. The
|
||||
`free_process_info` procedure needs to be called, even if this procedure
|
||||
returned an error, as some of the fields may have been allocated.
|
||||
|
||||
**Note**: The resulting information may or may contain the fields specified
|
||||
by the `selection` parameter. Always check whether the returned
|
||||
`Process_Info` struct has the required fields before checking the error code
|
||||
returned by this function.
|
||||
returned by this procedure.
|
||||
*/
|
||||
@(require_results)
|
||||
process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
|
||||
@@ -188,14 +188,14 @@ process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator:
|
||||
about a process that has been opened by the application, specified in
|
||||
the `process` parameter.
|
||||
|
||||
Use `free_process_info` to free the memory allocated by this procedure. In
|
||||
case the function returns an error it may only have been an error for one part
|
||||
of the information and you would still need to call it to free the other parts.
|
||||
Use `free_process_info` to free the memory allocated by this procedure. The
|
||||
`free_process_info` procedure needs to be called, even if this procedure
|
||||
returned an error, as some of the fields may have been allocated.
|
||||
|
||||
**Note**: The resulting information may or may contain the fields specified
|
||||
by the `selection` parameter. Always check whether the returned
|
||||
`Process_Info` struct has the required fields before checking the error code
|
||||
returned by this function.
|
||||
returned by this procedure.
|
||||
*/
|
||||
@(require_results)
|
||||
process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
|
||||
@@ -208,14 +208,14 @@ process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields,
|
||||
This procedure obtains the information, specified by `selection` parameter
|
||||
about the currently running process.
|
||||
|
||||
Use `free_process_info` to free the memory allocated by this procedure. In
|
||||
case the function returns an error it may only have been an error for one part
|
||||
of the information and you would still need to call it to free the other parts.
|
||||
Use `free_process_info` to free the memory allocated by this procedure. The
|
||||
`free_process_info` procedure needs to be called, even if this procedure
|
||||
returned an error, as some of the fields may have been allocated.
|
||||
|
||||
**Note**: The resulting information may or may contain the fields specified
|
||||
by the `selection` parameter. Always check whether the returned
|
||||
`Process_Info` struct has the required fields before checking the error code
|
||||
returned by this function.
|
||||
returned by this procedure.
|
||||
*/
|
||||
@(require_results)
|
||||
current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
|
||||
@@ -305,6 +305,7 @@ Process_Desc :: struct {
|
||||
// A slice of strings, each having the format `KEY=VALUE` representing the
|
||||
// full environment that the child process will receive.
|
||||
// In case this slice is `nil`, the current process' environment is used.
|
||||
// NOTE(laytan): maybe should be `Maybe([]string)` so you can do `nil` == current env, empty == empty/no env.
|
||||
env: []string,
|
||||
// The `stderr` handle to give to the child process. It can be either a file
|
||||
// or a writeable end of a pipe. Passing `nil` will shut down the process'
|
||||
|
||||
@@ -490,7 +490,6 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
if errno = linux.pipe2(&child_pipe_fds, {.CLOEXEC}); errno != .NONE {
|
||||
return process, _get_platform_error(errno)
|
||||
}
|
||||
defer linux.close(child_pipe_fds[WRITE])
|
||||
defer linux.close(child_pipe_fds[READ])
|
||||
|
||||
|
||||
@@ -508,6 +507,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
//
|
||||
pid: linux.Pid
|
||||
if pid, errno = linux.fork(); errno != .NONE {
|
||||
linux.close(child_pipe_fds[WRITE])
|
||||
return process, _get_platform_error(errno)
|
||||
}
|
||||
|
||||
@@ -573,25 +573,19 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
|
||||
}
|
||||
|
||||
success_byte: [1]u8
|
||||
linux.write(child_pipe_fds[WRITE], success_byte[:])
|
||||
|
||||
errno = linux.execveat(exe_fd, "", &cargs[0], env, {.AT_EMPTY_PATH})
|
||||
|
||||
// NOTE: we can't tell the parent about this failure because we already wrote the success byte.
|
||||
// So if this happens the user will just see the process failed when they call process_wait.
|
||||
|
||||
assert(errno != nil)
|
||||
intrinsics.trap()
|
||||
write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
|
||||
}
|
||||
|
||||
linux.close(child_pipe_fds[WRITE])
|
||||
|
||||
process.pid = int(pid)
|
||||
|
||||
n: int
|
||||
child_byte: [1]u8
|
||||
errno = .EINTR
|
||||
for errno == .EINTR {
|
||||
n, errno = linux.read(child_pipe_fds[READ], child_byte[:])
|
||||
_, errno = linux.read(child_pipe_fds[READ], child_byte[:])
|
||||
}
|
||||
|
||||
// If the read failed, something weird happened. Do not return the read
|
||||
|
||||
@@ -139,20 +139,22 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
err = _get_platform_error()
|
||||
return
|
||||
}
|
||||
defer posix.close(pipe[WRITE])
|
||||
defer posix.close(pipe[READ])
|
||||
|
||||
if posix.fcntl(pipe[READ], .SETFD, i32(posix.FD_CLOEXEC)) == -1 {
|
||||
posix.close(pipe[WRITE])
|
||||
err = _get_platform_error()
|
||||
return
|
||||
}
|
||||
if posix.fcntl(pipe[WRITE], .SETFD, i32(posix.FD_CLOEXEC)) == -1 {
|
||||
posix.close(pipe[WRITE])
|
||||
err = _get_platform_error()
|
||||
return
|
||||
}
|
||||
|
||||
switch pid := posix.fork(); pid {
|
||||
case -1:
|
||||
posix.close(pipe[WRITE])
|
||||
err = _get_platform_error()
|
||||
return
|
||||
|
||||
@@ -179,25 +181,20 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
if posix.chdir(cwd) != .OK { abort(pipe[WRITE]) }
|
||||
}
|
||||
|
||||
ok := u8(0)
|
||||
posix.write(pipe[WRITE], &ok, 1)
|
||||
|
||||
res := posix.execve(strings.to_cstring(&exe_builder), raw_data(cmd), env)
|
||||
|
||||
// NOTE: we can't tell the parent about this failure because we already wrote the success byte.
|
||||
// So if this happens the user will just see the process failed when they call process_wait.
|
||||
|
||||
assert(res == -1)
|
||||
runtime.trap()
|
||||
abort(pipe[WRITE])
|
||||
|
||||
case:
|
||||
posix.close(pipe[WRITE])
|
||||
|
||||
errno: posix.Errno
|
||||
for {
|
||||
errno_byte: u8
|
||||
switch posix.read(pipe[READ], &errno_byte, 1) {
|
||||
case 1:
|
||||
case 1:
|
||||
errno = posix.Errno(errno_byte)
|
||||
case:
|
||||
case -1:
|
||||
errno = posix.errno()
|
||||
if errno == .EINTR {
|
||||
continue
|
||||
|
||||
+185
-141
@@ -93,34 +93,11 @@ read_memory_as_slice :: proc(h: win32.HANDLE, addr: rawptr, dest: []$T) -> (byte
|
||||
@(private="package")
|
||||
_process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
|
||||
info.pid = pid
|
||||
defer if err != nil {
|
||||
free_process_info(info, allocator)
|
||||
}
|
||||
|
||||
// Data obtained from process snapshots
|
||||
if selection >= {.PPid, .Priority} {
|
||||
entry, entry_err := _process_entry_by_pid(info.pid)
|
||||
if entry_err != nil {
|
||||
err = General_Error.Not_Exist
|
||||
return
|
||||
}
|
||||
if .PPid in selection {
|
||||
info.fields += {.PPid}
|
||||
info.ppid = int(entry.th32ParentProcessID)
|
||||
}
|
||||
if .Priority in selection {
|
||||
info.fields += {.Priority}
|
||||
info.priority = int(entry.pcPriClassBase)
|
||||
}
|
||||
}
|
||||
if .Executable_Path in selection { // snap module
|
||||
info.executable_path = _process_exe_by_pid(pid, allocator) or_return
|
||||
info.fields += {.Executable_Path}
|
||||
}
|
||||
|
||||
// Note(flysand): Open the process handle right away to prevent some race
|
||||
// conditions. Once the handle is open, the process will be kept alive by
|
||||
// the OS.
|
||||
ph := win32.INVALID_HANDLE_VALUE
|
||||
|
||||
if selection >= {.Command_Line, .Environment, .Working_Dir, .Username} { // need process handle
|
||||
if selection >= {.Command_Line, .Environment, .Working_Dir, .Username} {
|
||||
ph = win32.OpenProcess(
|
||||
win32.PROCESS_QUERY_LIMITED_INFORMATION | win32.PROCESS_VM_READ,
|
||||
false,
|
||||
@@ -134,84 +111,15 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
|
||||
defer if ph != win32.INVALID_HANDLE_VALUE {
|
||||
win32.CloseHandle(ph)
|
||||
}
|
||||
|
||||
if selection >= {.Command_Line, .Environment, .Working_Dir} { // need peb
|
||||
process_info_size: u32
|
||||
process_info: win32.PROCESS_BASIC_INFORMATION
|
||||
status := win32.NtQueryInformationProcess(ph, .ProcessBasicInformation, &process_info, size_of(process_info), &process_info_size)
|
||||
if status != 0 {
|
||||
// TODO(flysand): There's probably a mismatch between NTSTATUS and
|
||||
// windows userland error codes, I haven't checked.
|
||||
err = Platform_Error(status)
|
||||
return
|
||||
}
|
||||
if process_info.PebBaseAddress == nil {
|
||||
// Not sure what the error is
|
||||
err = General_Error.Unsupported
|
||||
return
|
||||
}
|
||||
process_peb: win32.PEB
|
||||
|
||||
_ = read_memory_as_struct(ph, process_info.PebBaseAddress, &process_peb) or_return
|
||||
|
||||
process_params: win32.RTL_USER_PROCESS_PARAMETERS
|
||||
_ = read_memory_as_struct(ph, process_peb.ProcessParameters, &process_params) or_return
|
||||
|
||||
if selection >= {.Command_Line, .Command_Args} {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cmdline_w := make([]u16, process_params.CommandLine.Length, temp_allocator()) or_return
|
||||
_ = read_memory_as_slice(ph, process_params.CommandLine.Buffer, cmdline_w) or_return
|
||||
|
||||
if .Command_Line in selection {
|
||||
info.command_line = win32_utf16_to_utf8(cmdline_w, allocator) or_return
|
||||
info.fields += {.Command_Line}
|
||||
}
|
||||
if .Command_Args in selection {
|
||||
info.command_args = _parse_command_line(raw_data(cmdline_w), allocator) or_return
|
||||
info.fields += {.Command_Args}
|
||||
}
|
||||
}
|
||||
if .Environment in selection {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
env_len := process_params.EnvironmentSize / 2
|
||||
envs_w := make([]u16, env_len, temp_allocator()) or_return
|
||||
_ = read_memory_as_slice(ph, process_params.Environment, envs_w) or_return
|
||||
|
||||
info.environment = _parse_environment_block(raw_data(envs_w), allocator) or_return
|
||||
info.fields += {.Environment}
|
||||
}
|
||||
if .Working_Dir in selection {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cwd_w := make([]u16, process_params.CurrentDirectoryPath.Length, temp_allocator()) or_return
|
||||
_ = read_memory_as_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w) or_return
|
||||
|
||||
info.working_dir = win32_utf16_to_utf8(cwd_w, allocator) or_return
|
||||
info.fields += {.Working_Dir}
|
||||
}
|
||||
}
|
||||
|
||||
if .Username in selection {
|
||||
info.username = _get_process_user(ph, allocator) or_return
|
||||
info.fields += {.Username}
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
@(private="package")
|
||||
_process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
|
||||
pid := process.pid
|
||||
info.pid = pid
|
||||
defer if err != nil {
|
||||
free_process_info(info, allocator)
|
||||
}
|
||||
|
||||
// Data obtained from process snapshots
|
||||
if selection >= {.PPid, .Priority} { // snap process
|
||||
snapshot_process: if selection >= {.PPid, .Priority} {
|
||||
entry, entry_err := _process_entry_by_pid(info.pid)
|
||||
if entry_err != nil {
|
||||
err = General_Error.Not_Exist
|
||||
return
|
||||
err = entry_err
|
||||
if entry_err == General_Error.Not_Exist {
|
||||
return
|
||||
} else {
|
||||
break snapshot_process
|
||||
}
|
||||
}
|
||||
if .PPid in selection {
|
||||
info.fields += {.PPid}
|
||||
@@ -222,12 +130,18 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields
|
||||
info.priority = int(entry.pcPriClassBase)
|
||||
}
|
||||
}
|
||||
if .Executable_Path in selection { // snap module
|
||||
info.executable_path = _process_exe_by_pid(pid, allocator) or_return
|
||||
snapshot_modules: if .Executable_Path in selection {
|
||||
exe_path: string
|
||||
exe_path, err = _process_exe_by_pid(pid, allocator)
|
||||
if _, ok := err.(runtime.Allocator_Error); ok {
|
||||
return
|
||||
} else if err != nil {
|
||||
break snapshot_modules
|
||||
}
|
||||
info.executable_path = exe_path
|
||||
info.fields += {.Executable_Path}
|
||||
}
|
||||
ph := win32.HANDLE(process.handle)
|
||||
if selection >= {.Command_Line, .Environment, .Working_Dir} { // need peb
|
||||
read_peb: if selection >= {.Command_Line, .Environment, .Working_Dir} {
|
||||
process_info_size: u32
|
||||
process_info: win32.PROCESS_BASIC_INFORMATION
|
||||
status := win32.NtQueryInformationProcess(ph, .ProcessBasicInformation, &process_info, size_of(process_info), &process_info_size)
|
||||
@@ -235,25 +149,26 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields
|
||||
// TODO(flysand): There's probably a mismatch between NTSTATUS and
|
||||
// windows userland error codes, I haven't checked.
|
||||
err = Platform_Error(status)
|
||||
return
|
||||
break read_peb
|
||||
}
|
||||
if process_info.PebBaseAddress == nil {
|
||||
// Not sure what the error is
|
||||
err = General_Error.Unsupported
|
||||
return
|
||||
}
|
||||
|
||||
assert(process_info.PebBaseAddress != nil)
|
||||
process_peb: win32.PEB
|
||||
_ = read_memory_as_struct(ph, process_info.PebBaseAddress, &process_peb) or_return
|
||||
|
||||
_, err = read_memory_as_struct(ph, process_info.PebBaseAddress, &process_peb)
|
||||
if err != nil {
|
||||
break read_peb
|
||||
}
|
||||
process_params: win32.RTL_USER_PROCESS_PARAMETERS
|
||||
_ = read_memory_as_struct(ph, process_peb.ProcessParameters, &process_params) or_return
|
||||
|
||||
_, err = read_memory_as_struct(ph, process_peb.ProcessParameters, &process_params)
|
||||
if err != nil {
|
||||
break read_peb
|
||||
}
|
||||
if selection >= {.Command_Line, .Command_Args} {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cmdline_w := make([]u16, process_params.CommandLine.Length, temp_allocator()) or_return
|
||||
_ = read_memory_as_slice(ph, process_params.CommandLine.Buffer, cmdline_w) or_return
|
||||
|
||||
_, err = read_memory_as_slice(ph, process_params.CommandLine.Buffer, cmdline_w)
|
||||
if err != nil {
|
||||
break read_peb
|
||||
}
|
||||
if .Command_Line in selection {
|
||||
info.command_line = win32_utf16_to_utf8(cmdline_w, allocator) or_return
|
||||
info.fields += {.Command_Line}
|
||||
@@ -263,28 +178,147 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields
|
||||
info.fields += {.Command_Args}
|
||||
}
|
||||
}
|
||||
|
||||
if .Environment in selection {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
env_len := process_params.EnvironmentSize / 2
|
||||
envs_w := make([]u16, env_len, temp_allocator()) or_return
|
||||
_ = read_memory_as_slice(ph, process_params.Environment, envs_w) or_return
|
||||
|
||||
info.environment = _parse_environment_block(raw_data(envs_w), allocator) or_return
|
||||
_, err = read_memory_as_slice(ph, process_params.Environment, envs_w)
|
||||
if err != nil {
|
||||
break read_peb
|
||||
}
|
||||
info.environment = _parse_environment_block(raw_data(envs_w), allocator) or_return
|
||||
info.fields += {.Environment}
|
||||
}
|
||||
|
||||
if .Working_Dir in selection {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cwd_w := make([]u16, process_params.CurrentDirectoryPath.Length, temp_allocator()) or_return
|
||||
_ = read_memory_as_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w) or_return
|
||||
|
||||
_, err = read_memory_as_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w)
|
||||
if err != nil {
|
||||
break read_peb
|
||||
}
|
||||
info.working_dir = win32_utf16_to_utf8(cwd_w, allocator) or_return
|
||||
info.fields += {.Working_Dir}
|
||||
}
|
||||
}
|
||||
if .Username in selection {
|
||||
info.username = _get_process_user(ph, allocator) or_return
|
||||
read_username: if .Username in selection {
|
||||
username: string
|
||||
username, err = _get_process_user(ph, allocator)
|
||||
if _, ok := err.(runtime.Allocator_Error); ok {
|
||||
return
|
||||
} else if err != nil {
|
||||
break read_username
|
||||
}
|
||||
info.username = username
|
||||
info.fields += {.Username}
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
@(private="package")
|
||||
_process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
|
||||
pid := process.pid
|
||||
info.pid = pid
|
||||
// Data obtained from process snapshots
|
||||
snapshot_process: if selection >= {.PPid, .Priority} {
|
||||
entry, entry_err := _process_entry_by_pid(info.pid)
|
||||
if entry_err != nil {
|
||||
err = entry_err
|
||||
if entry_err == General_Error.Not_Exist {
|
||||
return
|
||||
} else {
|
||||
break snapshot_process
|
||||
}
|
||||
}
|
||||
if .PPid in selection {
|
||||
info.fields += {.PPid}
|
||||
info.ppid = int(entry.th32ParentProcessID)
|
||||
}
|
||||
if .Priority in selection {
|
||||
info.fields += {.Priority}
|
||||
info.priority = int(entry.pcPriClassBase)
|
||||
}
|
||||
}
|
||||
snapshot_module: if .Executable_Path in selection {
|
||||
exe_path: string
|
||||
exe_path, err = _process_exe_by_pid(pid, allocator)
|
||||
if _, ok := err.(runtime.Allocator_Error); ok {
|
||||
return
|
||||
} else if err != nil {
|
||||
break snapshot_module
|
||||
}
|
||||
info.executable_path = exe_path
|
||||
info.fields += {.Executable_Path}
|
||||
}
|
||||
ph := win32.HANDLE(process.handle)
|
||||
read_peb: if selection >= {.Command_Line, .Environment, .Working_Dir} {
|
||||
process_info_size: u32
|
||||
process_info: win32.PROCESS_BASIC_INFORMATION
|
||||
status := win32.NtQueryInformationProcess(ph, .ProcessBasicInformation, &process_info, size_of(process_info), &process_info_size)
|
||||
if status != 0 {
|
||||
// TODO(flysand): There's probably a mismatch between NTSTATUS and
|
||||
// windows userland error codes, I haven't checked.
|
||||
err = Platform_Error(status)
|
||||
return
|
||||
}
|
||||
assert(process_info.PebBaseAddress != nil)
|
||||
process_peb: win32.PEB
|
||||
_, err = read_memory_as_struct(ph, process_info.PebBaseAddress, &process_peb)
|
||||
if err != nil {
|
||||
break read_peb
|
||||
}
|
||||
process_params: win32.RTL_USER_PROCESS_PARAMETERS
|
||||
_, err = read_memory_as_struct(ph, process_peb.ProcessParameters, &process_params)
|
||||
if err != nil {
|
||||
break read_peb
|
||||
}
|
||||
if selection >= {.Command_Line, .Command_Args} {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cmdline_w := make([]u16, process_params.CommandLine.Length, temp_allocator()) or_return
|
||||
_, err = read_memory_as_slice(ph, process_params.CommandLine.Buffer, cmdline_w)
|
||||
if err != nil {
|
||||
break read_peb
|
||||
}
|
||||
if .Command_Line in selection {
|
||||
info.command_line = win32_utf16_to_utf8(cmdline_w, allocator) or_return
|
||||
info.fields += {.Command_Line}
|
||||
}
|
||||
if .Command_Args in selection {
|
||||
info.command_args = _parse_command_line(raw_data(cmdline_w), allocator) or_return
|
||||
info.fields += {.Command_Args}
|
||||
}
|
||||
}
|
||||
if .Environment in selection {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
env_len := process_params.EnvironmentSize / 2
|
||||
envs_w := make([]u16, env_len, temp_allocator()) or_return
|
||||
_, err = read_memory_as_slice(ph, process_params.Environment, envs_w)
|
||||
if err != nil {
|
||||
break read_peb
|
||||
}
|
||||
info.environment = _parse_environment_block(raw_data(envs_w), allocator) or_return
|
||||
info.fields += {.Environment}
|
||||
}
|
||||
if .Working_Dir in selection {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cwd_w := make([]u16, process_params.CurrentDirectoryPath.Length, temp_allocator()) or_return
|
||||
_, err = read_memory_as_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w)
|
||||
if err != nil {
|
||||
break read_peb
|
||||
}
|
||||
info.working_dir = win32_utf16_to_utf8(cwd_w, allocator) or_return
|
||||
info.fields += {.Working_Dir}
|
||||
}
|
||||
}
|
||||
read_username: if .Username in selection {
|
||||
username: string
|
||||
username, err = _get_process_user(ph, allocator)
|
||||
if _, ok := err.(runtime.Allocator_Error); ok {
|
||||
return
|
||||
} else if err != nil {
|
||||
break read_username
|
||||
}
|
||||
info.username = username
|
||||
info.fields += {.Username}
|
||||
}
|
||||
err = nil
|
||||
@@ -294,15 +328,15 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields
|
||||
@(private="package")
|
||||
_current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
|
||||
info.pid = get_pid()
|
||||
defer if err != nil {
|
||||
free_process_info(info, allocator)
|
||||
}
|
||||
|
||||
if selection >= {.PPid, .Priority} { // snap process
|
||||
snapshot_process: if selection >= {.PPid, .Priority} {
|
||||
entry, entry_err := _process_entry_by_pid(info.pid)
|
||||
if entry_err != nil {
|
||||
err = General_Error.Not_Exist
|
||||
return
|
||||
err = entry_err
|
||||
if entry_err == General_Error.Not_Exist {
|
||||
return
|
||||
} else {
|
||||
break snapshot_process
|
||||
}
|
||||
}
|
||||
if .PPid in selection {
|
||||
info.fields += {.PPid}
|
||||
@@ -313,14 +347,16 @@ _current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime
|
||||
info.priority = int(entry.pcPriClassBase)
|
||||
}
|
||||
}
|
||||
if .Executable_Path in selection {
|
||||
module_filename: if .Executable_Path in selection {
|
||||
exe_filename_w: [256]u16
|
||||
path_len := win32.GetModuleFileNameW(nil, raw_data(exe_filename_w[:]), len(exe_filename_w))
|
||||
assert(path_len > 0)
|
||||
info.executable_path = win32_utf16_to_utf8(exe_filename_w[:path_len], allocator) or_return
|
||||
info.fields += {.Executable_Path}
|
||||
}
|
||||
if selection >= {.Command_Line, .Command_Args} {
|
||||
command_line: if selection >= {.Command_Line, .Command_Args} {
|
||||
command_line_w := win32.GetCommandLineW()
|
||||
assert(command_line_w != nil)
|
||||
if .Command_Line in selection {
|
||||
info.command_line = win32_wstring_to_utf8(command_line_w, allocator) or_return
|
||||
info.fields += {.Command_Line}
|
||||
@@ -330,14 +366,22 @@ _current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime
|
||||
info.fields += {.Command_Args}
|
||||
}
|
||||
}
|
||||
if .Environment in selection {
|
||||
read_environment: if .Environment in selection {
|
||||
env_block := win32.GetEnvironmentStringsW()
|
||||
assert(env_block != nil)
|
||||
info.environment = _parse_environment_block(env_block, allocator) or_return
|
||||
info.fields += {.Environment}
|
||||
}
|
||||
if .Username in selection {
|
||||
read_username: if .Username in selection {
|
||||
process_handle := win32.GetCurrentProcess()
|
||||
info.username = _get_process_user(process_handle, allocator) or_return
|
||||
username: string
|
||||
username, err = _get_process_user(process_handle, allocator)
|
||||
if _, ok := err.(runtime.Allocator_Error); ok {
|
||||
return
|
||||
} else if err != nil {
|
||||
break read_username
|
||||
}
|
||||
info.username = username
|
||||
info.fields += {.Username}
|
||||
}
|
||||
if .Working_Dir in selection {
|
||||
|
||||
@@ -398,9 +398,9 @@ SIOCGIFFLAG :: enum c.int {
|
||||
PORTSEL = 13, /* Can set media type. */
|
||||
AUTOMEDIA = 14, /* Auto media select active. */
|
||||
DYNAMIC = 15, /* Dialup device with changing addresses. */
|
||||
LOWER_UP = 16,
|
||||
DORMANT = 17,
|
||||
ECHO = 18,
|
||||
LOWER_UP = 16,
|
||||
DORMANT = 17,
|
||||
ECHO = 18,
|
||||
}
|
||||
SIOCGIFFLAGS :: bit_set[SIOCGIFFLAG; c.int]
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
|
||||
elem.data = rawptr(uintptr(val.data) + uintptr(it^ * info.elem_size))
|
||||
elem.id = info.elem.id
|
||||
ok = true
|
||||
index = it^
|
||||
it^ += 1
|
||||
}
|
||||
case Type_Info_Slice:
|
||||
@@ -27,6 +28,7 @@ iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
|
||||
elem.data = rawptr(uintptr(array.data) + uintptr(it^ * info.elem_size))
|
||||
elem.id = info.elem.id
|
||||
ok = true
|
||||
index = it^
|
||||
it^ += 1
|
||||
}
|
||||
case Type_Info_Dynamic_Array:
|
||||
@@ -35,6 +37,7 @@ iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
|
||||
elem.data = rawptr(uintptr(array.data) + uintptr(it^ * info.elem_size))
|
||||
elem.id = info.elem.id
|
||||
ok = true
|
||||
index = it^
|
||||
it^ += 1
|
||||
}
|
||||
}
|
||||
@@ -69,10 +72,12 @@ iterate_map :: proc(val: any, it: ^int) -> (key, value: any, ok: bool) {
|
||||
key.id = info.key.id
|
||||
value.id = info.value.id
|
||||
ok = true
|
||||
it^ += 1
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,157 +9,157 @@ String :: distinct TypeRef // same as CFStringRef
|
||||
StringEncoding :: distinct u32
|
||||
|
||||
StringBuiltInEncodings :: enum StringEncoding {
|
||||
MacRoman = 0,
|
||||
MacRoman = 0,
|
||||
WindowsLatin1 = 0x0500,
|
||||
ISOLatin1 = 0x0201,
|
||||
ISOLatin1 = 0x0201,
|
||||
NextStepLatin = 0x0B01,
|
||||
ASCII = 0x0600,
|
||||
Unicode = 0x0100,
|
||||
UTF8 = 0x08000100,
|
||||
ASCII = 0x0600,
|
||||
Unicode = 0x0100,
|
||||
UTF8 = 0x08000100,
|
||||
NonLossyASCII = 0x0BFF,
|
||||
|
||||
UTF16 = 0x0100,
|
||||
UTF16 = 0x0100,
|
||||
UTF16BE = 0x10000100,
|
||||
UTF16LE = 0x14000100,
|
||||
|
||||
UTF32 = 0x0c000100,
|
||||
UTF32BE = 0x18000100,
|
||||
UTF32LE = 0x1c000100,
|
||||
UTF32 = 0x0c000100,
|
||||
UTF32BE = 0x18000100,
|
||||
UTF32LE = 0x1c000100,
|
||||
}
|
||||
|
||||
StringEncodings :: enum Index {
|
||||
MacJapanese = 1,
|
||||
MacChineseTrad = 2,
|
||||
MacKorean = 3,
|
||||
MacArabic = 4,
|
||||
MacHebrew = 5,
|
||||
MacGreek = 6,
|
||||
MacCyrillic = 7,
|
||||
MacDevanagari = 9,
|
||||
MacGurmukhi = 10,
|
||||
MacGujarati = 11,
|
||||
MacOriya = 12,
|
||||
MacBengali = 13,
|
||||
MacTamil = 14,
|
||||
MacTelugu = 15,
|
||||
MacKannada = 16,
|
||||
MacMalayalam = 17,
|
||||
MacSinhalese = 18,
|
||||
MacBurmese = 19,
|
||||
MacKhmer = 20,
|
||||
MacThai = 21,
|
||||
MacLaotian = 22,
|
||||
MacGeorgian = 23,
|
||||
MacArmenian = 24,
|
||||
MacChineseSimp = 25,
|
||||
MacTibetan = 26,
|
||||
MacMongolian = 27,
|
||||
MacEthiopic = 28,
|
||||
MacCentralEurRoman = 29,
|
||||
MacVietnamese = 30,
|
||||
MacExtArabic = 31,
|
||||
MacSymbol = 33,
|
||||
MacDingbats = 34,
|
||||
MacTurkish = 35,
|
||||
MacCroatian = 36,
|
||||
MacIcelandic = 37,
|
||||
MacRomanian = 38,
|
||||
MacCeltic = 39,
|
||||
MacGaelic = 40,
|
||||
MacFarsi = 0x8C,
|
||||
MacUkrainian = 0x98,
|
||||
MacInuit = 0xEC,
|
||||
MacVT100 = 0xFC,
|
||||
MacHFS = 0xFF,
|
||||
ISOLatin2 = 0x0202,
|
||||
ISOLatin3 = 0x0203,
|
||||
ISOLatin4 = 0x0204,
|
||||
ISOLatinCyrillic = 0x0205,
|
||||
ISOLatinArabic = 0x0206,
|
||||
ISOLatinGreek = 0x0207,
|
||||
ISOLatinHebrew = 0x0208,
|
||||
ISOLatin5 = 0x0209,
|
||||
ISOLatin6 = 0x020A,
|
||||
ISOLatinThai = 0x020B,
|
||||
ISOLatin7 = 0x020D,
|
||||
ISOLatin8 = 0x020E,
|
||||
ISOLatin9 = 0x020F,
|
||||
ISOLatin10 = 0x0210,
|
||||
DOSLatinUS = 0x0400,
|
||||
DOSGreek = 0x0405,
|
||||
DOSBalticRim = 0x0406,
|
||||
DOSLatin1 = 0x0410,
|
||||
DOSGreek1 = 0x0411,
|
||||
DOSLatin2 = 0x0412,
|
||||
DOSCyrillic = 0x0413,
|
||||
DOSTurkish = 0x0414,
|
||||
DOSPortuguese = 0x0415,
|
||||
DOSIcelandic = 0x0416,
|
||||
DOSHebrew = 0x0417,
|
||||
DOSCanadianFrench = 0x0418,
|
||||
DOSArabic = 0x0419,
|
||||
DOSNordic = 0x041A,
|
||||
DOSRussian = 0x041B,
|
||||
DOSGreek2 = 0x041C,
|
||||
DOSThai = 0x041D,
|
||||
DOSJapanese = 0x0420,
|
||||
DOSChineseSimplif = 0x0421,
|
||||
DOSKorean = 0x0422,
|
||||
DOSChineseTrad = 0x0423,
|
||||
WindowsLatin2 = 0x0501,
|
||||
WindowsCyrillic = 0x0502,
|
||||
WindowsGreek = 0x0503,
|
||||
WindowsLatin5 = 0x0504,
|
||||
WindowsHebrew = 0x0505,
|
||||
WindowsArabic = 0x0506,
|
||||
WindowsBalticRim = 0x0507,
|
||||
WindowsVietnamese = 0x0508,
|
||||
WindowsKoreanJohab = 0x0510,
|
||||
ANSEL = 0x0601,
|
||||
JIS_X0201_76 = 0x0620,
|
||||
JIS_X0208_83 = 0x0621,
|
||||
JIS_X0208_90 = 0x0622,
|
||||
JIS_X0212_90 = 0x0623,
|
||||
JIS_C6226_78 = 0x0624,
|
||||
ShiftJIS_X0213 = 0x0628,
|
||||
ShiftJIS_X0213_MenKuTen = 0x0629,
|
||||
GB_2312_80 = 0x0630,
|
||||
GBK_95 = 0x0631,
|
||||
GB_18030_2000 = 0x0632,
|
||||
KSC_5601_87 = 0x0640,
|
||||
KSC_5601_92_Johab = 0x0641,
|
||||
CNS_11643_92_P1 = 0x0651,
|
||||
CNS_11643_92_P2 = 0x0652,
|
||||
CNS_11643_92_P3 = 0x0653,
|
||||
ISO_2022_JP = 0x0820,
|
||||
ISO_2022_JP_2 = 0x0821,
|
||||
ISO_2022_JP_1 = 0x0822,
|
||||
ISO_2022_JP_3 = 0x0823,
|
||||
ISO_2022_CN = 0x0830,
|
||||
ISO_2022_CN_EXT = 0x0831,
|
||||
ISO_2022_KR = 0x0840,
|
||||
EUC_JP = 0x0920,
|
||||
EUC_CN = 0x0930,
|
||||
EUC_TW = 0x0931,
|
||||
EUC_KR = 0x0940,
|
||||
ShiftJIS = 0x0A01,
|
||||
KOI8_R = 0x0A02,
|
||||
Big5 = 0x0A03,
|
||||
MacRomanLatin1 = 0x0A04,
|
||||
HZ_GB_2312 = 0x0A05,
|
||||
Big5_HKSCS_1999 = 0x0A06,
|
||||
VISCII = 0x0A07,
|
||||
KOI8_U = 0x0A08,
|
||||
Big5_E = 0x0A09,
|
||||
NextStepJapanese = 0x0B02,
|
||||
EBCDIC_US = 0x0C01,
|
||||
EBCDIC_CP037 = 0x0C02,
|
||||
UTF7 = 0x04000100,
|
||||
UTF7_IMAP = 0x0A10,
|
||||
ShiftJIS_X0213_00 = 0x0628, // Deprecated. Use `ShiftJIS_X0213` instead.
|
||||
MacJapanese = 1,
|
||||
MacChineseTrad = 2,
|
||||
MacKorean = 3,
|
||||
MacArabic = 4,
|
||||
MacHebrew = 5,
|
||||
MacGreek = 6,
|
||||
MacCyrillic = 7,
|
||||
MacDevanagari = 9,
|
||||
MacGurmukhi = 10,
|
||||
MacGujarati = 11,
|
||||
MacOriya = 12,
|
||||
MacBengali = 13,
|
||||
MacTamil = 14,
|
||||
MacTelugu = 15,
|
||||
MacKannada = 16,
|
||||
MacMalayalam = 17,
|
||||
MacSinhalese = 18,
|
||||
MacBurmese = 19,
|
||||
MacKhmer = 20,
|
||||
MacThai = 21,
|
||||
MacLaotian = 22,
|
||||
MacGeorgian = 23,
|
||||
MacArmenian = 24,
|
||||
MacChineseSimp = 25,
|
||||
MacTibetan = 26,
|
||||
MacMongolian = 27,
|
||||
MacEthiopic = 28,
|
||||
MacCentralEurRoman = 29,
|
||||
MacVietnamese = 30,
|
||||
MacExtArabic = 31,
|
||||
MacSymbol = 33,
|
||||
MacDingbats = 34,
|
||||
MacTurkish = 35,
|
||||
MacCroatian = 36,
|
||||
MacIcelandic = 37,
|
||||
MacRomanian = 38,
|
||||
MacCeltic = 39,
|
||||
MacGaelic = 40,
|
||||
MacFarsi = 0x8C,
|
||||
MacUkrainian = 0x98,
|
||||
MacInuit = 0xEC,
|
||||
MacVT100 = 0xFC,
|
||||
MacHFS = 0xFF,
|
||||
ISOLatin2 = 0x0202,
|
||||
ISOLatin3 = 0x0203,
|
||||
ISOLatin4 = 0x0204,
|
||||
ISOLatinCyrillic = 0x0205,
|
||||
ISOLatinArabic = 0x0206,
|
||||
ISOLatinGreek = 0x0207,
|
||||
ISOLatinHebrew = 0x0208,
|
||||
ISOLatin5 = 0x0209,
|
||||
ISOLatin6 = 0x020A,
|
||||
ISOLatinThai = 0x020B,
|
||||
ISOLatin7 = 0x020D,
|
||||
ISOLatin8 = 0x020E,
|
||||
ISOLatin9 = 0x020F,
|
||||
ISOLatin10 = 0x0210,
|
||||
DOSLatinUS = 0x0400,
|
||||
DOSGreek = 0x0405,
|
||||
DOSBalticRim = 0x0406,
|
||||
DOSLatin1 = 0x0410,
|
||||
DOSGreek1 = 0x0411,
|
||||
DOSLatin2 = 0x0412,
|
||||
DOSCyrillic = 0x0413,
|
||||
DOSTurkish = 0x0414,
|
||||
DOSPortuguese = 0x0415,
|
||||
DOSIcelandic = 0x0416,
|
||||
DOSHebrew = 0x0417,
|
||||
DOSCanadianFrench = 0x0418,
|
||||
DOSArabic = 0x0419,
|
||||
DOSNordic = 0x041A,
|
||||
DOSRussian = 0x041B,
|
||||
DOSGreek2 = 0x041C,
|
||||
DOSThai = 0x041D,
|
||||
DOSJapanese = 0x0420,
|
||||
DOSChineseSimplif = 0x0421,
|
||||
DOSKorean = 0x0422,
|
||||
DOSChineseTrad = 0x0423,
|
||||
WindowsLatin2 = 0x0501,
|
||||
WindowsCyrillic = 0x0502,
|
||||
WindowsGreek = 0x0503,
|
||||
WindowsLatin5 = 0x0504,
|
||||
WindowsHebrew = 0x0505,
|
||||
WindowsArabic = 0x0506,
|
||||
WindowsBalticRim = 0x0507,
|
||||
WindowsVietnamese = 0x0508,
|
||||
WindowsKoreanJohab = 0x0510,
|
||||
ANSEL = 0x0601,
|
||||
JIS_X0201_76 = 0x0620,
|
||||
JIS_X0208_83 = 0x0621,
|
||||
JIS_X0208_90 = 0x0622,
|
||||
JIS_X0212_90 = 0x0623,
|
||||
JIS_C6226_78 = 0x0624,
|
||||
ShiftJIS_X0213 = 0x0628,
|
||||
ShiftJIS_X0213_MenKuTen = 0x0629,
|
||||
GB_2312_80 = 0x0630,
|
||||
GBK_95 = 0x0631,
|
||||
GB_18030_2000 = 0x0632,
|
||||
KSC_5601_87 = 0x0640,
|
||||
KSC_5601_92_Johab = 0x0641,
|
||||
CNS_11643_92_P1 = 0x0651,
|
||||
CNS_11643_92_P2 = 0x0652,
|
||||
CNS_11643_92_P3 = 0x0653,
|
||||
ISO_2022_JP = 0x0820,
|
||||
ISO_2022_JP_2 = 0x0821,
|
||||
ISO_2022_JP_1 = 0x0822,
|
||||
ISO_2022_JP_3 = 0x0823,
|
||||
ISO_2022_CN = 0x0830,
|
||||
ISO_2022_CN_EXT = 0x0831,
|
||||
ISO_2022_KR = 0x0840,
|
||||
EUC_JP = 0x0920,
|
||||
EUC_CN = 0x0930,
|
||||
EUC_TW = 0x0931,
|
||||
EUC_KR = 0x0940,
|
||||
ShiftJIS = 0x0A01,
|
||||
KOI8_R = 0x0A02,
|
||||
Big5 = 0x0A03,
|
||||
MacRomanLatin1 = 0x0A04,
|
||||
HZ_GB_2312 = 0x0A05,
|
||||
Big5_HKSCS_1999 = 0x0A06,
|
||||
VISCII = 0x0A07,
|
||||
KOI8_U = 0x0A08,
|
||||
Big5_E = 0x0A09,
|
||||
NextStepJapanese = 0x0B02,
|
||||
EBCDIC_US = 0x0C01,
|
||||
EBCDIC_CP037 = 0x0C02,
|
||||
UTF7 = 0x04000100,
|
||||
UTF7_IMAP = 0x0A10,
|
||||
ShiftJIS_X0213_00 = 0x0628, // Deprecated. Use `ShiftJIS_X0213` instead.
|
||||
}
|
||||
|
||||
@(link_prefix = "CF", default_calling_convention = "c")
|
||||
@(link_prefix="CF", default_calling_convention="c")
|
||||
foreign CoreFoundation {
|
||||
// Copies the character contents of a string to a local C string buffer after converting the characters to a given encoding.
|
||||
StringGetCString :: proc(theString: String, buffer: [^]byte, bufferSize: Index, encoding: StringEncoding) -> b8 ---
|
||||
@@ -181,23 +181,16 @@ foreign CoreFoundation {
|
||||
|
||||
STR :: StringMakeConstantString
|
||||
|
||||
StringCopyToOdinString :: proc(
|
||||
theString: String,
|
||||
allocator := context.allocator,
|
||||
) -> (
|
||||
str: string,
|
||||
ok: bool,
|
||||
) #optional_ok {
|
||||
StringCopyToOdinString :: proc(theString: String, allocator := context.allocator) -> (str: string, ok: bool) #optional_ok {
|
||||
length := StringGetLength(theString)
|
||||
max := StringGetMaximumSizeForEncoding(length, StringEncoding(StringBuiltInEncodings.UTF8))
|
||||
|
||||
buf, err := make([]byte, max, allocator)
|
||||
if err != nil { return }
|
||||
|
||||
raw_str := runtime.Raw_String {
|
||||
data = raw_data(buf),
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
StringGetBytes(theString, {0, length}, StringEncoding(StringBuiltInEncodings.UTF8), 0, false, raw_data(buf), max, (^Index)(&raw_str.len))
|
||||
|
||||
return transmute(string)raw_str, true
|
||||
n: Index
|
||||
StringGetBytes(theString, {0, length}, StringEncoding(StringBuiltInEncodings.UTF8), 0, false, raw_data(buf), Index(len(buf)), &n)
|
||||
return string(buf[:n]), true
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@ sys_write_string :: proc (fd: c.int, message: string) -> bool {
|
||||
Offset_From :: enum c.int {
|
||||
SEEK_SET = 0, // the offset is set to offset bytes.
|
||||
SEEK_CUR = 1, // the offset is set to its current location plus offset bytes.
|
||||
SEEK_END = 2, // the offset is set to the size of the file plus offset bytes.
|
||||
SEEK_HOLE = 3, // the offset is set to the start of the next hole greater than or equal to the supplied offset.
|
||||
SEEK_DATA = 4, // the offset is set to the start of the next non-hole file region greater than or equal to the supplied offset.
|
||||
SEEK_END = 2, // the offset is set to the size of the file plus offset bytes.
|
||||
SEEK_HOLE = 3, // the offset is set to the start of the next hole greater than or equal to the supplied offset.
|
||||
SEEK_DATA = 4, // the offset is set to the start of the next non-hole file region greater than or equal to the supplied offset.
|
||||
}
|
||||
|
||||
Open_Flags_Enum :: enum u8 {
|
||||
|
||||
@@ -192,43 +192,43 @@ _STRUCT_TIMEVAL :: struct {
|
||||
|
||||
/* pwd.h */
|
||||
_Password_Entry :: struct {
|
||||
pw_name: cstring, /* username */
|
||||
pw_passwd: cstring, /* user password */
|
||||
pw_uid: i32, /* user ID */
|
||||
pw_gid: i32, /* group ID */
|
||||
pw_name: cstring, /* username */
|
||||
pw_passwd: cstring, /* user password */
|
||||
pw_uid: i32, /* user ID */
|
||||
pw_gid: i32, /* group ID */
|
||||
pw_change: u64, /* password change time */
|
||||
pw_class: cstring, /* user access class */
|
||||
pw_gecos: cstring, /* full user name */
|
||||
pw_dir: cstring, /* home directory */
|
||||
pw_shell: cstring, /* shell program */
|
||||
pw_gecos: cstring, /* full user name */
|
||||
pw_dir: cstring, /* home directory */
|
||||
pw_shell: cstring, /* shell program */
|
||||
pw_expire: u64, /* account expiration */
|
||||
pw_fields: i32, /* filled fields */
|
||||
}
|
||||
|
||||
/* processinfo.h */
|
||||
_Proc_Bsdinfo :: struct {
|
||||
pbi_flags: u32, /* if is 64bit; emulated etc */
|
||||
pbi_status: u32,
|
||||
pbi_xstatus: u32,
|
||||
pbi_pid: u32,
|
||||
pbi_ppid: u32,
|
||||
pbi_uid: u32,
|
||||
pbi_gid: u32,
|
||||
pbi_ruid: u32,
|
||||
pbi_rgid: u32,
|
||||
pbi_svuid: u32,
|
||||
pbi_svgid: u32,
|
||||
res: u32,
|
||||
pbi_comm: [DARWIN_MAXCOMLEN]u8,
|
||||
pbi_name: [2 * DARWIN_MAXCOMLEN]u8, /* empty if no name is registered */
|
||||
pbi_nfiles: u32,
|
||||
pbi_pgid: u32,
|
||||
pbi_pjobc: u32,
|
||||
e_tdev: u32, /* controlling tty dev */
|
||||
e_tpgid: u32, /* tty process group id */
|
||||
pbi_nice: i32,
|
||||
pbi_start_tvsec: u64,
|
||||
pbi_start_tvusec: u64,
|
||||
pbi_flags: u32, /* if is 64bit; emulated etc */
|
||||
pbi_status: u32,
|
||||
pbi_xstatus: u32,
|
||||
pbi_pid: u32,
|
||||
pbi_ppid: u32,
|
||||
pbi_uid: u32,
|
||||
pbi_gid: u32,
|
||||
pbi_ruid: u32,
|
||||
pbi_rgid: u32,
|
||||
pbi_svuid: u32,
|
||||
pbi_svgid: u32,
|
||||
res: u32,
|
||||
pbi_comm: [DARWIN_MAXCOMLEN]u8,
|
||||
pbi_name: [2 * DARWIN_MAXCOMLEN]u8, /* empty if no name is registered */
|
||||
pbi_nfiles: u32,
|
||||
pbi_pgid: u32,
|
||||
pbi_pjobc: u32,
|
||||
e_tdev: u32, /* controlling tty dev */
|
||||
e_tpgid: u32, /* tty process group id */
|
||||
pbi_nice: i32,
|
||||
pbi_start_tvsec: u64,
|
||||
pbi_start_tvusec: u64,
|
||||
}
|
||||
|
||||
/*--==========================================================================--*/
|
||||
|
||||
@@ -695,12 +695,12 @@ Record_Lock_Flag :: enum c.int {
|
||||
|
||||
// struct flock
|
||||
File_Lock :: struct {
|
||||
start: off_t, /* starting offset */
|
||||
len: off_t, /* len = 0 means until end of file */
|
||||
pid: pid_t, /* lock owner */
|
||||
type: Record_Lock_Flag, /* lock type: read/write, etc. */
|
||||
whence: c.short, /* type of l_start */
|
||||
sysid: c.int, /* remote system id or zero for local */
|
||||
start: off_t, /* starting offset */
|
||||
len: off_t, /* len = 0 means until end of file */
|
||||
pid: pid_t, /* lock owner */
|
||||
type: Record_Lock_Flag, /* lock type: read/write, etc. */
|
||||
whence: c.short, /* type of l_start */
|
||||
sysid: c.int, /* remote system id or zero for local */
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -4,7 +4,7 @@ package sys_windows
|
||||
foreign import dwmapi "system:Dwmapi.lib"
|
||||
|
||||
DWMWINDOWATTRIBUTE :: enum {
|
||||
DWMWA_NCRENDERING_ENABLED,
|
||||
DWMWA_NCRENDERING_ENABLED = 1,
|
||||
DWMWA_NCRENDERING_POLICY,
|
||||
DWMWA_TRANSITIONS_FORCEDISABLED,
|
||||
DWMWA_ALLOW_NCPAINT,
|
||||
@@ -28,7 +28,7 @@ DWMWINDOWATTRIBUTE :: enum {
|
||||
DWMWA_TEXT_COLOR,
|
||||
DWMWA_VISIBLE_FRAME_BORDER_THICKNESS,
|
||||
DWMWA_SYSTEMBACKDROP_TYPE,
|
||||
DWMWA_LAST,
|
||||
DWMWA_LAST,
|
||||
}
|
||||
|
||||
DWMNCRENDERINGPOLICY :: enum {
|
||||
|
||||
@@ -1175,17 +1175,17 @@ SYSTEM_POWER_STATUS :: struct {
|
||||
}
|
||||
|
||||
AC_Line_Status :: enum BYTE {
|
||||
Offline = 0,
|
||||
Online = 1,
|
||||
Unknown = 255,
|
||||
Offline = 0,
|
||||
Online = 1,
|
||||
Unknown = 255,
|
||||
}
|
||||
|
||||
Battery_Flag :: enum BYTE {
|
||||
High = 0,
|
||||
Low = 1,
|
||||
Critical = 2,
|
||||
Charging = 3,
|
||||
No_Battery = 7,
|
||||
High = 0,
|
||||
Low = 1,
|
||||
Critical = 2,
|
||||
Charging = 3,
|
||||
No_Battery = 7,
|
||||
}
|
||||
Battery_Flags :: bit_set[Battery_Flag; BYTE]
|
||||
|
||||
|
||||
+26
-26
@@ -3546,11 +3546,11 @@ SIGDN :: enum c_int {
|
||||
}
|
||||
|
||||
SIATTRIBFLAGS :: enum c_int {
|
||||
AND = 0x1,
|
||||
OR = 0x2,
|
||||
APPCOMPAT = 0x3,
|
||||
MASK = 0x3,
|
||||
ALLITEMS = 0x4000,
|
||||
AND = 0x1,
|
||||
OR = 0x2,
|
||||
APPCOMPAT = 0x3,
|
||||
MASK = 0x3,
|
||||
ALLITEMS = 0x4000,
|
||||
}
|
||||
|
||||
FDAP :: enum c_int {
|
||||
@@ -4503,35 +4503,35 @@ DNS_INFO_NO_RECORDS :: 9501
|
||||
DNS_QUERY_NO_RECURSION :: 0x00000004
|
||||
|
||||
DNS_RECORD :: struct { // aka DNS_RECORDA
|
||||
pNext: ^DNS_RECORD,
|
||||
pName: cstring,
|
||||
wType: WORD,
|
||||
wDataLength: USHORT,
|
||||
Flags: DWORD,
|
||||
dwTtl: DWORD,
|
||||
_: DWORD,
|
||||
Data: struct #raw_union {
|
||||
CNAME: DNS_PTR_DATAA,
|
||||
A: u32be, // Ipv4 Address
|
||||
AAAA: u128be, // Ipv6 Address
|
||||
TXT: DNS_TXT_DATAA,
|
||||
NS: DNS_PTR_DATAA,
|
||||
MX: DNS_MX_DATAA,
|
||||
SRV: DNS_SRV_DATAA,
|
||||
},
|
||||
pNext: ^DNS_RECORD,
|
||||
pName: cstring,
|
||||
wType: WORD,
|
||||
wDataLength: USHORT,
|
||||
Flags: DWORD,
|
||||
dwTtl: DWORD,
|
||||
_: DWORD,
|
||||
Data: struct #raw_union {
|
||||
CNAME: DNS_PTR_DATAA,
|
||||
A: u32be, // Ipv4 Address
|
||||
AAAA: u128be, // Ipv6 Address
|
||||
TXT: DNS_TXT_DATAA,
|
||||
NS: DNS_PTR_DATAA,
|
||||
MX: DNS_MX_DATAA,
|
||||
SRV: DNS_SRV_DATAA,
|
||||
},
|
||||
}
|
||||
|
||||
DNS_TXT_DATAA :: struct {
|
||||
dwStringCount: DWORD,
|
||||
pStringArray: cstring,
|
||||
dwStringCount: DWORD,
|
||||
pStringArray: cstring,
|
||||
}
|
||||
|
||||
DNS_PTR_DATAA :: cstring
|
||||
|
||||
DNS_MX_DATAA :: struct {
|
||||
pNameExchange: cstring, // the hostname
|
||||
wPreference: WORD, // lower values preferred
|
||||
_: WORD, // padding.
|
||||
pNameExchange: cstring, // the hostname
|
||||
wPreference: WORD, // lower values preferred
|
||||
_: WORD, // padding.
|
||||
}
|
||||
DNS_SRV_DATAA :: struct {
|
||||
pNameTarget: cstring,
|
||||
|
||||
@@ -56,7 +56,7 @@ Decorations :: struct {
|
||||
|
||||
// Connecting decorations:
|
||||
nw, n, ne,
|
||||
w, x, e,
|
||||
w, x, e,
|
||||
sw, s, se: string,
|
||||
|
||||
// Straight lines:
|
||||
|
||||
@@ -175,10 +175,12 @@ pool_stop_task :: proc(pool: ^Pool, user_index: int, exit_code: int = 1) -> bool
|
||||
intrinsics.atomic_sub(&pool.num_outstanding, 1)
|
||||
intrinsics.atomic_sub(&pool.num_in_processing, 1)
|
||||
|
||||
old_thread_user_index := t.user_index
|
||||
|
||||
destroy(t)
|
||||
|
||||
replacement := create(pool_thread_runner)
|
||||
replacement.user_index = t.user_index
|
||||
replacement.user_index = old_thread_user_index
|
||||
replacement.data = data
|
||||
data.task = {}
|
||||
pool.threads[i] = replacement
|
||||
@@ -207,10 +209,12 @@ pool_stop_all_tasks :: proc(pool: ^Pool, exit_code: int = 1) {
|
||||
intrinsics.atomic_sub(&pool.num_outstanding, 1)
|
||||
intrinsics.atomic_sub(&pool.num_in_processing, 1)
|
||||
|
||||
old_thread_user_index := t.user_index
|
||||
|
||||
destroy(t)
|
||||
|
||||
replacement := create(pool_thread_runner)
|
||||
replacement.user_index = t.user_index
|
||||
replacement.user_index = old_thread_user_index
|
||||
replacement.data = data
|
||||
data.task = {}
|
||||
pool.threads[i] = replacement
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// +private
|
||||
package thread
|
||||
|
||||
import "base:runtime"
|
||||
import "core:sync"
|
||||
import "core:sys/unix"
|
||||
import "core:time"
|
||||
@@ -55,7 +56,10 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
|
||||
// Here on Unix, we start the OS thread in a running state, and so we manually have it wait on a condition
|
||||
// variable above. We must perform that waiting BEFORE we select the context!
|
||||
context = _select_context_for_thread(init_context)
|
||||
defer _maybe_destroy_default_temp_allocator(init_context)
|
||||
defer {
|
||||
_maybe_destroy_default_temp_allocator(init_context)
|
||||
runtime.run_thread_local_cleaners()
|
||||
}
|
||||
|
||||
t.procedure(t)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
package thread
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
import "core:sync"
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
@@ -39,7 +40,10 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
|
||||
// Here on Windows, the thread is created in a suspended state, and so we can select the context anywhere before the call
|
||||
// to t.procedure().
|
||||
context = _select_context_for_thread(init_context)
|
||||
defer _maybe_destroy_default_temp_allocator(init_context)
|
||||
defer {
|
||||
_maybe_destroy_default_temp_allocator(init_context)
|
||||
runtime.run_thread_local_cleaners()
|
||||
}
|
||||
|
||||
t.procedure(t)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user