From ae3d84df6b1989f01a9387933f87222ccad18e36 Mon Sep 17 00:00:00 2001 From: KJ Lawrence Date: Tue, 21 Jan 2020 10:58:47 -0500 Subject: [PATCH] os: add open_file function --- vlib/os/const.v | 12 +------ vlib/os/const_nix.v | 15 +++++++++ vlib/os/const_windows.v | 13 ++++++++ vlib/os/os.v | 71 ++++++++++++++++++++++++++++++++++++++++- vlib/os/os_nix.v | 37 +++++++++------------ vlib/os/os_test.v | 21 ++++++++++++ 6 files changed, 135 insertions(+), 34 deletions(-) create mode 100644 vlib/os/const_nix.v diff --git a/vlib/os/const.v b/vlib/os/const.v index df82478df6..5a8fff2744 100644 --- a/vlib/os/const.v +++ b/vlib/os/const.v @@ -1,16 +1,6 @@ module os // (Must be realized in Syscall) (Must be specified) -// File modes. -const ( - O_RDONLY = 1 // open the file read-only. - O_WRONLY = 2 // open the file write-only. - O_RDWR = 3 // open the file read-write. - O_APPEND = 8 // append data to the file when writing. - O_CREATE = 16 // create a new file if none exists. - O_EXCL = 32 // used with O_CREATE, file must not exist. - O_SYNC = 64 // open for synchronous I/O. - O_TRUNC = 128 // truncate regular writable file when opened. -) + // ref: http://www.ccfit.nsu.ru/~deviv/courses/unix/unix/ng7c229.html const ( S_IFMT = 0xF000 // type of file diff --git a/vlib/os/const_nix.v b/vlib/os/const_nix.v new file mode 100644 index 0000000000..adc63078bf --- /dev/null +++ b/vlib/os/const_nix.v @@ -0,0 +1,15 @@ +module os + +// File modes +const ( + O_RDONLY = 000000000 // open the file read-only. + O_WRONLY = 000000001 // open the file write-only. + O_RDWR = 000000002 // open the file read-write. + O_CREATE = 000000100 // create a new file if none exists. + O_EXCL = 000000200 // used with O_CREATE, file must not exist. + O_NOCTTY = 000000400 // if file is terminal, don't make it the controller terminal + O_TRUNC = 000001000 // truncate regular writable file when opened. + O_APPEND = 000002000 // append data to the file when writing. + O_NONBLOCK = 000004000 // prevents blocking when opening files + O_SYNC = 000010000 // open for synchronous I/O. +) \ No newline at end of file diff --git a/vlib/os/const_windows.v b/vlib/os/const_windows.v index e8f0460aac..d919e45447 100644 --- a/vlib/os/const_windows.v +++ b/vlib/os/const_windows.v @@ -89,3 +89,16 @@ const ( ENABLE_LVB_GRID_WORLDWIDE = 0x0010 ) +// File modes +const ( + O_RDONLY = 0 // open the file read-only. + O_WRONLY = 1 // open the file write-only. + O_RDWR = 2 // open the file read-write. + O_APPEND = 0x0008 // append data to the file when writing. + O_CREATE = 0x0100 // create a new file if none exists. + O_TRUNC = 0x0200 // truncate regular writable file when opened. + O_EXCL = 0x0400 // used with O_CREATE, file must not exist. + O_SYNC = 0 // open for synchronous I/O (ignored on Windows) + O_NOCTTY = 0 // make file non-controlling tty (ignored on Windows) + O_NONBLOCK = 0 // don't block on opening file (ignored on Windows) +) diff --git a/vlib/os/os.v b/vlib/os/os.v index 082e6b5eff..3722b30b6a 100644 --- a/vlib/os/os.v +++ b/vlib/os/os.v @@ -7,6 +7,7 @@ import filepath #include // #include #include + /* struct dirent { d_ino int @@ -31,6 +32,7 @@ pub const ( pub struct File { cfile voidptr // Using void* instead of FILE* +pub: fd int mut: opened bool @@ -71,6 +73,12 @@ fn C.getenv(byteptr) &char fn C.sigaction(int, voidptr, int) +fn C.open(charptr, int, int) int + + +fn C.fdopen(int, string) voidptr + + pub fn (f File) is_opened() bool { return f.opened } @@ -292,6 +300,58 @@ pub fn open_append(path string) ?File { 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 } + `a` { flags |= O_CREATE | O_APPEND } + `s` { flags |= O_SYNC } + `n` { flags |= O_NONBLOCK } + `c` { flags |= O_NOCTTY } + `+` { flags |= O_RDWR } + else {} + } + } + + mut permission := 0666 + if options.len > 0 { + permission = options[0] + } + + $if windows { + if permission < 0600 { + 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 + opened: true + } +} + /* pub fn (f mut File) write_bytes_at(data voidptr, size, pos int) { $if linux { @@ -344,6 +404,15 @@ fn posix_wait4_to_exit_status(waitret int) (int,bool) { } } +// posix_get_error_msg return error code representation in string. +pub fn posix_get_error_msg(code int) string { + ptr_text := C.strerror(code) // voidptr? + if ptr_text == 0 { + return '' + } + return tos3(ptr_text) +} + fn vpclose(f voidptr) int { $if windows { return C._pclose(f) @@ -459,7 +528,7 @@ pub fn sigint_to_signal_name(si int) string { return 'SIGBUS' } else {} - } + } } return 'unknown' } diff --git a/vlib/os/os_nix.v b/vlib/os/os_nix.v index 2035abb73c..1171399b39 100644 --- a/vlib/os/os_nix.v +++ b/vlib/os/os_nix.v @@ -2,6 +2,8 @@ module os #include #include +#include + pub const ( path_separator = '/' ) @@ -14,7 +16,6 @@ const ( fn C.symlink(charptr, charptr) int - pub fn init_os_args(argc int, argv &byteptr) []string { mut args := []string for i in 0 .. argc { @@ -23,15 +24,6 @@ pub fn init_os_args(argc int, argv &byteptr) []string { return args } -// get_error_msg return error code representation in string. -pub fn get_error_msg(code int) string { - ptr_text := C.strerror(code) // voidptr? - if ptr_text == 0 { - return '' - } - return tos3(ptr_text) -} - pub fn ls(path string) ?[]string { mut res := []string dir := C.opendir(path.str) @@ -68,9 +60,8 @@ pub fn is_dir(path string) bool { } */ - +// open opens a file at the specified and returns back a read-only `File` object pub fn open(path string) ?File { - mut file := File{} /* $if linux { $if !android { @@ -85,9 +76,8 @@ pub fn open(path string) ?File { } } */ - cpath := path.str - file = File{ - cfile: C.fopen(charptr(cpath), 'rb') + file := File{ + cfile: C.fopen(charptr(path.str), 'rb') opened: true } if isnil(file.cfile) { @@ -96,10 +86,8 @@ pub fn open(path string) ?File { return file } -// create creates a file at a specified location and returns a writable `File` object. +// create creates or opens a file at a specified location and returns a write-only `File` object pub fn create(path string) ?File { - mut fd := 0 - mut file := File{} /* // NB: android/termux/bionic is also a kind of linux, // but linux syscalls there sometimes fail, @@ -123,7 +111,7 @@ pub fn create(path string) ?File { } } */ - file = File{ + file := File{ cfile: C.fopen(charptr(path.str), 'wb') opened: true } @@ -187,7 +175,7 @@ pub fn mkdir(path string) ?bool { $if !android { ret := C.syscall(sys_mkdir, apath.str, 511) if ret == -1 { - return error(get_error_msg(C.errno)) + return error(posix_get_error_msg(C.errno)) } return true } @@ -195,7 +183,7 @@ pub fn mkdir(path string) ?bool { */ r := C.mkdir(apath.str, 511) if r == -1 { - return error(get_error_msg(C.errno)) + return error(posix_get_error_msg(C.errno)) } return true } @@ -231,7 +219,12 @@ pub fn symlink(origin, target string) ?bool { if res == 0 { return true } - return error(get_error_msg(C.errno)) + return error(posix_get_error_msg(C.errno)) +} + +// get_error_msg return error code representation in string. +pub fn get_error_msg(code int) string { + return posix_get_error_msg(code) } // convert any value to []byte (LittleEndian) and write it diff --git a/vlib/os/os_test.v b/vlib/os/os_test.v index 250f39e1b3..743593785f 100644 --- a/vlib/os/os_test.v +++ b/vlib/os/os_test.v @@ -24,6 +24,27 @@ fn test_unsetenv() { assert os.getenv('foo') == '' } +fn test_open_file() { + filename := './test1.txt' + hello := 'hello world!' + os.open_file(filename, "r+", 0666) or { + assert err == "No such file or directory" + } + + mut file := os.open_file(filename, "w+", 0666) or { panic(err) } + file.write(hello) + file.close() + + assert hello.len == os.file_size(filename) + + read_hello := os.read_file(filename) or { + panic('error reading file $filename') + } + assert hello == read_hello + + os.rm(filename) +} + fn test_write_and_read_string_to_file() { filename := './test1.txt' hello := 'hello world!'