os: move file methods to vlib/os/file.c.v, document them, add new file.read_struct and file.write_struct and tests
							parent
							
								
									6097045b46
								
							
						
					
					
						commit
						5e0e44eb69
					
				| 
						 | 
					@ -20,7 +20,7 @@ fn read_file(file string, cap int) []string {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn test_file_reader() {
 | 
					fn test_file_reader() {
 | 
				
			||||||
	for cap := 64; cap <= 10000; cap += 256 {
 | 
						for cap := 1; cap <= 10000; cap += 256 {
 | 
				
			||||||
		lines := read_file(@FILE, cap)
 | 
							lines := read_file(@FILE, cap)
 | 
				
			||||||
		assert lines.last() == '// my last line'
 | 
							assert lines.last() == '// my last line'
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										215
									
								
								vlib/os/file.c.v
								
								
								
								
							
							
						
						
									
										215
									
								
								vlib/os/file.c.v
								
								
								
								
							| 
						 | 
					@ -13,8 +13,132 @@ struct FileInfo {
 | 
				
			||||||
	size int
 | 
						size int
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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('/', '\\')
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						fd := C.open(charptr(p.str), flags, permission)
 | 
				
			||||||
 | 
						if fd == -1 {
 | 
				
			||||||
 | 
							return error(posix_get_error_msg(C.errno))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						cfile := C.fdopen(fd, charptr(mode.str))
 | 
				
			||||||
 | 
						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
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// open_stdin - return an os.File for stdin, so that you can use .get_line on it too.
 | 
				
			||||||
 | 
					pub fn open_stdin() File {
 | 
				
			||||||
 | 
						return File{
 | 
				
			||||||
 | 
							fd: 0
 | 
				
			||||||
 | 
							cfile: C.stdin
 | 
				
			||||||
 | 
							is_opened: true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// **************************** Write ops  ***************************
 | 
					// **************************** Write ops  ***************************
 | 
				
			||||||
// write implements the Writer interface
 | 
					// write implements the Writer interface.
 | 
				
			||||||
 | 
					// It returns how many bytes were actually written.
 | 
				
			||||||
pub fn (mut f File) write(buf []byte) ?int {
 | 
					pub fn (mut f File) write(buf []byte) ?int {
 | 
				
			||||||
	if !f.is_opened {
 | 
						if !f.is_opened {
 | 
				
			||||||
		return error('file is not opened')
 | 
							return error('file is not opened')
 | 
				
			||||||
| 
						 | 
					@ -22,8 +146,8 @@ pub fn (mut f File) write(buf []byte) ?int {
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	$if linux {
 | 
						$if linux {
 | 
				
			||||||
		$if !android {
 | 
							$if !android {
 | 
				
			||||||
			C.syscall(sys_write, f.fd, s.str, s.len)
 | 
								res := C.syscall(sys_write, f.fd, s.str, s.len)
 | 
				
			||||||
			return
 | 
								return res
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	*/
 | 
						*/
 | 
				
			||||||
| 
						 | 
					@ -34,6 +158,8 @@ pub fn (mut f File) write(buf []byte) ?int {
 | 
				
			||||||
	return written
 | 
						return written
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// writeln writes the string `s` into the file, and appends a \n character.
 | 
				
			||||||
 | 
					// It returns how many bytes were written, including the \n character.
 | 
				
			||||||
pub fn (mut f File) writeln(s string) ?int {
 | 
					pub fn (mut f File) writeln(s string) ?int {
 | 
				
			||||||
	if !f.is_opened {
 | 
						if !f.is_opened {
 | 
				
			||||||
		return error('file is not opened')
 | 
							return error('file is not opened')
 | 
				
			||||||
| 
						 | 
					@ -59,6 +185,8 @@ pub fn (mut f File) writeln(s string) ?int {
 | 
				
			||||||
	return (written + 1)
 | 
						return (written + 1)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// write_string writes the string `s` into the file
 | 
				
			||||||
 | 
					// It returns how many bytes were actually written.
 | 
				
			||||||
pub fn (mut f File) write_string(s string) ?int {
 | 
					pub fn (mut f File) write_string(s string) ?int {
 | 
				
			||||||
	if !f.is_opened {
 | 
						if !f.is_opened {
 | 
				
			||||||
		return error('file is not opened')
 | 
							return error('file is not opened')
 | 
				
			||||||
| 
						 | 
					@ -71,19 +199,34 @@ pub fn (mut f File) write_string(s string) ?int {
 | 
				
			||||||
	return written
 | 
						return written
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// write_to implements the RandomWriter interface
 | 
					// 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.
 | 
				
			||||||
pub fn (mut f File) write_to(pos int, buf []byte) ?int {
 | 
					pub fn (mut f File) write_to(pos int, buf []byte) ?int {
 | 
				
			||||||
 | 
						if !f.is_opened {
 | 
				
			||||||
 | 
							return error('file is not opened')
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	C.fseek(f.cfile, pos, C.SEEK_SET)
 | 
						C.fseek(f.cfile, pos, C.SEEK_SET)
 | 
				
			||||||
	res := int(C.fwrite(buf.data, 1, buf.len, f.cfile))
 | 
						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)
 | 
						C.fseek(f.cfile, 0, C.SEEK_END)
 | 
				
			||||||
	return res
 | 
						return res
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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.
 | 
				
			||||||
[unsafe]
 | 
					[unsafe]
 | 
				
			||||||
pub fn (mut f File) write_bytes(data voidptr, size int) int {
 | 
					pub fn (mut f File) write_bytes(data voidptr, size int) int {
 | 
				
			||||||
	return int(C.fwrite(data, 1, size, f.cfile))
 | 
						return int(C.fwrite(data, 1, size, f.cfile))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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.
 | 
				
			||||||
[unsafe]
 | 
					[unsafe]
 | 
				
			||||||
pub fn (mut f File) write_bytes_at(data voidptr, size int, pos int) int {
 | 
					pub fn (mut f File) write_bytes_at(data voidptr, size int, pos int) int {
 | 
				
			||||||
	C.fseek(f.cfile, pos, C.SEEK_SET)
 | 
						C.fseek(f.cfile, pos, C.SEEK_SET)
 | 
				
			||||||
| 
						 | 
					@ -93,12 +236,13 @@ pub fn (mut f File) write_bytes_at(data voidptr, size int, pos int) int {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// **************************** Read ops  ***************************
 | 
					// **************************** Read ops  ***************************
 | 
				
			||||||
// read_bytes reads bytes from the beginning of the file
 | 
					// read_bytes reads bytes from the beginning of the file.
 | 
				
			||||||
 | 
					// Utility method, same as .read_bytes_at(size, 0).
 | 
				
			||||||
pub fn (f &File) read_bytes(size int) []byte {
 | 
					pub fn (f &File) read_bytes(size int) []byte {
 | 
				
			||||||
	return f.read_bytes_at(size, 0)
 | 
						return f.read_bytes_at(size, 0)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// read_bytes_at reads bytes at the given position in the file
 | 
					// read_bytes_at reads `size` bytes at the given position in the file.
 | 
				
			||||||
pub fn (f &File) read_bytes_at(size int, pos int) []byte {
 | 
					pub fn (f &File) read_bytes_at(size int, pos int) []byte {
 | 
				
			||||||
	mut arr := []byte{len: size}
 | 
						mut arr := []byte{len: size}
 | 
				
			||||||
	nreadbytes := f.read_bytes_into(pos, mut arr) or {
 | 
						nreadbytes := f.read_bytes_into(pos, mut arr) or {
 | 
				
			||||||
| 
						 | 
					@ -109,8 +253,8 @@ pub fn (f &File) read_bytes_at(size int, pos int) []byte {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// read_bytes_into fills `buf` with bytes at the given position in the file.
 | 
					// read_bytes_into fills `buf` with bytes at the given position in the file.
 | 
				
			||||||
// `buf` must have length greater than zero.
 | 
					// `buf` *must* have length greater than zero.
 | 
				
			||||||
// Returns number of bytes read or an error.
 | 
					// Returns the number of read bytes, or an error.
 | 
				
			||||||
pub fn (f &File) read_bytes_into(pos int, mut buf []byte) ?int {
 | 
					pub fn (f &File) read_bytes_into(pos int, mut buf []byte) ?int {
 | 
				
			||||||
	if buf.len == 0 {
 | 
						if buf.len == 0 {
 | 
				
			||||||
		panic(@FN + ': `buf.len` == 0')
 | 
							panic(@FN + ': `buf.len` == 0')
 | 
				
			||||||
| 
						 | 
					@ -129,7 +273,7 @@ pub fn (f &File) read_bytes_into(pos int, mut buf []byte) ?int {
 | 
				
			||||||
	return nbytes
 | 
						return nbytes
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// read implements the Reader interface
 | 
					// read implements the Reader interface.
 | 
				
			||||||
pub fn (f &File) read(mut buf []byte) ?int {
 | 
					pub fn (f &File) read(mut buf []byte) ?int {
 | 
				
			||||||
	if buf.len == 0 {
 | 
						if buf.len == 0 {
 | 
				
			||||||
		return 0
 | 
							return 0
 | 
				
			||||||
| 
						 | 
					@ -142,7 +286,7 @@ pub fn (f &File) read(mut buf []byte) ?int {
 | 
				
			||||||
	return nbytes
 | 
						return nbytes
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// read_at reads buf.len bytes from pos in the file
 | 
					// read_at reads `buf.len` bytes starting at file byte offset `pos`, in `buf`.
 | 
				
			||||||
pub fn (f &File) read_at(pos int, mut buf []byte) ?int {
 | 
					pub fn (f &File) read_at(pos int, mut buf []byte) ?int {
 | 
				
			||||||
	if buf.len == 0 {
 | 
						if buf.len == 0 {
 | 
				
			||||||
		return 0
 | 
							return 0
 | 
				
			||||||
| 
						 | 
					@ -157,7 +301,7 @@ pub fn (f &File) read_at(pos int, mut buf []byte) ?int {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// **************************** Utility  ops ***********************
 | 
					// **************************** Utility  ops ***********************
 | 
				
			||||||
// flush writes any unwritten data in stream's buffer
 | 
					// flush writes any buffered unwritten data left in the file stream.
 | 
				
			||||||
pub fn (mut f File) flush() {
 | 
					pub fn (mut f File) flush() {
 | 
				
			||||||
	if !f.is_opened {
 | 
						if !f.is_opened {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
| 
						 | 
					@ -165,18 +309,49 @@ pub fn (mut f File) flush() {
 | 
				
			||||||
	C.fflush(f.cfile)
 | 
						C.fflush(f.cfile)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// open_stdin - return an os.File for stdin, so that you can use .get_line on it too.
 | 
					// write_str writes the bytes of a string into a file,
 | 
				
			||||||
pub fn open_stdin() File {
 | 
					// *including* the terminating 0 byte.
 | 
				
			||||||
	return File{
 | 
					 | 
				
			||||||
		fd: 0
 | 
					 | 
				
			||||||
		cfile: C.stdin
 | 
					 | 
				
			||||||
		is_opened: true
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub fn (mut f File) write_str(s string) ? {
 | 
					pub fn (mut f File) write_str(s string) ? {
 | 
				
			||||||
	if !f.is_opened {
 | 
						if !f.is_opened {
 | 
				
			||||||
		return error('file is closed')
 | 
							return error('file is closed')
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	f.write(s.bytes()) ?
 | 
						f.write(s.bytes()) ?
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						C.errno = 0
 | 
				
			||||||
 | 
						nbytes := int(C.fread(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 read', nbytes)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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')
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						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)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,56 @@
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct Point {
 | 
				
			||||||
 | 
						x f64
 | 
				
			||||||
 | 
						y f64
 | 
				
			||||||
 | 
						z f64
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const unit_point = Point{1.0, 1.0, 1.0}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const tfolder = os.join_path(os.temp_dir(), 'os_file_test')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const tfile = os.join_path(tfolder, 'test_file')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const another_point = Point{0.25, 2.25, 6.25}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn testsuite_begin() {
 | 
				
			||||||
 | 
						os.rmdir_all(tfolder) or { }
 | 
				
			||||||
 | 
						assert !os.is_dir(tfolder)
 | 
				
			||||||
 | 
						os.mkdir_all(tfolder) or { panic(err) }
 | 
				
			||||||
 | 
						os.chdir(tfolder)
 | 
				
			||||||
 | 
						assert os.is_dir(tfolder)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn testsuite_end() {
 | 
				
			||||||
 | 
						os.chdir(os.wd_at_startup)
 | 
				
			||||||
 | 
						os.rmdir_all(tfolder) or { panic(err) }
 | 
				
			||||||
 | 
						assert !os.is_dir(tfolder)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn test_write_struct() {
 | 
				
			||||||
 | 
						size_of_point := int(sizeof(Point))
 | 
				
			||||||
 | 
						mut f := os.open_file(tfile, 'w') or { panic(err) }
 | 
				
			||||||
 | 
						f.write_struct(another_point) or { panic(err) }
 | 
				
			||||||
 | 
						f.close()
 | 
				
			||||||
 | 
						x := os.read_file(tfile) or { panic(err) }
 | 
				
			||||||
 | 
						y := unsafe { byteptr(memdup(&another_point, size_of_point)).vstring_with_len(size_of_point) }
 | 
				
			||||||
 | 
						assert x == y
 | 
				
			||||||
 | 
						$if debug {
 | 
				
			||||||
 | 
							eprintln(x.bytes())
 | 
				
			||||||
 | 
							eprintln(y.bytes())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn test_read_struct() {
 | 
				
			||||||
 | 
						mut f := os.open_file(tfile, 'w') or { panic(err) }
 | 
				
			||||||
 | 
						f.write_struct(another_point) or { panic(err) }
 | 
				
			||||||
 | 
						f.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						f = os.open_file(tfile, 'r') or { panic(err) }
 | 
				
			||||||
 | 
						mut p := Point{}
 | 
				
			||||||
 | 
						f.read_struct(mut p) or { panic(err) }
 | 
				
			||||||
 | 
						f.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert p == another_point
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										139
									
								
								vlib/os/os_c.v
								
								
								
								
							
							
						
						
									
										139
									
								
								vlib/os/os_c.v
								
								
								
								
							| 
						 | 
					@ -214,74 +214,6 @@ pub fn fileno(cfile voidptr) int {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// open_append opens `path` file for appending.
 | 
					 | 
				
			||||||
pub fn open_append(path string) ?File {
 | 
					 | 
				
			||||||
	mut file := File{}
 | 
					 | 
				
			||||||
	$if windows {
 | 
					 | 
				
			||||||
		wpath := path.replace('/', '\\').to_wide()
 | 
					 | 
				
			||||||
		mode := 'ab'
 | 
					 | 
				
			||||||
		file = File{
 | 
					 | 
				
			||||||
			cfile: C._wfopen(wpath, mode.to_wide())
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} $else {
 | 
					 | 
				
			||||||
		cpath := path.str
 | 
					 | 
				
			||||||
		file = File{
 | 
					 | 
				
			||||||
			cfile: C.fopen(charptr(cpath), 'ab')
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if isnil(file.cfile) {
 | 
					 | 
				
			||||||
		return error('failed to create(append) file "$path"')
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	file.is_opened = true
 | 
					 | 
				
			||||||
	return file
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// 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 {
 | 
					 | 
				
			||||||
			`r` { flags |= o_rdonly }
 | 
					 | 
				
			||||||
			`w` { flags |= o_create | o_trunc }
 | 
					 | 
				
			||||||
			`b` { flags |= o_binary }
 | 
					 | 
				
			||||||
			`a` { flags |= o_create | o_append }
 | 
					 | 
				
			||||||
			`s` { flags |= o_sync }
 | 
					 | 
				
			||||||
			`n` { flags |= o_nonblock }
 | 
					 | 
				
			||||||
			`c` { flags |= o_noctty }
 | 
					 | 
				
			||||||
			`+` { flags |= o_rdwr }
 | 
					 | 
				
			||||||
			else {}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	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('/', '\\')
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	fd := C.open(charptr(p.str), flags, permission)
 | 
					 | 
				
			||||||
	if fd == -1 {
 | 
					 | 
				
			||||||
		return error(posix_get_error_msg(C.errno))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	cfile := C.fdopen(fd, charptr(mode.str))
 | 
					 | 
				
			||||||
	if isnil(cfile) {
 | 
					 | 
				
			||||||
		return error('Failed to open or create file "$path"')
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return File{
 | 
					 | 
				
			||||||
		cfile: cfile
 | 
					 | 
				
			||||||
		fd: fd
 | 
					 | 
				
			||||||
		is_opened: true
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// vpopen system starts the specified command, waits for it to complete, and returns its code.
 | 
					// vpopen system starts the specified command, waits for it to complete, and returns its code.
 | 
				
			||||||
fn vpopen(path string) voidptr {
 | 
					fn vpopen(path string) voidptr {
 | 
				
			||||||
	// *C.FILE {
 | 
						// *C.FILE {
 | 
				
			||||||
| 
						 | 
					@ -850,63 +782,26 @@ pub fn chmod(path string, mode int) {
 | 
				
			||||||
	C.chmod(charptr(path.str), mode)
 | 
						C.chmod(charptr(path.str), mode)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// open tries to open a file for reading and returns back a read-only `File` object.
 | 
					// open_append opens `path` file for appending.
 | 
				
			||||||
pub fn open(path string) ?File {
 | 
					pub fn open_append(path string) ?File {
 | 
				
			||||||
	/*
 | 
						mut file := File{}
 | 
				
			||||||
	$if linux {
 | 
						$if windows {
 | 
				
			||||||
		$if !android {
 | 
							wpath := path.replace('/', '\\').to_wide()
 | 
				
			||||||
			fd := C.syscall(sys_open, path.str, 511)
 | 
							mode := 'ab'
 | 
				
			||||||
			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{
 | 
							file = File{
 | 
				
			||||||
				fd: fd
 | 
								cfile: C._wfopen(wpath, mode.to_wide())
 | 
				
			||||||
				is_opened: true
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						} $else {
 | 
				
			||||||
 | 
							cpath := path.str
 | 
				
			||||||
 | 
							file = File{
 | 
				
			||||||
 | 
								cfile: C.fopen(charptr(cpath), 'ab')
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if isnil(file.cfile) {
 | 
				
			||||||
 | 
							return error('failed to create(append) file "$path"')
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						file.is_opened = true
 | 
				
			||||||
	return file
 | 
						return file
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	*/
 | 
					 | 
				
			||||||
	cfile := vfopen(path, 'wb') ?
 | 
					 | 
				
			||||||
	fd := fileno(cfile)
 | 
					 | 
				
			||||||
	return File{
 | 
					 | 
				
			||||||
		cfile: cfile
 | 
					 | 
				
			||||||
		fd: fd
 | 
					 | 
				
			||||||
		is_opened: true
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// execvp - loads and executes a new child process, *in place* of the current process.
 | 
					// execvp - loads and executes a new child process, *in place* of the current process.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue