os: add error_posix() and error_win32() for explicit platform error handling and default behavior (#20694)

This commit is contained in:
syrmel 2024-01-31 06:25:07 +01:00 committed by GitHub
parent 74d4081f1b
commit 382d7658cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 59 additions and 15 deletions

View file

@ -153,18 +153,18 @@ pub fn truncate(path string, len u64) ! {
C.open(&char(path.str), o_wronly | o_trunc, 0) C.open(&char(path.str), o_wronly | o_trunc, 0)
} }
if fp < 0 { if fp < 0 {
return error_with_code(posix_get_error_msg(C.errno), C.errno) return error_posix()
} }
defer { defer {
C.close(fp) C.close(fp)
} }
$if windows { $if windows {
if C._chsize_s(fp, len) != 0 { if C._chsize_s(fp, len) != 0 {
return error_with_code(posix_get_error_msg(C.errno), C.errno) return error_posix()
} }
} $else { } $else {
if C.ftruncate(fp, len) != 0 { if C.ftruncate(fp, len) != 0 {
return error_with_code(posix_get_error_msg(C.errno), C.errno) return error_posix()
} }
} }
} }
@ -263,8 +263,7 @@ pub fn cp(src string, dst string) ! {
w_src := src.replace('/', '\\') w_src := src.replace('/', '\\')
w_dst := dst.replace('/', '\\') w_dst := dst.replace('/', '\\')
if C.CopyFile(w_src.to_wide(), w_dst.to_wide(), false) == 0 { if C.CopyFile(w_src.to_wide(), w_dst.to_wide(), false) == 0 {
result := C.GetLastError() return error_win32(msg: 'failed to copy ${src} to ${dst}')
return error_with_code('failed to copy ${src} to ${dst}', int(result))
} }
} $else { } $else {
fp_from := C.open(&char(src.str), C.O_RDONLY, 0) fp_from := C.open(&char(src.str), C.O_RDONLY, 0)
@ -320,7 +319,7 @@ pub fn vfopen(path string, mode string) !&C.FILE {
fp = C.fopen(&char(path.str), &char(mode.str)) fp = C.fopen(&char(path.str), &char(mode.str))
} }
if isnil(fp) { if isnil(fp) {
return error('failed to open file "${path}"') return error_posix(msg: 'failed to open file "${path}"')
} else { } else {
return fp return fp
} }
@ -515,7 +514,7 @@ pub fn rm(path string) ! {
rc = C.remove(&char(path.str)) rc = C.remove(&char(path.str))
} }
if rc == -1 { if rc == -1 {
return error('Failed to remove "${path}": ' + posix_get_error_msg(C.errno)) return error_posix(msg: 'Failed to remove "${path}": ' + posix_get_error_msg(C.errno))
} }
// C.unlink(path.cstr()) // C.unlink(path.cstr())
} }
@ -526,12 +525,14 @@ pub fn rmdir(path string) ! {
rc := C.RemoveDirectory(path.to_wide()) rc := C.RemoveDirectory(path.to_wide())
if !rc { if !rc {
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-removedirectorya - 0 == false, is failure // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-removedirectorya - 0 == false, is failure
return error('Failed to remove "${path}": ' + posix_get_error_msg(C.errno)) return error_win32(
msg: 'Failed to remove "${path}": ' + get_error_msg(int(C.GetLastError()))
)
} }
} $else { } $else {
rc := C.rmdir(&char(path.str)) rc := C.rmdir(&char(path.str))
if rc == -1 { if rc == -1 {
return error(posix_get_error_msg(C.errno)) return error_posix()
} }
} }
} }
@ -868,7 +869,7 @@ fn kind_of_existing_path(path string) PathKind {
pub fn chdir(path string) ! { pub fn chdir(path string) ! {
ret := $if windows { C._wchdir(path.to_wide()) } $else { C.chdir(&char(path.str)) } ret := $if windows { C._wchdir(path.to_wide()) } $else { C.chdir(&char(path.str)) }
if ret == -1 { if ret == -1 {
return error_with_code(posix_get_error_msg(C.errno), C.errno) return error_posix()
} }
} }
@ -1021,7 +1022,7 @@ pub fn flush() {
// Octals like `0o600` can be used. // Octals like `0o600` can be used.
pub fn chmod(path string, mode int) ! { pub fn chmod(path string, mode int) ! {
if C.chmod(&char(path.str), mode) != 0 { if C.chmod(&char(path.str), mode) != 0 {
return error_with_code('chmod failed: ' + posix_get_error_msg(C.errno), C.errno) return error_posix(msg: 'chmod failed: ' + posix_get_error_msg(C.errno))
} }
} }
@ -1031,7 +1032,7 @@ pub fn chown(path string, owner int, group int) ! {
return error('os.chown() not implemented for Windows') return error('os.chown() not implemented for Windows')
} $else { } $else {
if C.chown(&char(path.str), owner, group) != 0 { if C.chown(&char(path.str), owner, group) != 0 {
return error_with_code(posix_get_error_msg(C.errno), C.errno) return error_posix()
} }
} }
} }
@ -1053,7 +1054,7 @@ pub fn open_append(path string) !File {
} }
} }
if isnil(file.cfile) { if isnil(file.cfile) {
return error('failed to create(append) file "${path}"') return error_posix(msg: 'failed to create(append) file "${path}"')
} }
file.is_opened = true file.is_opened = true
return file return file
@ -1078,7 +1079,7 @@ pub fn execvp(cmdpath string, cmdargs []string) ! {
res = C.execvp(&char(cmdpath.str), cargs.data) res = C.execvp(&char(cmdpath.str), cargs.data)
} }
if res == -1 { if res == -1 {
return error_with_code(posix_get_error_msg(C.errno), C.errno) return error_posix()
} }
// just in case C._execvp returned ... that happens on windows ... // just in case C._execvp returned ... that happens on windows ...
@ -1112,7 +1113,7 @@ pub fn execve(cmdpath string, cmdargs []string, envs []string) ! {
// Note: normally execve does not return at all. // Note: normally execve does not return at all.
// If it returns, then something went wrong... // If it returns, then something went wrong...
if res == -1 { if res == -1 {
return error_with_code(posix_get_error_msg(C.errno), C.errno) return error_posix()
} }
} }
@ -1155,3 +1156,36 @@ pub fn last_error() IError {
return error_with_code(msg, code) return error_with_code(msg, code)
} }
} }
// Magic constant because zero is used explicitly at times
const error_code_not_set = 0x7EFEFEFE
@[params]
struct SystemError {
msg string
code int = os.error_code_not_set
}
// Return a POSIX error:
// Code defaults to last error (from C.errno)
// Message defaults to POSIX error message for the error code
@[inline]
pub fn error_posix(e SystemError) IError {
code := if e.code == os.error_code_not_set { C.errno } else { e.code }
message := if e.msg == '' { posix_get_error_msg(code) } else { e.msg }
return error_with_code(message, code)
}
// Return a Win32 API error:
// Code defaults to last error (calling C.GetLastError())
// Message defaults to Win 32 API error message for the error code
@[inline]
pub fn error_win32(e SystemError) IError {
$if windows {
code := if e.code == os.error_code_not_set { int(C.GetLastError()) } else { e.code }
message := if e.msg == '' { get_error_msg(code) } else { e.msg }
return error_with_code(message, code)
} $else {
panic('Win32 API not available on this platform.')
}
}

View file

@ -642,6 +642,16 @@ fn test_rmdir_all() {
assert !os.exists('some') assert !os.exists('some')
} }
fn test_rmdir_not_exist() ! {
dir := 'non_existing_dir'
assert !os.exists(dir)
os.rmdir(dir) or {
// 0x00000002 is both ENOENT in POSIX and ERROR_FILE_NOT_FOUND in Win32 API
assert err.code() == 0x00000002
}
assert !os.exists(dir)
}
fn test_dir() { fn test_dir() {
$if windows { $if windows {
assert os.dir('C:\\a\\b\\c') == 'C:\\a\\b' assert os.dir('C:\\a\\b\\c') == 'C:\\a\\b'