os: add os.open_stdin/0 and os.File.get_line/0

pull/6349/head
Delyan Angelov 2020-09-09 19:37:38 +03:00
parent 67cc515e74
commit ce6d2759f5
3 changed files with 80 additions and 1 deletions

View File

@ -1,5 +1,7 @@
module os
import strings
pub struct File {
cfile voidptr // Using void* instead of FILE*
pub:
@ -88,3 +90,63 @@ pub fn (mut f File) flush() {
}
C.fflush(f.cfile)
}
// 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
}
}
// File.get_line - get a single line from the file. NB: the ending newline is *included*.
pub fn (mut f File) get_line() ?string {
if !f.is_opened {
return error('file is closed')
}
$if !windows {
mut zbuf := byteptr(0)
mut zblen := size_t(0)
mut zx := 0
unsafe {
zx = C.getline(&zbuf, &zblen, f.cfile)
if zx == -1 {
C.free(zbuf)
if C.errno == 0 {
return error('end of file')
}
return error(posix_get_error_msg(C.errno))
}
return zbuf.vstring_with_len(zx)
}
}
//
// using C.fgets is less efficient than f.get_line_getline,
// but is available everywhere, while C.getline does not work
// on windows
//
buf := [4096]byte{}
mut res := strings.new_builder(1024)
mut x := 0
for {
unsafe {
x = C.fgets(charptr(buf), 4096, f.cfile)
}
if x == 0 {
if res.len > 0 {
break
}
return error('end of file')
}
bufbp := byteptr(buf)
mut blen := vstrlen(bufbp)
res.write_bytes(bufbp, blen)
unsafe {
if blen == 0 || bufbp[blen-1] == `\n` || bufbp[blen-1] == `\r` {
break
}
}
}
return res.str()
}

View File

@ -928,7 +928,7 @@ pub fn executable() string {
eprintln('os.executable() failed at reading /proc/curproc/exe to get exe path')
return executable_fallback()
}
return result.vstring_with_len(count)
return unsafe { result.vstring_with_len(count) }
}
$if dragonfly {
mut result := vcalloc(max_path_len)

View File

@ -43,6 +43,23 @@ fn test_open_file() {
os.rm(filename)
}
fn test_file_get_line() {
filename := './fgetline.txt'
os.write_file(filename, 'line 1\nline 2')
mut f := os.open_file(filename, 'r', 0) or {
assert false
return
}
line1 := f.get_line() or { '' }
line2 := f.get_line() or { '' }
f.close()
//
//eprintln('line1: $line1')
//eprintln('line2: $line2')
assert line1 == 'line 1\n'
assert line2 == 'line 2'
}
fn test_create_file() {
filename := './test1.txt'
hello := 'hello world!'