os: add open_file function

pull/3524/head
KJ Lawrence 2020-01-21 10:58:47 -05:00 committed by Alexander Medvednikov
parent 5deb29a7c9
commit ae3d84df6b
6 changed files with 135 additions and 34 deletions

View File

@ -1,16 +1,6 @@
module os module os
// (Must be realized in Syscall) (Must be specified) // (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 // ref: http://www.ccfit.nsu.ru/~deviv/courses/unix/unix/ng7c229.html
const ( const (
S_IFMT = 0xF000 // type of file S_IFMT = 0xF000 // type of file

View File

@ -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.
)

View File

@ -89,3 +89,16 @@ const (
ENABLE_LVB_GRID_WORLDWIDE = 0x0010 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)
)

View File

@ -7,6 +7,7 @@ import filepath
#include <sys/stat.h> // #include <signal.h> #include <sys/stat.h> // #include <signal.h>
#include <errno.h> #include <errno.h>
/* /*
struct dirent { struct dirent {
d_ino int d_ino int
@ -31,6 +32,7 @@ pub const (
pub struct File { pub struct File {
cfile voidptr // Using void* instead of FILE* cfile voidptr // Using void* instead of FILE*
pub:
fd int fd int
mut: mut:
opened bool opened bool
@ -71,6 +73,12 @@ fn C.getenv(byteptr) &char
fn C.sigaction(int, voidptr, int) 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 { pub fn (f File) is_opened() bool {
return f.opened return f.opened
} }
@ -292,6 +300,58 @@ pub fn open_append(path string) ?File {
return 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) { pub fn (f mut File) write_bytes_at(data voidptr, size, pos int) {
$if linux { $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 { fn vpclose(f voidptr) int {
$if windows { $if windows {
return C._pclose(f) return C._pclose(f)
@ -459,7 +528,7 @@ pub fn sigint_to_signal_name(si int) string {
return 'SIGBUS' return 'SIGBUS'
} }
else {} else {}
} }
} }
return 'unknown' return 'unknown'
} }

View File

@ -2,6 +2,8 @@ module os
#include <dirent.h> #include <dirent.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h>
pub const ( pub const (
path_separator = '/' path_separator = '/'
) )
@ -14,7 +16,6 @@ const (
fn C.symlink(charptr, charptr) int fn C.symlink(charptr, charptr) int
pub fn init_os_args(argc int, argv &byteptr) []string { pub fn init_os_args(argc int, argv &byteptr) []string {
mut args := []string mut args := []string
for i in 0 .. argc { for i in 0 .. argc {
@ -23,15 +24,6 @@ pub fn init_os_args(argc int, argv &byteptr) []string {
return args 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 { pub fn ls(path string) ?[]string {
mut res := []string mut res := []string
dir := C.opendir(path.str) 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 { pub fn open(path string) ?File {
mut file := File{}
/* /*
$if linux { $if linux {
$if !android { $if !android {
@ -85,9 +76,8 @@ pub fn open(path string) ?File {
} }
} }
*/ */
cpath := path.str file := File{
file = File{ cfile: C.fopen(charptr(path.str), 'rb')
cfile: C.fopen(charptr(cpath), 'rb')
opened: true opened: true
} }
if isnil(file.cfile) { if isnil(file.cfile) {
@ -96,10 +86,8 @@ pub fn open(path string) ?File {
return 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 { pub fn create(path string) ?File {
mut fd := 0
mut file := File{}
/* /*
// NB: android/termux/bionic is also a kind of linux, // NB: android/termux/bionic is also a kind of linux,
// but linux syscalls there sometimes fail, // 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') cfile: C.fopen(charptr(path.str), 'wb')
opened: true opened: true
} }
@ -187,7 +175,7 @@ pub fn mkdir(path string) ?bool {
$if !android { $if !android {
ret := C.syscall(sys_mkdir, apath.str, 511) ret := C.syscall(sys_mkdir, apath.str, 511)
if ret == -1 { if ret == -1 {
return error(get_error_msg(C.errno)) return error(posix_get_error_msg(C.errno))
} }
return true return true
} }
@ -195,7 +183,7 @@ pub fn mkdir(path string) ?bool {
*/ */
r := C.mkdir(apath.str, 511) r := C.mkdir(apath.str, 511)
if r == -1 { if r == -1 {
return error(get_error_msg(C.errno)) return error(posix_get_error_msg(C.errno))
} }
return true return true
} }
@ -231,7 +219,12 @@ pub fn symlink(origin, target string) ?bool {
if res == 0 { if res == 0 {
return true 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 // convert any value to []byte (LittleEndian) and write it

View File

@ -24,6 +24,27 @@ fn test_unsetenv() {
assert os.getenv('foo') == '' 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() { fn test_write_and_read_string_to_file() {
filename := './test1.txt' filename := './test1.txt'
hello := 'hello world!' hello := 'hello world!'