From 8f98f1db9e0a4e91e08d6aaf318712427d2ba11a Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sun, 14 Aug 2022 09:50:05 +0300 Subject: [PATCH] os: fix `os.open_file('text.txt', 'wb', 0o666)`, add test (#15420) --- vlib/os/const_nix.c.v | 23 +++++++++---------- vlib/os/file.c.v | 52 ++++++++++++++++++++++++++++++------------- vlib/os/file_test.v | 15 ++++++++++++- 3 files changed, 62 insertions(+), 28 deletions(-) diff --git a/vlib/os/const_nix.c.v b/vlib/os/const_nix.c.v index 275df70158..7f4c378a67 100644 --- a/vlib/os/const_nix.c.v +++ b/vlib/os/const_nix.c.v @@ -1,16 +1,15 @@ module os -// File modes const ( - o_rdonly = 0o00000000 // open the file read-only. - o_wronly = 0o00000001 // open the file write-only. - o_rdwr = 0o00000002 // open the file read-write. - o_binary = 0o00000000 // input and output is not translated; the default on unix - o_create = 0o00000100 // create a new file if none exists. - o_excl = 0o00000200 // used with o_create, file must not exist. - o_noctty = 0o00000400 // if file is terminal, don't make it the controller terminal - o_trunc = 0o00001000 // truncate regular writable file when opened. - o_append = 0o00002000 // append data to the file when writing. - o_nonblock = 0o00004000 // prevents blocking when opening files - o_sync = 0o04010000 // open for synchronous I/O. + o_binary = 0 // input and output is not translated; the default on unix + o_rdonly = C.O_RDONLY // open the file read-only. + o_wronly = C.O_WRONLY // open the file write-only. + o_rdwr = C.O_RDWR // open the file read-write. + o_create = C.O_CREAT // create a new file if none exists. + o_excl = C.O_EXCL // used with o_create, file must not exist. + o_noctty = C.O_NOCTTY // if file is terminal, don't make it the controller terminal + o_trunc = C.O_TRUNC // truncate regular writable file when opened. + o_append = C.O_APPEND // append data to the file when writing. + o_nonblock = C.O_NONBLOCK // prevents blocking when opening files + o_sync = C.O_SYNC // open for synchronous I/O. ) diff --git a/vlib/os/file.c.v b/vlib/os/file.c.v index 7ed7897c4b..3b659c897a 100644 --- a/vlib/os/file.c.v +++ b/vlib/os/file.c.v @@ -54,28 +54,41 @@ fn fix_windows_path(path string) string { // 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 + mut seek_to_end := false 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 } + `w` { + flags |= o_create | o_trunc | o_wronly + } + `a` { + flags |= o_create | o_append | o_wronly + seek_to_end = true + } + `r` { + flags |= o_rdonly + } + `b` { + flags |= o_binary + } + `s` { + flags |= o_sync + } + `n` { + flags |= o_nonblock + } + `c` { + flags |= o_noctty + } + `+` { + flags &= ~o_wronly + 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] @@ -92,10 +105,19 @@ pub fn open_file(path string, mode string, options ...int) ?File { if fd == -1 { return error(posix_get_error_msg(C.errno)) } - cfile := C.fdopen(fd, &char(mode.str)) + fdopen_mode := mode.replace('b', '') + cfile := C.fdopen(fd, &char(fdopen_mode.str)) if isnil(cfile) { return error('Failed to open or create file "$path"') } + if seek_to_end { + // ensure appending will work, even on bsd/macos systems: + $if windows { + C._fseeki64(cfile, 0, C.SEEK_END) + } $else { + C.fseeko(cfile, 0, C.SEEK_END) + } + } return File{ cfile: cfile fd: fd diff --git a/vlib/os/file_test.v b/vlib/os/file_test.v index d1392cd6c2..9e76e56604 100644 --- a/vlib/os/file_test.v +++ b/vlib/os/file_test.v @@ -388,7 +388,7 @@ fn test_reopen() ? { f2.reopen(tfile1, 'r')? assert !f2.eof() - z := f2.read(mut line_buffer)? + z := f2.read(mut line_buffer) or { panic(err) } assert f2.eof() assert z > 0 content := line_buffer#[..z].bytestr() @@ -406,3 +406,16 @@ fn test_eof() ? { f.read_bytes(100) assert f.eof() } + +fn test_open_file_wb_ab() ? { + os.rm(tfile) or {} + mut wfile := os.open_file('text.txt', 'wb', 0o666)? + wfile.write_string('hello')? + wfile.close() + assert os.read_file('text.txt')? == 'hello' + // + mut afile := os.open_file('text.txt', 'ab', 0o666)? + afile.write_string('hello')? + afile.close() + assert os.read_file('text.txt')? == 'hellohello' +}