mirror of
https://github.com/odin-lang/Odin.git
synced 2025-12-28 17:04:34 +00:00
319 lines
11 KiB
Odin
319 lines
11 KiB
Odin
package os
|
|
|
|
import "base:intrinsics"
|
|
import "base:runtime"
|
|
import "core:io"
|
|
|
|
Platform_Error :: _Platform_Error
|
|
#assert(size_of(Platform_Error) <= 4)
|
|
#assert(intrinsics.type_has_nil(Platform_Error))
|
|
|
|
General_Error :: enum u32 {
|
|
None,
|
|
|
|
Exist,
|
|
Not_Exist,
|
|
|
|
Timeout,
|
|
|
|
Broken_Pipe,
|
|
|
|
Invalid_File,
|
|
Invalid_Dir,
|
|
Invalid_Path,
|
|
Invalid_Callback,
|
|
|
|
Pattern_Has_Separator,
|
|
|
|
File_Is_Pipe,
|
|
Not_Dir,
|
|
|
|
// Environment variable not found.
|
|
Env_Var_Not_Found,
|
|
}
|
|
|
|
|
|
Errno :: Error // alias for legacy use
|
|
|
|
Error :: union #shared_nil {
|
|
General_Error,
|
|
io.Error,
|
|
runtime.Allocator_Error,
|
|
Platform_Error,
|
|
}
|
|
#assert(size_of(Error) == 8)
|
|
|
|
ERROR_NONE :: Error{}
|
|
|
|
ERROR_EOF :: io.Error.EOF
|
|
|
|
@(require_results)
|
|
is_platform_error :: proc "contextless" (ferr: Error) -> (err: i32, ok: bool) {
|
|
v := ferr.(Platform_Error) or_else {}
|
|
return i32(v), i32(v) != 0
|
|
}
|
|
|
|
@(require_results)
|
|
error_string :: proc "contextless" (ferr: Error) -> string {
|
|
if ferr == nil {
|
|
return ""
|
|
}
|
|
switch e in ferr {
|
|
case General_Error:
|
|
switch e {
|
|
case .None: return ""
|
|
case .Exist: return "file already exists"
|
|
case .Not_Exist: return "file does not exist"
|
|
case .Timeout: return "i/o timeout"
|
|
case .Broken_Pipe: return "Broken pipe"
|
|
case .Invalid_File: return "invalid file"
|
|
case .Invalid_Dir: return "invalid directory"
|
|
case .Invalid_Path: return "invalid path"
|
|
case .Invalid_Callback: return "invalid callback"
|
|
case .Pattern_Has_Separator: return "pattern has separator"
|
|
case .File_Is_Pipe: return "file is pipe"
|
|
case .Not_Dir: return "file is not directory"
|
|
case .Env_Var_Not_Found: return "environment variable not found"
|
|
}
|
|
case io.Error:
|
|
switch e {
|
|
case .None: return ""
|
|
case .EOF: return "eof"
|
|
case .Unexpected_EOF: return "unexpected eof"
|
|
case .Short_Write: return "short write"
|
|
case .Invalid_Write: return "invalid write result"
|
|
case .Short_Buffer: return "short buffer"
|
|
case .No_Progress: return "multiple read calls return no data or error"
|
|
case .Invalid_Whence: return "invalid whence"
|
|
case .Invalid_Offset: return "invalid offset"
|
|
case .Invalid_Unread: return "invalid unread"
|
|
case .Negative_Read: return "negative read"
|
|
case .Negative_Write: return "negative write"
|
|
case .Negative_Count: return "negative count"
|
|
case .Buffer_Full: return "buffer full"
|
|
case .Permission_Denied: return "permission denied"
|
|
case .Closed: return "file already closed"
|
|
case .No_Size: return "file has no definite size"
|
|
case .Unsupported: return "unsupported"
|
|
case .Unknown: //
|
|
}
|
|
case runtime.Allocator_Error:
|
|
switch e {
|
|
case .None: return ""
|
|
case .Out_Of_Memory: return "out of memory"
|
|
case .Invalid_Pointer: return "invalid allocator pointer"
|
|
case .Invalid_Argument: return "invalid allocator argument"
|
|
case .Mode_Not_Implemented: return "allocator mode not implemented"
|
|
}
|
|
case Platform_Error:
|
|
return _error_string(e)
|
|
}
|
|
|
|
return "unknown error"
|
|
}
|
|
|
|
print_error :: proc(f: Handle, ferr: Error, msg: string) -> (n: int, err: Error) {
|
|
err_str := error_string(ferr)
|
|
|
|
// msg + ": " + err_str + '\n'
|
|
length := len(msg) + 2 + len(err_str) + 1
|
|
buf_ := intrinsics.alloca(length, 1)
|
|
buf := buf_[:length]
|
|
|
|
copy(buf, msg)
|
|
buf[len(msg)] = ':'
|
|
buf[len(msg) + 1] = ' '
|
|
copy(buf[len(msg) + 2:], err_str)
|
|
buf[length - 1] = '\n'
|
|
return write(f, buf)
|
|
}
|
|
|
|
|
|
@(require_results, private)
|
|
_error_string :: proc "contextless" (e: Platform_Error) -> string where intrinsics.type_is_enum(Platform_Error) {
|
|
if e == nil {
|
|
return ""
|
|
}
|
|
|
|
when ODIN_OS == .Darwin {
|
|
if s := string(_darwin_string_error(i32(e))); s != "" {
|
|
return s
|
|
}
|
|
}
|
|
|
|
when ODIN_OS != .Linux {
|
|
@(require_results)
|
|
binary_search :: proc "contextless" (array: $A/[]$T, key: T) -> (index: int, found: bool) #no_bounds_check {
|
|
n := len(array)
|
|
left, right := 0, n
|
|
for left < right {
|
|
mid := int(uint(left+right) >> 1)
|
|
if array[mid] < key {
|
|
left = mid+1
|
|
} else {
|
|
// equal or greater
|
|
right = mid
|
|
}
|
|
}
|
|
return left, left < n && array[left] == key
|
|
}
|
|
|
|
err := runtime.Type_Info_Enum_Value(e)
|
|
|
|
ti := &runtime.type_info_base(type_info_of(Platform_Error)).variant.(runtime.Type_Info_Enum)
|
|
if idx, ok := binary_search(ti.values, err); ok {
|
|
return ti.names[idx]
|
|
}
|
|
} else {
|
|
@(rodata, static)
|
|
pe_strings := [Platform_Error]string{
|
|
.NONE = "",
|
|
.EPERM = "Operation not permitted",
|
|
.ENOENT = "No such file or directory",
|
|
.ESRCH = "No such process",
|
|
.EINTR = "Interrupted system call",
|
|
.EIO = "Input/output error",
|
|
.ENXIO = "No such device or address",
|
|
.E2BIG = "Argument list too long",
|
|
.ENOEXEC = "Exec format error",
|
|
.EBADF = "Bad file descriptor",
|
|
.ECHILD = "No child processes",
|
|
.EAGAIN = "Resource temporarily unavailable",
|
|
.ENOMEM = "Cannot allocate memory",
|
|
.EACCES = "Permission denied",
|
|
.EFAULT = "Bad address",
|
|
.ENOTBLK = "Block device required",
|
|
.EBUSY = "Device or resource busy",
|
|
.EEXIST = "File exists",
|
|
.EXDEV = "Invalid cross-device link",
|
|
.ENODEV = "No such device",
|
|
.ENOTDIR = "Not a directory",
|
|
.EISDIR = "Is a directory",
|
|
.EINVAL = "Invalid argument",
|
|
.ENFILE = "Too many open files in system",
|
|
.EMFILE = "Too many open files",
|
|
.ENOTTY = "Inappropriate ioctl for device",
|
|
.ETXTBSY = "Text file busy",
|
|
.EFBIG = "File too large",
|
|
.ENOSPC = "No space left on device",
|
|
.ESPIPE = "Illegal seek",
|
|
.EROFS = "Read-only file system",
|
|
.EMLINK = "Too many links",
|
|
.EPIPE = "Broken pipe",
|
|
.EDOM = "Numerical argument out of domain",
|
|
.ERANGE = "Numerical result out of range",
|
|
.EDEADLK = "Resource deadlock avoided",
|
|
.ENAMETOOLONG = "File name too long",
|
|
.ENOLCK = "No locks available",
|
|
.ENOSYS = "Function not implemented",
|
|
.ENOTEMPTY = "Directory not empty",
|
|
.ELOOP = "Too many levels of symbolic links",
|
|
.EUNKNOWN_41 = "Unknown Error (41)",
|
|
.ENOMSG = "No message of desired type",
|
|
.EIDRM = "Identifier removed",
|
|
.ECHRNG = "Channel number out of range",
|
|
.EL2NSYNC = "Level 2 not synchronized",
|
|
.EL3HLT = "Level 3 halted",
|
|
.EL3RST = "Level 3 reset",
|
|
.ELNRNG = "Link number out of range",
|
|
.EUNATCH = "Protocol driver not attached",
|
|
.ENOCSI = "No CSI structure available",
|
|
.EL2HLT = "Level 2 halted",
|
|
.EBADE = "Invalid exchange",
|
|
.EBADR = "Invalid request descriptor",
|
|
.EXFULL = "Exchange full",
|
|
.ENOANO = "No anode",
|
|
.EBADRQC = "Invalid request code",
|
|
.EBADSLT = "Invalid slot",
|
|
.EUNKNOWN_58 = "Unknown Error (58)",
|
|
.EBFONT = "Bad font file format",
|
|
.ENOSTR = "Device not a stream",
|
|
.ENODATA = "No data available",
|
|
.ETIME = "Timer expired",
|
|
.ENOSR = "Out of streams resources",
|
|
.ENONET = "Machine is not on the network",
|
|
.ENOPKG = "Package not installed",
|
|
.EREMOTE = "Object is remote",
|
|
.ENOLINK = "Link has been severed",
|
|
.EADV = "Advertise error",
|
|
.ESRMNT = "Srmount error",
|
|
.ECOMM = "Communication error on send",
|
|
.EPROTO = "Protocol error",
|
|
.EMULTIHOP = "Multihop attempted",
|
|
.EDOTDOT = "RFS specific error",
|
|
.EBADMSG = "Bad message",
|
|
.EOVERFLOW = "Value too large for defined data type",
|
|
.ENOTUNIQ = "Name not unique on network",
|
|
.EBADFD = "File descriptor in bad state",
|
|
.EREMCHG = "Remote address changed",
|
|
.ELIBACC = "Can not access a needed shared library",
|
|
.ELIBBAD = "Accessing a corrupted shared library",
|
|
.ELIBSCN = ".lib section in a.out corrupted",
|
|
.ELIBMAX = "Attempting to link in too many shared libraries",
|
|
.ELIBEXEC = "Cannot exec a shared library directly",
|
|
.EILSEQ = "Invalid or incomplete multibyte or wide character",
|
|
.ERESTART = "Interrupted system call should be restarted",
|
|
.ESTRPIPE = "Streams pipe error",
|
|
.EUSERS = "Too many users",
|
|
.ENOTSOCK = "Socket operation on non-socket",
|
|
.EDESTADDRREQ = "Destination address required",
|
|
.EMSGSIZE = "Message too long",
|
|
.EPROTOTYPE = "Protocol wrong type for socket",
|
|
.ENOPROTOOPT = "Protocol not available",
|
|
.EPROTONOSUPPORT = "Protocol not supported",
|
|
.ESOCKTNOSUPPORT = "Socket type not supported",
|
|
.EOPNOTSUPP = "Operation not supported",
|
|
.EPFNOSUPPORT = "Protocol family not supported",
|
|
.EAFNOSUPPORT = "Address family not supported by protocol",
|
|
.EADDRINUSE = "Address already in use",
|
|
.EADDRNOTAVAIL = "Cannot assign requested address",
|
|
.ENETDOWN = "Network is down",
|
|
.ENETUNREACH = "Network is unreachable",
|
|
.ENETRESET = "Network dropped connection on reset",
|
|
.ECONNABORTED = "Software caused connection abort",
|
|
.ECONNRESET = "Connection reset by peer",
|
|
.ENOBUFS = "No buffer space available",
|
|
.EISCONN = "Transport endpoint is already connected",
|
|
.ENOTCONN = "Transport endpoint is not connected",
|
|
.ESHUTDOWN = "Cannot send after transport endpoint shutdown",
|
|
.ETOOMANYREFS = "Too many references: cannot splice",
|
|
.ETIMEDOUT = "Connection timed out",
|
|
.ECONNREFUSED = "Connection refused",
|
|
.EHOSTDOWN = "Host is down",
|
|
.EHOSTUNREACH = "No route to host",
|
|
.EALREADY = "Operation already in progress",
|
|
.EINPROGRESS = "Operation now in progress",
|
|
.ESTALE = "Stale file handle",
|
|
.EUCLEAN = "Structure needs cleaning",
|
|
.ENOTNAM = "Not a XENIX named type file",
|
|
.ENAVAIL = "No XENIX semaphores available",
|
|
.EISNAM = "Is a named type file",
|
|
.EREMOTEIO = "Remote I/O error",
|
|
.EDQUOT = "Disk quota exceeded",
|
|
.ENOMEDIUM = "No medium found",
|
|
.EMEDIUMTYPE = "Wrong medium type",
|
|
.ECANCELED = "Operation canceled",
|
|
.ENOKEY = "Required key not available",
|
|
.EKEYEXPIRED = "Key has expired",
|
|
.EKEYREVOKED = "Key has been revoked",
|
|
.EKEYREJECTED = "Key was rejected by service",
|
|
.EOWNERDEAD = "Owner died",
|
|
.ENOTRECOVERABLE = "State not recoverable",
|
|
.ERFKILL = "Operation not possible due to RF-kill",
|
|
.EHWPOISON = "Memory page has hardware error",
|
|
}
|
|
if Platform_Error.NONE <= e && e <= max(Platform_Error) {
|
|
return pe_strings[e]
|
|
}
|
|
}
|
|
return "<unknown platform error>"
|
|
}
|
|
|
|
@(private, require_results)
|
|
error_to_io_error :: proc(ferr: Error) -> io.Error {
|
|
if ferr == nil {
|
|
return .None
|
|
}
|
|
return ferr.(io.Error) or_else .Unknown
|
|
}
|