2020-08-01 23:07:37 +02:00
|
|
|
module os
|
|
|
|
|
|
|
|
pub struct File {
|
2021-01-12 04:38:43 +01:00
|
|
|
cfile voidptr // Using void* instead of FILE*
|
2020-08-01 23:07:37 +02:00
|
|
|
pub:
|
2021-01-12 04:38:43 +01:00
|
|
|
fd int
|
2020-08-01 23:07:37 +02:00
|
|
|
pub mut:
|
|
|
|
is_opened bool
|
|
|
|
}
|
|
|
|
|
|
|
|
struct FileInfo {
|
|
|
|
name string
|
|
|
|
size int
|
|
|
|
}
|
|
|
|
|
2021-05-22 06:45:00 +02:00
|
|
|
fn C.fseeko(&C.FILE, u64, int) int
|
2021-03-26 07:51:55 +01:00
|
|
|
|
2021-05-22 06:45:00 +02:00
|
|
|
fn C._fseeki64(&C.FILE, u64, int) int
|
2021-03-26 07:51:55 +01:00
|
|
|
|
2021-05-22 06:45:00 +02:00
|
|
|
fn C.getc(&C.FILE) int
|
2021-05-21 12:18:08 +02:00
|
|
|
|
2021-03-04 12:33:54 +01:00
|
|
|
// open_file can be used to open or create a file with custom flags and permissions and returns a `File` object.
|
|
|
|
pub fn open_file(path string, mode string, options ...int) ?File {
|
|
|
|
mut flags := 0
|
|
|
|
for m in mode {
|
|
|
|
match m {
|
|
|
|
`w` { flags |= o_create | o_trunc }
|
|
|
|
`a` { flags |= o_create | o_append }
|
|
|
|
`r` { flags |= o_rdonly }
|
|
|
|
`b` { flags |= o_binary }
|
|
|
|
`s` { flags |= o_sync }
|
|
|
|
`n` { flags |= o_nonblock }
|
|
|
|
`c` { flags |= o_noctty }
|
|
|
|
`+` { flags |= o_rdwr }
|
|
|
|
else {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if mode == 'r+' {
|
|
|
|
flags = o_rdwr
|
|
|
|
}
|
|
|
|
if mode == 'w' {
|
|
|
|
flags = o_wronly | o_create | o_trunc
|
|
|
|
}
|
|
|
|
if mode == 'a' {
|
|
|
|
flags = o_wronly | o_create | o_append
|
|
|
|
}
|
|
|
|
mut permission := 0o666
|
|
|
|
if options.len > 0 {
|
|
|
|
permission = options[0]
|
|
|
|
}
|
|
|
|
$if windows {
|
|
|
|
if permission < 0o600 {
|
|
|
|
permission = 0x0100
|
|
|
|
} else {
|
|
|
|
permission = 0x0100 | 0x0080
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mut p := path
|
|
|
|
$if windows {
|
|
|
|
p = path.replace('/', '\\')
|
|
|
|
}
|
2021-04-04 16:43:32 +02:00
|
|
|
fd := C.open(&char(p.str), flags, permission)
|
2021-03-04 12:33:54 +01:00
|
|
|
if fd == -1 {
|
|
|
|
return error(posix_get_error_msg(C.errno))
|
|
|
|
}
|
2021-04-04 16:43:32 +02:00
|
|
|
cfile := C.fdopen(fd, &char(mode.str))
|
2021-03-04 12:33:54 +01:00
|
|
|
if isnil(cfile) {
|
|
|
|
return error('Failed to open or create file "$path"')
|
|
|
|
}
|
|
|
|
return File{
|
|
|
|
cfile: cfile
|
|
|
|
fd: fd
|
|
|
|
is_opened: true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// open tries to open a file for reading and returns back a read-only `File` object.
|
|
|
|
pub fn open(path string) ?File {
|
|
|
|
/*
|
|
|
|
$if linux {
|
|
|
|
$if !android {
|
|
|
|
fd := C.syscall(sys_open, path.str, 511)
|
|
|
|
if fd == -1 {
|
|
|
|
return error('failed to open file "$path"')
|
|
|
|
}
|
|
|
|
return File{
|
|
|
|
fd: fd
|
|
|
|
is_opened: true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
cfile := vfopen(path, 'rb') ?
|
|
|
|
fd := fileno(cfile)
|
|
|
|
return File{
|
|
|
|
cfile: cfile
|
|
|
|
fd: fd
|
|
|
|
is_opened: true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// create creates or opens a file at a specified location and returns a write-only `File` object.
|
|
|
|
pub fn create(path string) ?File {
|
|
|
|
/*
|
|
|
|
// NB: android/termux/bionic is also a kind of linux,
|
|
|
|
// but linux syscalls there sometimes fail,
|
|
|
|
// while the libc version should work.
|
|
|
|
$if linux {
|
|
|
|
$if !android {
|
|
|
|
//$if macos {
|
|
|
|
// fd = C.syscall(398, path.str, 0x601, 0x1b6)
|
|
|
|
//}
|
|
|
|
//$if linux {
|
|
|
|
fd = C.syscall(sys_creat, path.str, 511)
|
|
|
|
//}
|
|
|
|
if fd == -1 {
|
|
|
|
return error('failed to create file "$path"')
|
|
|
|
}
|
|
|
|
file = File{
|
|
|
|
fd: fd
|
|
|
|
is_opened: true
|
|
|
|
}
|
|
|
|
return file
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
cfile := vfopen(path, 'wb') ?
|
|
|
|
fd := fileno(cfile)
|
|
|
|
return File{
|
|
|
|
cfile: cfile
|
|
|
|
fd: fd
|
|
|
|
is_opened: true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-03 17:57:21 +02:00
|
|
|
[deprecated: 'use os.stdin() instead']
|
|
|
|
[deprecated_after: '2021-05-17']
|
2021-03-04 12:33:54 +01:00
|
|
|
pub fn open_stdin() File {
|
2021-05-04 07:40:27 +02:00
|
|
|
return stdin()
|
2021-05-03 17:57:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// stdin - return an os.File for stdin, so that you can use .get_line on it too.
|
|
|
|
pub fn stdin() File {
|
2021-03-04 12:33:54 +01:00
|
|
|
return File{
|
|
|
|
fd: 0
|
|
|
|
cfile: C.stdin
|
|
|
|
is_opened: true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-03 18:00:51 +02:00
|
|
|
// stdout - return an os.File for stdout
|
|
|
|
pub fn stdout() File {
|
|
|
|
return File{
|
|
|
|
fd: 1
|
|
|
|
cfile: C.stdout
|
|
|
|
is_opened: true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// stderr - return an os.File for stderr
|
|
|
|
pub fn stderr() File {
|
|
|
|
return File{
|
|
|
|
fd: 2
|
|
|
|
cfile: C.stderr
|
|
|
|
is_opened: true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-10 07:33:46 +02:00
|
|
|
// read implements the Reader interface.
|
|
|
|
pub fn (f &File) read(mut buf []byte) ?int {
|
|
|
|
if buf.len == 0 {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
nbytes := fread(buf.data, 1, buf.len, f.cfile) ?
|
|
|
|
return nbytes
|
|
|
|
}
|
|
|
|
|
2020-08-01 23:07:37 +02:00
|
|
|
// **************************** Write ops ***************************
|
2021-03-04 12:33:54 +01:00
|
|
|
// write implements the Writer interface.
|
|
|
|
// It returns how many bytes were actually written.
|
2020-11-15 21:54:47 +01:00
|
|
|
pub fn (mut f File) write(buf []byte) ?int {
|
2020-08-01 23:07:37 +02:00
|
|
|
if !f.is_opened {
|
2020-09-12 07:31:15 +02:00
|
|
|
return error('file is not opened')
|
2020-08-01 23:07:37 +02:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
$if linux {
|
|
|
|
$if !android {
|
2021-03-04 12:33:54 +01:00
|
|
|
res := C.syscall(sys_write, f.fd, s.str, s.len)
|
|
|
|
return res
|
2020-08-01 23:07:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
2021-04-27 00:42:16 +02:00
|
|
|
written := int(C.fwrite(buf.data, 1, buf.len, f.cfile))
|
2020-11-15 21:54:47 +01:00
|
|
|
if written == 0 && buf.len != 0 {
|
2020-09-12 07:31:15 +02:00
|
|
|
return error('0 bytes written')
|
|
|
|
}
|
|
|
|
return written
|
2020-08-01 23:07:37 +02:00
|
|
|
}
|
|
|
|
|
2021-03-04 12:33:54 +01:00
|
|
|
// writeln writes the string `s` into the file, and appends a \n character.
|
|
|
|
// It returns how many bytes were written, including the \n character.
|
2020-09-12 07:31:15 +02:00
|
|
|
pub fn (mut f File) writeln(s string) ?int {
|
2020-08-01 23:07:37 +02:00
|
|
|
if !f.is_opened {
|
2020-09-12 07:31:15 +02:00
|
|
|
return error('file is not opened')
|
2020-08-01 23:07:37 +02:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
$if linux {
|
|
|
|
$if !android {
|
|
|
|
snl := s + '\n'
|
|
|
|
C.syscall(sys_write, f.fd, snl.str, snl.len)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
// TODO perf
|
2021-04-27 00:42:16 +02:00
|
|
|
written := int(C.fwrite(s.str, 1, s.len, f.cfile))
|
2020-09-12 07:31:15 +02:00
|
|
|
if written == 0 && s.len != 0 {
|
|
|
|
return error('0 bytes written')
|
|
|
|
}
|
2021-04-05 20:39:32 +02:00
|
|
|
x := C.fputs(c'\n', f.cfile)
|
2020-09-12 07:31:15 +02:00
|
|
|
if x < 0 {
|
|
|
|
return error('could not add newline')
|
|
|
|
}
|
|
|
|
return (written + 1)
|
2020-08-01 23:07:37 +02:00
|
|
|
}
|
|
|
|
|
2021-03-04 12:33:54 +01:00
|
|
|
// write_string writes the string `s` into the file
|
|
|
|
// It returns how many bytes were actually written.
|
2020-12-08 19:37:26 +01:00
|
|
|
pub fn (mut f File) write_string(s string) ?int {
|
|
|
|
if !f.is_opened {
|
|
|
|
return error('file is not opened')
|
|
|
|
}
|
|
|
|
// TODO perf
|
2021-04-27 00:42:16 +02:00
|
|
|
written := int(C.fwrite(s.str, 1, s.len, f.cfile))
|
2020-12-08 19:37:26 +01:00
|
|
|
if written == 0 && s.len != 0 {
|
|
|
|
return error('0 bytes written')
|
|
|
|
}
|
|
|
|
return written
|
|
|
|
}
|
|
|
|
|
2021-03-04 12:33:54 +01:00
|
|
|
// write_to implements the RandomWriter interface.
|
|
|
|
// It returns how many bytes were actually written.
|
|
|
|
// It resets the seek position to the end of the file.
|
2021-03-26 07:51:55 +01:00
|
|
|
pub fn (mut f File) write_to(pos u64, buf []byte) ?int {
|
2021-03-04 12:33:54 +01:00
|
|
|
if !f.is_opened {
|
|
|
|
return error('file is not opened')
|
|
|
|
}
|
2021-03-26 07:51:55 +01:00
|
|
|
$if x64 {
|
|
|
|
$if windows {
|
|
|
|
C._fseeki64(f.cfile, pos, C.SEEK_SET)
|
|
|
|
res := int(C.fwrite(buf.data, 1, buf.len, f.cfile))
|
|
|
|
if res == 0 && buf.len != 0 {
|
|
|
|
return error('0 bytes written')
|
|
|
|
}
|
|
|
|
C._fseeki64(f.cfile, 0, C.SEEK_END)
|
|
|
|
return res
|
|
|
|
} $else {
|
|
|
|
C.fseeko(f.cfile, pos, C.SEEK_SET)
|
|
|
|
res := int(C.fwrite(buf.data, 1, buf.len, f.cfile))
|
|
|
|
if res == 0 && buf.len != 0 {
|
|
|
|
return error('0 bytes written')
|
|
|
|
}
|
|
|
|
C.fseeko(f.cfile, 0, C.SEEK_END)
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$if x32 {
|
|
|
|
C.fseek(f.cfile, pos, C.SEEK_SET)
|
|
|
|
res := int(C.fwrite(buf.data, 1, buf.len, f.cfile))
|
|
|
|
if res == 0 && buf.len != 0 {
|
|
|
|
return error('0 bytes written')
|
|
|
|
}
|
|
|
|
C.fseek(f.cfile, 0, C.SEEK_END)
|
|
|
|
return res
|
2021-03-04 12:33:54 +01:00
|
|
|
}
|
2021-03-26 07:51:55 +01:00
|
|
|
return error('Could not write to file')
|
2020-11-15 21:54:47 +01:00
|
|
|
}
|
|
|
|
|
2021-03-04 12:33:54 +01:00
|
|
|
// write_bytes writes `size` bytes to the file, starting from the address in `data`.
|
|
|
|
// NB: write_bytes is unsafe and should be used carefully, since if you pass invalid
|
|
|
|
// pointers to it, it will cause your programs to segfault.
|
2021-03-20 08:02:28 +01:00
|
|
|
[deprecated: 'use File.write_ptr()']
|
2021-02-27 09:11:17 +01:00
|
|
|
[unsafe]
|
2020-08-01 23:07:37 +02:00
|
|
|
pub fn (mut f File) write_bytes(data voidptr, size int) int {
|
2021-03-20 08:02:28 +01:00
|
|
|
return unsafe { f.write_ptr(data, size) }
|
2020-08-01 23:07:37 +02:00
|
|
|
}
|
|
|
|
|
2021-03-04 12:33:54 +01:00
|
|
|
// write_bytes_at writes `size` bytes to the file, starting from the address in `data`,
|
|
|
|
// at byte offset `pos`, counting from the start of the file (pos 0).
|
|
|
|
// NB: write_bytes_at is unsafe and should be used carefully, since if you pass invalid
|
|
|
|
// pointers to it, it will cause your programs to segfault.
|
2021-03-20 08:02:28 +01:00
|
|
|
[deprecated: 'use File.write_ptr_at() instead']
|
2021-02-27 09:11:17 +01:00
|
|
|
[unsafe]
|
2021-03-26 07:51:55 +01:00
|
|
|
pub fn (mut f File) write_bytes_at(data voidptr, size int, pos u64) int {
|
2021-03-20 08:02:28 +01:00
|
|
|
return unsafe { f.write_ptr_at(data, size, pos) }
|
|
|
|
}
|
|
|
|
|
|
|
|
// write_ptr writes `size` bytes to the file, starting from the address in `data`.
|
|
|
|
// NB: write_ptr is unsafe and should be used carefully, since if you pass invalid
|
|
|
|
// pointers to it, it will cause your programs to segfault.
|
|
|
|
[unsafe]
|
|
|
|
pub fn (mut f File) write_ptr(data voidptr, size int) int {
|
|
|
|
return int(C.fwrite(data, 1, size, f.cfile))
|
|
|
|
}
|
|
|
|
|
|
|
|
// write_ptr_at writes `size` bytes to the file, starting from the address in `data`,
|
|
|
|
// at byte offset `pos`, counting from the start of the file (pos 0).
|
|
|
|
// NB: write_ptr_at is unsafe and should be used carefully, since if you pass invalid
|
|
|
|
// pointers to it, it will cause your programs to segfault.
|
|
|
|
[unsafe]
|
2021-03-26 07:51:55 +01:00
|
|
|
pub fn (mut f File) write_ptr_at(data voidptr, size int, pos u64) int {
|
|
|
|
$if x64 {
|
|
|
|
$if windows {
|
|
|
|
C._fseeki64(f.cfile, pos, C.SEEK_SET)
|
|
|
|
res := int(C.fwrite(data, 1, size, f.cfile))
|
|
|
|
C._fseeki64(f.cfile, 0, C.SEEK_END)
|
|
|
|
return res
|
|
|
|
} $else {
|
|
|
|
C.fseeko(f.cfile, pos, C.SEEK_SET)
|
|
|
|
res := int(C.fwrite(data, 1, size, f.cfile))
|
|
|
|
C.fseeko(f.cfile, 0, C.SEEK_END)
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$if x32 {
|
|
|
|
C.fseek(f.cfile, pos, C.SEEK_SET)
|
|
|
|
res := int(C.fwrite(data, 1, size, f.cfile))
|
|
|
|
C.fseek(f.cfile, 0, C.SEEK_END)
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
return 0
|
2020-08-01 23:07:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// **************************** Read ops ***************************
|
2021-05-13 08:48:55 +02:00
|
|
|
|
|
|
|
// fread wraps C.fread and handles error and end-of-file detection.
|
|
|
|
fn fread(ptr voidptr, item_size int, items int, stream &C.FILE) ?int {
|
|
|
|
nbytes := int(C.fread(ptr, item_size, items, stream))
|
|
|
|
// If no bytes were read, check for errors and end-of-file.
|
|
|
|
if nbytes <= 0 {
|
|
|
|
// If fread encountered end-of-file return the none error. Note that fread
|
|
|
|
// may read data and encounter the end-of-file, but we shouldn't return none
|
|
|
|
// in that case which is why we only check for end-of-file if no data was
|
|
|
|
// read. The caller will get none on their next call because there will be
|
|
|
|
// no data available and the end-of-file will be encountered again.
|
|
|
|
if C.feof(stream) != 0 {
|
|
|
|
return none
|
|
|
|
}
|
|
|
|
// If fread encountered an error, return it. Note that fread and ferror do
|
|
|
|
// not tell us what the error was, so we can't return anything more specific
|
|
|
|
// than there was an error. This is because fread and ferror do not set
|
|
|
|
// errno.
|
|
|
|
if C.ferror(stream) != 0 {
|
|
|
|
return error('file read error')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nbytes
|
|
|
|
}
|
|
|
|
|
2021-03-04 12:33:54 +01:00
|
|
|
// read_bytes reads bytes from the beginning of the file.
|
|
|
|
// Utility method, same as .read_bytes_at(size, 0).
|
2020-08-01 23:07:37 +02:00
|
|
|
pub fn (f &File) read_bytes(size int) []byte {
|
|
|
|
return f.read_bytes_at(size, 0)
|
|
|
|
}
|
|
|
|
|
2021-03-04 12:33:54 +01:00
|
|
|
// read_bytes_at reads `size` bytes at the given position in the file.
|
2021-03-26 07:51:55 +01:00
|
|
|
pub fn (f &File) read_bytes_at(size int, pos u64) []byte {
|
2020-09-12 07:31:15 +02:00
|
|
|
mut arr := []byte{len: size}
|
2020-09-27 03:14:24 +02:00
|
|
|
nreadbytes := f.read_bytes_into(pos, mut arr) or {
|
2020-09-14 15:34:34 +02:00
|
|
|
// return err
|
|
|
|
return []
|
|
|
|
}
|
2020-08-01 23:07:37 +02:00
|
|
|
return arr[0..nreadbytes]
|
|
|
|
}
|
|
|
|
|
2021-05-21 12:18:08 +02:00
|
|
|
// read_bytes_into_newline reads from the beginning of the file into the provided buffer.
|
|
|
|
// Each consecutive call on the same file continues reading where it previously ended.
|
|
|
|
// A read call is either stopped, if the buffer is full, a newline was read or EOF.
|
|
|
|
pub fn (f &File) read_bytes_into_newline(mut buf []byte) ?int {
|
|
|
|
if buf.len == 0 {
|
|
|
|
panic(@FN + ': `buf.len` == 0')
|
|
|
|
}
|
|
|
|
newline := 10
|
|
|
|
mut c := 0
|
|
|
|
mut buf_ptr := 0
|
|
|
|
mut nbytes := 0
|
|
|
|
|
2021-05-22 06:48:12 +02:00
|
|
|
stream := &C.FILE(f.cfile)
|
2021-05-21 12:18:08 +02:00
|
|
|
for (buf_ptr < buf.len) {
|
2021-05-22 06:48:12 +02:00
|
|
|
c = C.getc(stream)
|
2021-05-21 12:18:08 +02:00
|
|
|
match c {
|
|
|
|
C.EOF {
|
2021-05-22 06:48:12 +02:00
|
|
|
if C.feof(stream) != 0 {
|
2021-05-21 12:18:08 +02:00
|
|
|
return nbytes
|
|
|
|
}
|
2021-05-22 06:48:12 +02:00
|
|
|
if C.ferror(stream) != 0 {
|
2021-05-21 12:18:08 +02:00
|
|
|
return error('file read error')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
newline {
|
|
|
|
buf[buf_ptr] = byte(c)
|
|
|
|
nbytes++
|
|
|
|
return nbytes
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
buf[buf_ptr] = byte(c)
|
|
|
|
buf_ptr++
|
|
|
|
nbytes++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nbytes
|
|
|
|
}
|
|
|
|
|
2020-12-26 13:57:51 +01:00
|
|
|
// read_bytes_into fills `buf` with bytes at the given position in the file.
|
2021-03-04 12:33:54 +01:00
|
|
|
// `buf` *must* have length greater than zero.
|
|
|
|
// Returns the number of read bytes, or an error.
|
2021-03-26 07:51:55 +01:00
|
|
|
pub fn (f &File) read_bytes_into(pos u64, mut buf []byte) ?int {
|
2020-09-14 15:34:34 +02:00
|
|
|
if buf.len == 0 {
|
|
|
|
panic(@FN + ': `buf.len` == 0')
|
|
|
|
}
|
2021-03-26 07:51:55 +01:00
|
|
|
$if x64 {
|
|
|
|
$if windows {
|
|
|
|
// Note: fseek errors if pos == os.file_size, which we accept
|
|
|
|
C._fseeki64(f.cfile, pos, C.SEEK_SET)
|
2021-05-13 08:48:55 +02:00
|
|
|
nbytes := fread(buf.data, 1, buf.len, f.cfile) ?
|
2021-03-26 07:51:55 +01:00
|
|
|
$if debug {
|
|
|
|
C._fseeki64(f.cfile, 0, C.SEEK_SET)
|
|
|
|
}
|
|
|
|
return nbytes
|
|
|
|
} $else {
|
|
|
|
C.fseeko(f.cfile, pos, C.SEEK_SET)
|
2021-05-13 08:48:55 +02:00
|
|
|
nbytes := fread(buf.data, 1, buf.len, f.cfile) ?
|
2021-03-26 07:51:55 +01:00
|
|
|
$if debug {
|
|
|
|
C.fseeko(f.cfile, 0, C.SEEK_SET)
|
|
|
|
}
|
|
|
|
return nbytes
|
|
|
|
}
|
2020-09-14 15:34:34 +02:00
|
|
|
}
|
2021-03-26 07:51:55 +01:00
|
|
|
$if x32 {
|
|
|
|
C.fseek(f.cfile, pos, C.SEEK_SET)
|
2021-05-13 08:48:55 +02:00
|
|
|
nbytes := fread(buf.data, 1, buf.len, f.cfile) ?
|
2021-03-26 07:51:55 +01:00
|
|
|
$if debug {
|
|
|
|
C.fseek(f.cfile, 0, C.SEEK_SET)
|
|
|
|
}
|
|
|
|
return nbytes
|
2020-09-14 15:34:34 +02:00
|
|
|
}
|
2021-03-26 07:51:55 +01:00
|
|
|
return error('Could not read file')
|
2020-09-14 15:34:34 +02:00
|
|
|
}
|
|
|
|
|
2021-03-04 12:33:54 +01:00
|
|
|
// read_at reads `buf.len` bytes starting at file byte offset `pos`, in `buf`.
|
2021-03-19 11:35:08 +01:00
|
|
|
[deprecated: 'use File.read_from() instead']
|
2021-03-26 07:51:55 +01:00
|
|
|
pub fn (f &File) read_at(pos u64, mut buf []byte) ?int {
|
2021-03-19 11:35:08 +01:00
|
|
|
return f.read_from(pos, mut buf)
|
|
|
|
}
|
|
|
|
|
|
|
|
// read_from implements the RandomReader interface.
|
2021-03-26 07:51:55 +01:00
|
|
|
pub fn (f &File) read_from(pos u64, mut buf []byte) ?int {
|
2020-11-15 21:54:47 +01:00
|
|
|
if buf.len == 0 {
|
|
|
|
return 0
|
|
|
|
}
|
2021-03-26 07:51:55 +01:00
|
|
|
$if x64 {
|
|
|
|
$if windows {
|
|
|
|
C._fseeki64(f.cfile, pos, C.SEEK_SET)
|
|
|
|
} $else {
|
|
|
|
C.fseeko(f.cfile, pos, C.SEEK_SET)
|
|
|
|
}
|
|
|
|
|
2021-05-13 08:48:55 +02:00
|
|
|
nbytes := fread(buf.data, 1, buf.len, f.cfile) ?
|
2021-03-26 07:51:55 +01:00
|
|
|
return nbytes
|
|
|
|
}
|
|
|
|
$if x32 {
|
|
|
|
C.fseek(f.cfile, pos, C.SEEK_SET)
|
2021-05-13 08:48:55 +02:00
|
|
|
nbytes := fread(buf.data, 1, buf.len, f.cfile) ?
|
2021-03-26 07:51:55 +01:00
|
|
|
return nbytes
|
2020-11-15 21:54:47 +01:00
|
|
|
}
|
2021-03-26 07:51:55 +01:00
|
|
|
return error('Could not read file')
|
2020-11-15 21:54:47 +01:00
|
|
|
}
|
|
|
|
|
2020-08-01 23:07:37 +02:00
|
|
|
// **************************** Utility ops ***********************
|
2021-03-04 12:33:54 +01:00
|
|
|
// flush writes any buffered unwritten data left in the file stream.
|
2020-08-01 23:07:37 +02:00
|
|
|
pub fn (mut f File) flush() {
|
|
|
|
if !f.is_opened {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
C.fflush(f.cfile)
|
|
|
|
}
|
2020-09-09 18:37:38 +02:00
|
|
|
|
2021-03-04 12:33:54 +01:00
|
|
|
// write_str writes the bytes of a string into a file,
|
|
|
|
// *including* the terminating 0 byte.
|
2021-03-19 02:51:31 +01:00
|
|
|
[deprecated: 'use File.write_string() instead']
|
2020-11-15 21:54:47 +01:00
|
|
|
pub fn (mut f File) write_str(s string) ? {
|
2021-03-19 02:51:31 +01:00
|
|
|
f.write_string(s) or { return err }
|
2020-09-09 18:37:38 +02:00
|
|
|
}
|
2021-03-04 12:33:54 +01:00
|
|
|
|
|
|
|
// read_struct reads a single struct of type `T`
|
|
|
|
pub fn (mut f File) read_struct<T>(mut t T) ? {
|
|
|
|
if !f.is_opened {
|
|
|
|
return none
|
|
|
|
}
|
|
|
|
tsize := int(sizeof(*t))
|
|
|
|
if tsize == 0 {
|
|
|
|
return none
|
|
|
|
}
|
2021-05-13 08:48:55 +02:00
|
|
|
nbytes := fread(t, 1, tsize, f.cfile) ?
|
2021-03-04 12:33:54 +01:00
|
|
|
if nbytes != tsize {
|
|
|
|
return error_with_code('incomplete struct read', nbytes)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-06 19:44:53 +01:00
|
|
|
// read_struct_at reads a single struct of type `T` at position specified in file
|
2021-03-30 11:40:23 +02:00
|
|
|
pub fn (mut f File) read_struct_at<T>(mut t T, pos u64) ? {
|
2021-03-06 19:44:53 +01:00
|
|
|
if !f.is_opened {
|
|
|
|
return none
|
|
|
|
}
|
|
|
|
tsize := int(sizeof(*t))
|
|
|
|
if tsize == 0 {
|
|
|
|
return none
|
|
|
|
}
|
2021-03-30 11:40:23 +02:00
|
|
|
mut nbytes := 0
|
|
|
|
$if x64 {
|
|
|
|
$if windows {
|
|
|
|
C._fseeki64(f.cfile, pos, C.SEEK_SET)
|
2021-05-13 08:48:55 +02:00
|
|
|
nbytes = fread(t, 1, tsize, f.cfile) ?
|
2021-03-30 11:40:23 +02:00
|
|
|
C._fseeki64(f.cfile, 0, C.SEEK_END)
|
|
|
|
} $else {
|
|
|
|
C.fseeko(f.cfile, pos, C.SEEK_SET)
|
2021-05-13 08:48:55 +02:00
|
|
|
nbytes = fread(t, 1, tsize, f.cfile) ?
|
2021-03-30 11:40:23 +02:00
|
|
|
C.fseeko(f.cfile, 0, C.SEEK_END)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$if x32 {
|
|
|
|
C.fseek(f.cfile, pos, C.SEEK_SET)
|
2021-05-13 08:48:55 +02:00
|
|
|
nbytes = fread(t, 1, tsize, f.cfile) ?
|
2021-03-30 11:40:23 +02:00
|
|
|
C.fseek(f.cfile, 0, C.SEEK_END)
|
|
|
|
}
|
2021-03-06 19:44:53 +01:00
|
|
|
if nbytes != tsize {
|
|
|
|
return error_with_code('incomplete struct read', nbytes)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-10 17:45:12 +01:00
|
|
|
// read_raw reads and returns a single instance of type `T`
|
|
|
|
pub fn (mut f File) read_raw<T>() ?T {
|
|
|
|
if !f.is_opened {
|
|
|
|
return none
|
|
|
|
}
|
|
|
|
tsize := int(sizeof(T))
|
|
|
|
if tsize == 0 {
|
|
|
|
return none
|
|
|
|
}
|
|
|
|
mut t := T{}
|
2021-05-13 08:48:55 +02:00
|
|
|
nbytes := fread(&t, 1, tsize, f.cfile) ?
|
2021-03-10 17:45:12 +01:00
|
|
|
if nbytes != tsize {
|
|
|
|
return error_with_code('incomplete struct read', nbytes)
|
|
|
|
}
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
|
|
|
// read_raw_at reads and returns a single instance of type `T` starting at file byte offset `pos`
|
2021-03-30 11:40:23 +02:00
|
|
|
pub fn (mut f File) read_raw_at<T>(pos u64) ?T {
|
2021-03-10 17:45:12 +01:00
|
|
|
if !f.is_opened {
|
|
|
|
return none
|
|
|
|
}
|
|
|
|
tsize := int(sizeof(T))
|
|
|
|
if tsize == 0 {
|
|
|
|
return none
|
|
|
|
}
|
2021-03-30 11:40:23 +02:00
|
|
|
mut nbytes := 0
|
2021-03-10 17:45:12 +01:00
|
|
|
mut t := T{}
|
2021-03-30 11:40:23 +02:00
|
|
|
$if x64 {
|
|
|
|
$if windows {
|
|
|
|
if C._fseeki64(f.cfile, pos, C.SEEK_SET) != 0 {
|
|
|
|
return error(posix_get_error_msg(C.errno))
|
|
|
|
}
|
2021-05-13 08:48:55 +02:00
|
|
|
nbytes = fread(&t, 1, tsize, f.cfile) ?
|
2021-03-30 11:40:23 +02:00
|
|
|
if C._fseeki64(f.cfile, 0, C.SEEK_END) != 0 {
|
|
|
|
return error(posix_get_error_msg(C.errno))
|
|
|
|
}
|
|
|
|
} $else {
|
|
|
|
if C.fseeko(f.cfile, pos, C.SEEK_SET) != 0 {
|
|
|
|
return error(posix_get_error_msg(C.errno))
|
|
|
|
}
|
2021-05-13 08:48:55 +02:00
|
|
|
nbytes = fread(&t, 1, tsize, f.cfile) ?
|
2021-03-30 11:40:23 +02:00
|
|
|
if C.fseeko(f.cfile, 0, C.SEEK_END) != 0 {
|
|
|
|
return error(posix_get_error_msg(C.errno))
|
|
|
|
}
|
|
|
|
}
|
2021-03-10 17:45:12 +01:00
|
|
|
}
|
2021-03-30 11:40:23 +02:00
|
|
|
$if x32 {
|
|
|
|
if C.fseek(f.cfile, pos, C.SEEK_SET) != 0 {
|
|
|
|
return error(posix_get_error_msg(C.errno))
|
|
|
|
}
|
2021-05-13 08:48:55 +02:00
|
|
|
nbytes = fread(&t, 1, tsize, f.cfile) ?
|
2021-03-30 11:40:23 +02:00
|
|
|
if C.fseek(f.cfile, 0, C.SEEK_END) != 0 {
|
|
|
|
return error(posix_get_error_msg(C.errno))
|
|
|
|
}
|
2021-03-10 17:45:12 +01:00
|
|
|
}
|
2021-03-30 11:40:23 +02:00
|
|
|
|
2021-03-10 17:45:12 +01:00
|
|
|
if nbytes != tsize {
|
|
|
|
return error_with_code('incomplete struct read', nbytes)
|
|
|
|
}
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
2021-03-04 12:33:54 +01:00
|
|
|
// write_struct writes a single struct of type `T`
|
|
|
|
pub fn (mut f File) write_struct<T>(t &T) ? {
|
|
|
|
if !f.is_opened {
|
|
|
|
return error('file is not opened')
|
|
|
|
}
|
2021-03-10 17:45:12 +01:00
|
|
|
tsize := int(sizeof(T))
|
2021-03-04 12:33:54 +01:00
|
|
|
if tsize == 0 {
|
|
|
|
return error('struct size is 0')
|
|
|
|
}
|
|
|
|
C.errno = 0
|
|
|
|
nbytes := int(C.fwrite(t, 1, tsize, f.cfile))
|
|
|
|
if C.errno != 0 {
|
|
|
|
return error(posix_get_error_msg(C.errno))
|
|
|
|
}
|
|
|
|
if nbytes != tsize {
|
|
|
|
return error_with_code('incomplete struct write', nbytes)
|
|
|
|
}
|
|
|
|
}
|
2021-03-06 19:44:53 +01:00
|
|
|
|
|
|
|
// write_struct_at writes a single struct of type `T` at position specified in file
|
2021-03-30 11:40:23 +02:00
|
|
|
pub fn (mut f File) write_struct_at<T>(t &T, pos u64) ? {
|
2021-03-06 19:44:53 +01:00
|
|
|
if !f.is_opened {
|
|
|
|
return error('file is not opened')
|
|
|
|
}
|
2021-03-10 17:45:12 +01:00
|
|
|
tsize := int(sizeof(T))
|
2021-03-06 19:44:53 +01:00
|
|
|
if tsize == 0 {
|
|
|
|
return error('struct size is 0')
|
|
|
|
}
|
|
|
|
C.errno = 0
|
2021-03-30 11:40:23 +02:00
|
|
|
mut nbytes := 0
|
|
|
|
$if x64 {
|
|
|
|
$if windows {
|
|
|
|
C._fseeki64(f.cfile, pos, C.SEEK_SET)
|
|
|
|
nbytes = int(C.fwrite(t, 1, tsize, f.cfile))
|
|
|
|
C._fseeki64(f.cfile, 0, C.SEEK_END)
|
|
|
|
} $else {
|
|
|
|
C.fseeko(f.cfile, pos, C.SEEK_SET)
|
|
|
|
nbytes = int(C.fwrite(t, 1, tsize, f.cfile))
|
|
|
|
C.fseeko(f.cfile, 0, C.SEEK_END)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$if x32 {
|
|
|
|
C.fseek(f.cfile, pos, C.SEEK_SET)
|
|
|
|
nbytes = int(C.fwrite(t, 1, tsize, f.cfile))
|
|
|
|
C.fseek(f.cfile, 0, C.SEEK_END)
|
|
|
|
}
|
2021-03-06 19:44:53 +01:00
|
|
|
if C.errno != 0 {
|
|
|
|
return error(posix_get_error_msg(C.errno))
|
|
|
|
}
|
|
|
|
if nbytes != tsize {
|
|
|
|
return error_with_code('incomplete struct write', nbytes)
|
|
|
|
}
|
|
|
|
}
|
2021-03-10 17:45:12 +01:00
|
|
|
|
|
|
|
// TODO `write_raw[_at]` implementations are copy-pasted from `write_struct[_at]`
|
|
|
|
|
|
|
|
// write_raw writes a single instance of type `T`
|
|
|
|
pub fn (mut f File) write_raw<T>(t &T) ? {
|
|
|
|
if !f.is_opened {
|
|
|
|
return error('file is not opened')
|
|
|
|
}
|
|
|
|
tsize := int(sizeof(T))
|
|
|
|
if tsize == 0 {
|
|
|
|
return error('struct size is 0')
|
|
|
|
}
|
|
|
|
C.errno = 0
|
|
|
|
nbytes := int(C.fwrite(t, 1, tsize, f.cfile))
|
|
|
|
if C.errno != 0 {
|
|
|
|
return error(posix_get_error_msg(C.errno))
|
|
|
|
}
|
|
|
|
if nbytes != tsize {
|
|
|
|
return error_with_code('incomplete struct write', nbytes)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// write_raw_at writes a single instance of type `T` starting at file byte offset `pos`
|
2021-03-30 11:40:23 +02:00
|
|
|
pub fn (mut f File) write_raw_at<T>(t &T, pos u64) ? {
|
2021-03-10 17:45:12 +01:00
|
|
|
if !f.is_opened {
|
|
|
|
return error('file is not opened')
|
|
|
|
}
|
|
|
|
tsize := int(sizeof(T))
|
|
|
|
if tsize == 0 {
|
|
|
|
return error('struct size is 0')
|
|
|
|
}
|
2021-03-30 11:40:23 +02:00
|
|
|
mut nbytes := 0
|
|
|
|
|
|
|
|
$if x64 {
|
|
|
|
$if windows {
|
|
|
|
if C._fseeki64(f.cfile, pos, C.SEEK_SET) != 0 {
|
|
|
|
return error(posix_get_error_msg(C.errno))
|
|
|
|
}
|
|
|
|
nbytes = int(C.fwrite(t, 1, tsize, f.cfile))
|
|
|
|
if C.errno != 0 {
|
|
|
|
return error(posix_get_error_msg(C.errno))
|
|
|
|
}
|
|
|
|
if C._fseeki64(f.cfile, 0, C.SEEK_END) != 0 {
|
|
|
|
return error(posix_get_error_msg(C.errno))
|
|
|
|
}
|
|
|
|
} $else {
|
|
|
|
if C.fseeko(f.cfile, pos, C.SEEK_SET) != 0 {
|
|
|
|
return error(posix_get_error_msg(C.errno))
|
|
|
|
}
|
|
|
|
nbytes = int(C.fwrite(t, 1, tsize, f.cfile))
|
|
|
|
if C.errno != 0 {
|
|
|
|
return error(posix_get_error_msg(C.errno))
|
|
|
|
}
|
|
|
|
if C.fseeko(f.cfile, 0, C.SEEK_END) != 0 {
|
|
|
|
return error(posix_get_error_msg(C.errno))
|
|
|
|
}
|
|
|
|
}
|
2021-03-10 17:45:12 +01:00
|
|
|
}
|
2021-03-30 11:40:23 +02:00
|
|
|
$if x32 {
|
|
|
|
if C.fseek(f.cfile, pos, C.SEEK_SET) != 0 {
|
|
|
|
return error(posix_get_error_msg(C.errno))
|
|
|
|
}
|
|
|
|
nbytes = int(C.fwrite(t, 1, tsize, f.cfile))
|
|
|
|
if C.errno != 0 {
|
|
|
|
return error(posix_get_error_msg(C.errno))
|
|
|
|
}
|
|
|
|
if C.fseek(f.cfile, 0, C.SEEK_END) != 0 {
|
|
|
|
return error(posix_get_error_msg(C.errno))
|
|
|
|
}
|
2021-03-10 17:45:12 +01:00
|
|
|
}
|
2021-03-30 11:40:23 +02:00
|
|
|
|
2021-03-10 17:45:12 +01:00
|
|
|
if nbytes != tsize {
|
|
|
|
return error_with_code('incomplete struct write', nbytes)
|
|
|
|
}
|
|
|
|
}
|