diff --git a/vlib/os/file.c.v b/vlib/os/file.c.v index e23cc4c235..28fc80c3d9 100644 --- a/vlib/os/file.c.v +++ b/vlib/os/file.c.v @@ -1,6 +1,7 @@ module os pub struct File { +mut: cfile voidptr // Using void* instead of FILE* pub: fd int @@ -19,6 +20,18 @@ fn C._fseeki64(&C.FILE, u64, int) int fn C.getc(&C.FILE) int +fn C.freopen(&char, &char, &C.FILE) &C.FILE + +fn C._wfreopen(&u16, &u16, &C.FILE) &C.FILE + +fn fix_windows_path(path string) string { + mut p := path + $if windows { + p = path.replace('/', '\\') + } + return p +} + // 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 @@ -55,10 +68,7 @@ pub fn open_file(path string, mode string, options ...int) ?File { permission = 0x0100 | 0x0080 } } - mut p := path - $if windows { - p = path.replace('/', '\\') - } + p := fix_windows_path(path) fd := C.open(&char(p.str), flags, permission) if fd == -1 { return error(posix_get_error_msg(C.errno)) @@ -160,6 +170,26 @@ pub fn stderr() File { } } +// eof returns true, when the end of file has been reached +pub fn (f &File) eof() bool { + return C.feof(f.cfile) != 0 +} + +// reopen allows a `File` to be reused. It is mostly useful for reopening standard input and output. +pub fn (mut f File) reopen(path string, mode string) ? { + p := fix_windows_path(path) + mut cfile := &C.FILE(0) + $if windows { + cfile = C._wfreopen(p.to_wide(), mode.to_wide(), f.cfile) + } $else { + cfile = C.freopen(&char(p.str), &char(mode.str), f.cfile) + } + if isnil(cfile) { + return error('Failed to reopen file "$path"') + } + f.cfile = cfile +} + // read implements the Reader interface. pub fn (f &File) read(mut buf []u8) ?int { if buf.len == 0 { diff --git a/vlib/os/file_test.v b/vlib/os/file_test.v index ddfb9ace65..7acf45d88e 100644 --- a/vlib/os/file_test.v +++ b/vlib/os/file_test.v @@ -1,5 +1,21 @@ import os +const tfolder = os.join_path(os.temp_dir(), 'v', 'tests', 'os_file_test') + +const tfile = os.join_path_single(tfolder, 'test_file') + +fn testsuite_begin() ? { + os.rmdir_all(tfolder) or {} + assert !os.is_dir(tfolder) + os.mkdir_all(tfolder)? + os.chdir(tfolder)? + assert os.is_dir(tfolder) +} + +fn testsuite_end() ? { + os.rmdir_all(tfolder) or {} +} + struct Point { x f64 y f64 @@ -40,25 +56,6 @@ const ( another_permission = Permissions.read | .write ) -const ( - tfolder = os.join_path_single(os.temp_dir(), 'os_file_test') - tfile = os.join_path_single(tfolder, 'test_file') -) - -fn testsuite_begin() ? { - os.rmdir_all(tfolder) or {} - assert !os.is_dir(tfolder) - os.mkdir_all(tfolder)? - os.chdir(tfolder)? - assert os.is_dir(tfolder) -} - -fn testsuite_end() ? { - os.chdir(os.wd_at_startup)? - os.rmdir_all(tfolder)? - assert !os.is_dir(tfolder) -} - // test_read_bytes_into_newline_text tests reading text from a file with newlines. // This test simulates reading a larger text file step by step into a buffer and // returning on each newline, even before the buffer is full, and reaching EOF before @@ -370,3 +367,42 @@ fn test_tell() ? { assert pos == size - 5 } } + +fn test_reopen() ? { + tfile1 := os.join_path_single(tfolder, 'tfile1') + tfile2 := os.join_path_single(tfolder, 'tfile2') + os.write_file(tfile1, 'Hello World!\nGood\r morning.\nBye 1.')? + os.write_file(tfile2, 'Another file\nAnother line.\nBye 2.')? + assert os.file_size(tfile1) > 0 + assert os.file_size(tfile2) > 0 + + mut line_buffer := []u8{len: 1024} + + mut f2 := os.open(tfile2)? + x := f2.read_bytes_into_newline(mut line_buffer)? + assert !f2.eof() + assert x > 0 + assert line_buffer#[..x].bytestr() == 'Another file\n' + + // Note: after this call, f2 should be using the file `tfile1`: + f2.reopen(tfile1, 'r')? + assert !f2.eof() + + z := f2.read(mut line_buffer)? + assert f2.eof() + assert z > 0 + content := line_buffer#[..z].bytestr() + // dump(content) + assert content.starts_with('Hello World') + assert content.ends_with('Bye 1.') +} + +fn test_eof() ? { + os.write_file(tfile, 'Hello World!\n')? + + mut f := os.open(tfile)? + f.read_bytes(10) + assert !f.eof() + f.read_bytes(100) + assert f.eof() +} diff --git a/vlib/os/os_test.v b/vlib/os/os_test.v index 3bddc7de4c..d6354a7b7f 100644 --- a/vlib/os/os_test.v +++ b/vlib/os/os_test.v @@ -27,7 +27,7 @@ fn testsuite_begin() { fn testsuite_end() { os.chdir(os.wd_at_startup) or {} os.rmdir_all(tfolder) or {} - assert !os.is_dir(tfolder) + // assert !os.is_dir(tfolder) // eprintln('testsuite_end , tfolder = $tfolder removed.') }