fuzion/sys/fileio.fz
# This file is part of the Fuzion language implementation.
#
# The Fuzion language implementation is free software: you can redistribute it
# and/or modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, version 3 of the License.
#
# The Fuzion language implementation is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with The
# Fuzion language implementation. If not, see <https://www.gnu.org/licenses/>.
# -----------------------------------------------------------------------
#
# Tokiwa Software GmbH, Germany
#
# Source code of Fuzion standard library feature fuzion.sys.fileio
#
# Author: Wael Youssfi (wael.youssfi@tokiwa.software)
#
# -----------------------------------------------------------------------
# fuzion.sys.fileio -- fileio presents basic features to handle File I/O operations
#
module fileio is
# reads n bytes of a file opened as fd
#
# in case the outcome is an array, it may be shorter than n. this means the end of file
# has been reached.
#
module read(fd i64, n u64) outcome (array u8) =>
len := n.as_i32
arr := fuzion.sys.internal_array_init u8 len
res := read fd arr.data len
if res < -1
error "unspecified read error: {res}"
else if res < 0
[]
else
buf := arr.as_array
(buf.slice 0 res).as_array
# intrinsic that fills an array u8 with the file bytes that is represented by the file descriptor
#
# returns:
# 0 or greater on success
# -1 at end of file
# -2 for any other error
#
module read(
# the file descriptor
fd i64,
# the internal array data representing the container for the bytes to be read from the file
file_array fuzion.sys.Pointer,
# the length of the array that represents the file bytes
file_array_length i32) i32 => intrinsic
# retrieves the file size in bytes and returns an outcome of error in case of an error
#
private get_file_size(
# the (relative or absolute) file name, using platform specific path separators
path String) outcome i64 =>
md := fuzion.sys.internal_array_init i64 4
match stats (fuzion.sys.c_string path) md.data
TRUE => md.as_array[0]
FALSE => error "error getting file size"
# writes the content of an array of bytes to a file opened as fd
#
# this might overwrite parts or all of an existing file.
#
module write(fd i64, content array u8) outcome unit =>
res := write fd content.internal_array.data content.length
if res = 0
unit
else
error "write error: {res}"
# intrinsic to write bytes (internal array data) in a file using the file descriptor
# returns 0 in case of success, -1 in case of failure
#
private write(
# the file descriptor
fd i64,
# the internal array data representing the content bytes to insert in file
content fuzion.sys.Pointer,
# the length of the internal array representing the content
content_length i32) i32 => intrinsic
# deletes the file/dir found in the path
# returns unit as outcome in case of successful deletion and error in case of failure
# if the targeted dir has content, then the return value will be error and the deletion will not take place
#
module delete(
# the (relative or absolute) file name, using platform specific path separators
path String) outcome unit =>
arr := fuzion.sys.c_string path
if delete arr unit
unit
else
error "an error occurred while performing the delete operation on the following file/dir: \"$path\""
# intrinsic that deletes the file/dir represented by the path returning TRUE in case of success
# and FALSE in case of failure
#
private delete(
# the internal array data representing the file/dir path in bytes
path fuzion.sys.Pointer,
# dummy parameter to avoid duplicate feature name
_ unit) bool => intrinsic_constructor
# moves file/dir from an old path to a the new path
# can rename the file/dir as well by changing the name of the old file/dir to a new name in the new_path
# returns a unit type as outcome in case of success and error in case of failure
#
module move(
# the old (relative or absolute) file name, using platform specific path separators
old_path String,
# the new (relative or absolute) file name, using platform specific path separators
new_path String) outcome unit =>
arr0 := fuzion.sys.c_string old_path
arr1 := fuzion.sys.c_string new_path
if move arr0 arr1 unit
unit
else
error "an error occurred while performing the following move operation: \"$old_path\" --> \"$new_path\""
# intrinsic that returns TRUE in case the move was successful and FALSE in case not
#
private move(
# the internal array data representing the old file/dir path in bytes
old_path fuzion.sys.Pointer,
# the internal array data representing the new file/dir path in bytes
new_path fuzion.sys.Pointer,
# dummy parameter for overloading
_ unit) bool => intrinsic_constructor
# creates a directory using the specified path
# parent directories in the path should exist otherwise, no creation will take place and an error will be the outcome
#
module create_dir(
# the (relative or absolute) dir name, using platform specific path separators
path String) outcome unit =>
arr := fuzion.sys.c_string path
if create_dir arr unit
unit
else
error "an error occurred while creating the following directory: \"$path\""
# intrinsic that returns TRUE in case of success or FALSE in case of failure during dir creation
#
private create_dir(
# the internal array data representing the dir path in bytes
path fuzion.sys.Pointer,
# dummy parameter to enable overloading
_ unit) bool => intrinsic_constructor
# intrinsic that fills an array with some metadata of the file/dir provided by the path
# returns TRUE in case the operation was successful and FALSE in case of failure
# in case the path refers to a symbolic link it resolves it and returns info about the actual file
#
# in case an error is returned (the result of this feature is false), then the size field of
# the meta_data array will contain the errno for the stat call.
#
module stats(
# the internal array data representing the file/dir path in bytes
path fuzion.sys.Pointer,
# the internal array data representing the metadata fields [size in bytes, creation_time in seconds, regular file? 1 : 0, dir? 1 : 0]
meta_data fuzion.sys.Pointer) bool => intrinsic_constructor
# intrinsic that fills an array with some metadata of the file/dir provided by the path
# returns TRUE in case the operation was successful and FALSE in case of failure
# in case the path refers to a symbolic link it does not attempt to follow it and returns info about the link itself
#
# in case an error is returned (the result of this feature is false), then the size field of
# the meta_data array will contain the errno for the lstat call.
#
module lstats(path fuzion.sys.Pointer, meta_data fuzion.sys.Pointer) bool => intrinsic # NYI behaves the same as stats in the interpreter
# Opens an IO source using a Fuzion Pointer as path and an i8 flag to represent the opening method (Read: 0, Write: 1, Append: 2)
# returns outcome i64 representing the file descriptor in success
# returns an error in failure
#
module open(
# a Fuzion Object represention the path for the source to be opened
path String,
# a flag to speicify the open method (Read: 0, Write: 1, Append: 2)
flag i8) outcome i64 =>
or := fuzion.sys.internal_array_init i64 2 # [file descriptor, error number]
open (fuzion.sys.c_string path) or.data flag
open_results := or.as_array
if open_results[1] = i64 0
open_results[0]
else
error "error number: {open_results[1]}"
# intrinsic that fills a Fuzion object with the file descriptor and the error number from C back-end/ -1 in the interpreter
# after opening the source represented by the path parameter
#
private open(
# a Fuzion Pointer represention the path for the source to be opened
path fuzion.sys.Pointer,
# open_results[file descriptor, error number] as a Fuzion Pointer
open_results fuzion.sys.Pointer,
# opening flag (Read: 0, Write: 1, Append: 2)
flag i8) unit => intrinsic
# Closes an IO source using an i64 representing the source handler (file descriptor)
# returns outcome unit in case of success and an error in case of failure
#
module close(
# file descriptor
fd i64) outcome unit =>
closing_result := close fd unit
if closing_result = i8 0
unit
else
error "error number: $closing_result"
# intrinsic that takes an i64 value that represents the file descriptor and closes the stream
# returns an i8 to represent the result of the operation
# 0 in case no errors occurred and the error number in case the operation failed in the C back-end/ -1 in the interpreter
#
private close(
# file descriptor
fd i64,
# dummy variable to enable overload
_ unit) i8 => intrinsic
# seek offset in the stream represented by fd
# returns an outcome i64 that represents the new offset
# returns an error in case of failure
#
module seek(
# file descriptor
fd i64,
# the offset to seek from the beginning of this file
offset i64) outcome i64 =>
iarr := fuzion.sys.internal_array_init i64 2
seek fd offset iarr.data
arr := iarr.as_array
if arr[1] = i64 0
arr[0]
else
error "error number: {arr[1]}"
# intrinsic to set the file-pointer offset at which the next read or write occurs
# the offset is measured from the beginning of the file indicated by the file descriptor
# and fills a Fuzion object with the new offset
# and the error number from the C back-end/ -1 in the interpreter
#
private seek(
# file descriptor
fd i64,
# the offset to seek from the beginning of this file
offset i64,
# Array data [new file position, error number]
seek_results fuzion.sys.Pointer) unit => intrinsic
# returns the current file-pointer offset as an outcome i64,
# the offset is measured from the beginning of the file indicated by the file descriptor
# returns the current offset in success and error in failure
#
module file_position(
# file descriptor
fd i64) outcome i64 =>
iarr := fuzion.sys.internal_array_init i64 2
file_position fd iarr.data
arr := iarr.as_array
if arr[1] = i64 0
arr[0]
else
error "error number: {arr[1]}"
# intrinsic that fills a Fuzion object with the current file stream position
# and the error number from C back-end/ -1 in the interpreter
#
private file_position(
# file descriptor
fd i64,
# Array data [new file position, error number]
position_results fuzion.sys.Pointer) unit => intrinsic
# memory map a file
#
# res[0]: 0 on success, -1 on error
#
# return: allocated memory (same as fuzion.sys.internal_array_init.alloc)
#
# note: offset+size must not exceed file size.
# note: returning allocated memory - instead of error code - simplifies handling in DFA.
#
module mmap(fd i64, offset, size i64, res fuzion.sys.Pointer) Pointer => intrinsic_constructor
# close a memory mapped file
#
# return: 0 on success, -1 on error
#
module munmap(address fuzion.sys.Pointer, size i64) i32 => intrinsic
# get a byte from a given mapped buffer
#
module mapped_buffer_get
(# memory address obtained by `mmap`
m fuzion.sys.Pointer,
# index of the byte
i i64
) u8 => intrinsic_constructor
# set a byte of a given mapped buffer to a new value
#
module mapped_buffer_set
(# memory address obtained by `mmap`
m fuzion.sys.Pointer,
# index of the byte
i i64,
# new value to be written at m[i]
o u8
) unit => intrinsic
# flush user-space buffers of file descriptor
#
# return: 0 on success everything else is an error
#
module flush(fd i64) i32 => intrinsic
# open the directory given by path
#
# accepts as arguments a path (processed by fuzion.sys.c_string) and a pointer to
# an array of two i64.
#
# after calling, the array will contain an integer that is intended to be used as an
# opaque reference to the opened directory in its first field, and an integer that is
# not 0 in case of an error in its second field.
#
module open_dir(path fuzion.sys.Pointer, open_results fuzion.sys.Pointer) unit => intrinsic
# open the directory given by path
#
# returns an integer that is intended to be used as an opaque reference
# to the opened directory, or an error if one was encountered.
#
module open_dir(path String) outcome i64 =>
or := fuzion.sys.internal_array_init i64 2 # [file descriptor, error number]
open_dir (fuzion.sys.c_string path) or.data
open_results := or.as_array
if open_results[1] = i64 0
open_results[0]
else
error "error number: {open_results[1]}"
# reads the next entry of the directory given by opaque integer reference
# to an open directory
#
# returns the name of the next entry, or an error string on failure.
#
module read_dir(ptr i64) String => intrinsic
# checks whether there is a next entry in the directory given by opaque
# integer reference to an open directory
#
# true if the next call to read_dir should find a new entry, if false then
# the next call to read_dir will return an error string.
#
module read_dir_has_next(ptr i64) bool => intrinsic
# closes the directory given by opaque integer reference to an open
# directory
#
# returns 0 on success, something else on failure.
#
module close_dir(ptr i64) i64 => intrinsic