X-Git-Url: http://nitlanguage.org diff --git a/lib/standard/file.nit b/lib/standard/file.nit index fb89882..2f93d4e 100644 --- a/lib/standard/file.nit +++ b/lib/standard/file.nit @@ -27,6 +27,8 @@ in "C Header" `{ #include #include #include + #include + #include `} # File Abstract Stream @@ -42,6 +44,21 @@ abstract class FStream # File descriptor of this file fun fd: Int do return _file.fileno + + # Sets the buffering mode for the current FStream + # + # If the buf_size is <= 0, its value will be 512 by default + # + # The mode is any of the buffer_mode enumeration in `Sys`: + # - buffer_mode_full + # - buffer_mode_line + # - buffer_mode_none + fun set_buffering_mode(buf_size, mode: Int) do + if buf_size <= 0 then buf_size = 512 + if _file.set_buffering_type(buf_size, mode) != 0 then + last_error = new IOError("Error while changing buffering type for FStream, returned error {sys.errno.strerror}") + end + end end # File input stream @@ -55,8 +72,14 @@ class IFStream # The original path is reused, therefore the reopened file can be a different file. fun reopen do - if not eof then close + if not eof and not _file.address_is_null then close + last_error = null _file = new NativeFile.io_open_read(path.to_cstring) + if _file.address_is_null then + last_error = new IOError("Error: Opening file at '{path.as(not null)}' failed with '{sys.errno.strerror}'") + end_reached = true + return + end end_reached = false _buffer_pos = 0 _buffer.clear @@ -64,6 +87,7 @@ class IFStream redef fun close do + if _file.address_is_null then return var i = _file.io_close _buffer.clear end_reached = true @@ -79,7 +103,7 @@ class IFStream _buffer.length = nb _buffer_pos = 0 end - + # End of file? redef var end_reached: Bool = false @@ -89,11 +113,21 @@ class IFStream self.path = path prepare_buffer(10) _file = new NativeFile.io_open_read(path.to_cstring) - assert not _file.address_is_null else - print "Error: Opening file at '{path}' failed with '{sys.errno.strerror}'" + if _file.address_is_null then + last_error = new IOError("Error: Opening file at '{path}' failed with '{sys.errno.strerror}'") + end_reached = true end end + init from_fd(fd: Int) do + self.path = "" + prepare_buffer(1) + _file = fd.fd_to_stream(read_only) + if _file.address_is_null then + last_error = new IOError("Error: Converting fd {fd} to stream failed with '{sys.errno.strerror}'") + end_reached = true + end + end end # File output stream @@ -103,30 +137,52 @@ class OFStream redef fun write(s) do - assert _is_writable + if last_error != null then return + if not _is_writable then + last_error = new IOError("Cannot write to non-writable stream") + return + end if s isa FlatText then write_native(s.to_cstring, s.length) else for i in s.substrings do write_native(i.to_cstring, i.length) end + _file.flush end redef fun close do + if _file.address_is_null then + if last_error != null then return + last_error = new IOError("Cannot close unopened write stream") + _is_writable = false + return + end var i = _file.io_close + if i != 0 then + last_error = new IOError("Close failed due to error {sys.errno.strerror}") + end _is_writable = false end - redef var is_writable = false # Write `len` bytes from `native`. private fun write_native(native: NativeString, len: Int) do - assert _is_writable + if last_error != null then return + if not _is_writable then + last_error = new IOError("Cannot write to non-writable stream") + return + end + if _file.address_is_null then + last_error = new IOError("Writing on a null stream") + _is_writable = false + return + end var err = _file.io_write(native, len) if err != len then # Big problem - printn("Problem in writing : ", err, " ", len, "\n") + last_error = new IOError("Problem in writing : {err} {len} \n") end end @@ -134,14 +190,43 @@ class OFStream init open(path: String) do _file = new NativeFile.io_open_write(path.to_cstring) - assert not _file.address_is_null else - print "Error: Opening file at '{path}' failed with '{sys.errno.strerror}'" - end self.path = path _is_writable = true + if _file.address_is_null then + last_error = new IOError("Error: Opening file at '{path}' failed with '{sys.errno.strerror}'") + is_writable = false + end + end + + # Creates a new File stream from a file descriptor + init from_fd(fd: Int) do + self.path = "" + _file = fd.fd_to_stream(wipe_write) + _is_writable = true + if _file.address_is_null then + last_error = new IOError("Error: Opening stream from file descriptor {fd} failed with '{sys.errno.strerror}'") + _is_writable = false + end end end +redef class Int + # Creates a file stream from a file descriptor `fd` using the file access `mode`. + # + # NOTE: The `mode` specified must be compatible with the one used in the file descriptor. + private fun fd_to_stream(mode: NativeString): NativeFile is extern "file_int_fdtostream" +end + +# Constant for read-only file streams +private fun read_only: NativeString do return "r".to_cstring + +# Constant for write-only file streams +# +# If a stream is opened on a file with this method, +# it will wipe the previous file if any. +# Else, it will create the file. +private fun wipe_write: NativeString do return "w".to_cstring + ############################################################################### class Stdin @@ -613,6 +698,10 @@ private extern class NativeFile `{ FILE* `} fun io_close: Int is extern "file_NativeFile_NativeFile_io_close_0" fun file_stat: FileStat is extern "file_NativeFile_NativeFile_file_stat_0" fun fileno: Int `{ return fileno(recv); `} + # Flushes the buffer, forcing the write operation + fun flush: Int is extern "fflush" + # Used to specify how the buffering will be handled for the current stream. + fun set_buffering_type(buf_length: Int, mode: Int): Int is extern "file_NativeFile_NativeFile_set_buffering_type_0" new io_open_read(path: NativeString) is extern "file_NativeFileCapable_NativeFileCapable_io_open_read_1" new io_open_write(path: NativeString) is extern "file_NativeFileCapable_NativeFileCapable_io_open_write_1" @@ -632,6 +721,89 @@ redef class Sys # Standard output for errors var stderr: OStream = new Stderr is protected writable + # Enumeration for buffer mode full (flushes when buffer is full) + fun buffer_mode_full: Int is extern "file_Sys_Sys_buffer_mode_full_0" + # Enumeration for buffer mode line (flushes when a `\n` is encountered) + fun buffer_mode_line: Int is extern "file_Sys_Sys_buffer_mode_line_0" + # Enumeration for buffer mode none (flushes ASAP when something is written) + fun buffer_mode_none: Int is extern "file_Sys_Sys_buffer_mode_none_0" + + # returns first available stream to read or write to + # return null on interruption (possibly a signal) + protected fun poll( streams : Sequence[FStream] ) : nullable FStream + do + var in_fds = new Array[Int] + var out_fds = new Array[Int] + var fd_to_stream = new HashMap[Int,FStream] + for s in streams do + var fd = s.fd + if s isa IFStream then in_fds.add( fd ) + if s isa OFStream then out_fds.add( fd ) + + fd_to_stream[fd] = s + end + + var polled_fd = intern_poll( in_fds, out_fds ) + + if polled_fd == null then + return null + else + return fd_to_stream[polled_fd] + end + end + + private fun intern_poll(in_fds: Array[Int], out_fds: Array[Int]) : nullable Int is extern import Array[Int].length, Array[Int].[], Int.as(nullable Int) `{ + int in_len, out_len, total_len; + struct pollfd *c_fds; + sigset_t sigmask; + int i; + int first_polled_fd = -1; + int result; + + in_len = Array_of_Int_length( in_fds ); + out_len = Array_of_Int_length( out_fds ); + total_len = in_len + out_len; + c_fds = malloc( sizeof(struct pollfd) * total_len ); + + /* input streams */ + for ( i=0; i 0 ) { + /* analyse results */ + for ( i=0; i