lib/file: remove useless protected for top-level methods
[nit.git] / lib / standard / file.nit
index 102c8e3..956d72f 100644 (file)
@@ -32,9 +32,9 @@ in "C Header" `{
        #include <errno.h>
 `}
 
-# File Abstract Stream
-abstract class FStream
-       super IOS
+# `Stream` used to interact with a File or FileDescriptor
+abstract class FileStream
+       super Stream
        # The path of the file.
        var path: nullable String = null
 
@@ -42,12 +42,37 @@ abstract class FStream
        private var file: nullable NativeFile = null
 
        # The status of a file. see POSIX stat(2).
-       fun file_stat: NativeFileStat do return _file.file_stat
+       #
+       #     var f = new FileReader.open("/etc/issue")
+       #     assert f.file_stat.is_file
+       #
+       # Return null in case of error
+       fun file_stat: nullable FileStat
+       do
+               var stat = _file.file_stat
+               if stat.address_is_null then return null
+               return new FileStat(stat)
+       end
 
        # File descriptor of this file
        fun fd: Int do return _file.fileno
 
-       # Sets the buffering mode for the current FStream
+       redef fun close
+       do
+               if _file == null then return
+               if _file.address_is_null then
+                       if last_error != null then return
+                       last_error = new IOError("Cannot close unopened file")
+                       return
+               end
+               var i = _file.io_close
+               if i != 0 then
+                       last_error = new IOError("Close failed due to error {sys.errno.strerror}")
+               end
+               _file = null
+       end
+
+       # Sets the buffering mode for the current FileStream
        #
        # If the buf_size is <= 0, its value will be 512 by default
        #
@@ -58,20 +83,25 @@ abstract class FStream
        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}")
+                       last_error = new IOError("Error while changing buffering type for FileStream, returned error {sys.errno.strerror}")
                end
        end
 end
 
-# File input stream
-class IFStream
-       super FStream
-       super BufferedIStream
-       super PollableIStream
+# `Stream` that can read from a File
+class FileReader
+       super FileStream
+       super BufferedReader
+       super PollableReader
        # Misc
 
        # Open the same file again.
        # The original path is reused, therefore the reopened file can be a different file.
+       #
+       #     var f = new FileReader.open("/etc/issue")
+       #     var l = f.read_line
+       #     f.reopen
+       #     assert l == f.read_line
        fun reopen
        do
                if not eof and not _file.address_is_null then close
@@ -89,8 +119,7 @@ class IFStream
 
        redef fun close
        do
-               if _file.address_is_null then return
-               var i = _file.io_close
+               super
                _buffer.clear
                end_reached = true
        end
@@ -110,6 +139,16 @@ class IFStream
        redef var end_reached: Bool = false
 
        # Open the file at `path` for reading.
+       #
+       #     var f = new FileReader.open("/etc/issue")
+       #     assert not f.end_reached
+       #     f.close
+       #
+       # In case of error, `last_error` is set
+       #
+       #     f = new FileReader.open("/fail/does not/exist")
+       #     assert f.end_reached
+       #     assert f.last_error != null
        init open(path: String)
        do
                self.path = path
@@ -121,6 +160,9 @@ class IFStream
                end
        end
 
+       # Creates a new File stream from a file descriptor
+       #
+       # This is a low-level method.
        init from_fd(fd: Int) do
                self.path = ""
                prepare_buffer(1)
@@ -132,10 +174,10 @@ class IFStream
        end
 end
 
-# File output stream
-class OFStream
-       super FStream
-       super OStream
+# `Stream` that can write to a File
+class FileWriter
+       super FileStream
+       super Writer
 
        redef fun write(s)
        do
@@ -149,21 +191,11 @@ class OFStream
                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
+               super
                _is_writable = false
        end
        redef var is_writable = false
@@ -232,8 +264,10 @@ private fun wipe_write: NativeString do return "w".to_cstring
 ###############################################################################
 
 # Standard input stream.
+#
+# The class of the default value of `sys.stdin`.
 class Stdin
-       super IFStream
+       super FileReader
 
        init do
                _file = new NativeFile.native_stdin
@@ -245,18 +279,23 @@ class Stdin
 end
 
 # Standard output stream.
+#
+# The class of the default value of `sys.stdout`.
 class Stdout
-       super OFStream
+       super FileWriter
        init do
                _file = new NativeFile.native_stdout
                path = "/dev/stdout"
                _is_writable = true
+               set_buffering_mode(256, sys.buffer_mode_line)
        end
 end
 
 # Standard error stream.
+#
+# The class of the default value of `sys.stderr`.
 class Stderr
-       super OFStream
+       super FileWriter
        init do
                _file = new NativeFile.native_stderr
                path = "/dev/stderr"
@@ -266,11 +305,11 @@ end
 
 ###############################################################################
 
-redef class Streamable
+redef class Writable
        # Like `write_to` but take care of creating the file
        fun write_to_file(filepath: String)
        do
-               var stream = new OFStream.open(filepath)
+               var stream = new FileWriter.open(filepath)
                write_to(stream)
                stream.close
        end
@@ -301,6 +340,10 @@ class Path
        #
        # Returns `null` if there is no file at `self`.
        #
+       #     assert "/etc/".to_path.stat.is_dir
+       #     assert "/etc/issue".to_path.stat.is_file
+       #     assert "/fail/does not/exist".to_path.stat == null
+       #
        # ~~~
        # var p = "/tmp/".to_path
        # var stat = p.stat
@@ -327,8 +370,6 @@ class Path
        end
 
        # Delete a file from the file system, return `true` on success
-       #
-       # Require: `exists`
        fun delete: Bool do return path.to_cstring.file_delete
 
        # Copy content of file at `path` to `dest`
@@ -351,21 +392,80 @@ class Path
        # Open this file for reading
        #
        # Require: `exists and not link_stat.is_dir`
-       fun open_ro: IFStream
+       fun open_ro: FileReader
        do
                # TODO manage streams error when they are merged
-               return new IFStream.open(path)
+               return new FileReader.open(path)
        end
 
        # Open this file for writing
        #
        # Require: `not exists or not stat.is_dir`
-       fun open_wo: OFStream
+       fun open_wo: FileWriter
        do
                # TODO manage streams error when they are merged
-               return new OFStream.open(path)
+               return new FileWriter.open(path)
        end
 
+       # Read all the content of the file
+       #
+       # ~~~
+       # var content = "/etc/issue".to_path.read_all
+       # print content
+       # ~~~
+       #
+       # See `Reader::read_all` for details.
+       fun read_all: String
+       do
+               var s = open_ro
+               var res = s.read_all
+               s.close
+               return res
+       end
+
+       # Read all the lines of the file
+       #
+       # ~~~
+       # var lines = "/etc/passwd".to_path.read_lines
+       #
+       # print "{lines.length} users"
+       #
+       # for l in lines do
+       #     var fields = l.split(":")
+       #     print "name={fields[0]} uid={fields[2]}"
+       # end
+       # ~~~
+       #
+       # See `Reader::read_lines` for details.
+       fun read_lines: Array[String]
+       do
+               var s = open_ro
+               var res = s.read_lines
+               s.close
+               return res
+       end
+
+       # Return an iterator on each line of the file
+       #
+       # ~~~
+       # for l in "/etc/passwd".to_path.each_line do
+       #     var fields = l.split(":")
+       #     print "name={fields[0]} uid={fields[2]}"
+       # end
+       # ~~~
+       #
+       # Note: the stream is automatically closed at the end of the file (see `LineIterator::close_on_finish`)
+       #
+       # See `Reader::each_line` for details.
+       fun each_line: LineIterator
+       do
+               var s = open_ro
+               var res = s.each_line
+               res.close_on_finish = true
+               return res
+       end
+
+
        # Lists the name of the files contained within the directory at `path`
        #
        # Require: `exists and is_dir`
@@ -437,6 +537,11 @@ class FileStat
                return stat.atime
        end
 
+       # Returns the last access time
+       #
+       # alias for `last_access_time`
+       fun atime: Int do return last_access_time
+
        # Returns the last modification time in seconds since Epoch
        fun last_modification_time: Int
        do
@@ -444,6 +549,12 @@ class FileStat
                return stat.mtime
        end
 
+       # Returns the last modification time
+       #
+       # alias for `last_modification_time`
+       fun mtime: Int do return last_modification_time
+
+
        # Size of the file at `path`
        fun size: Int
        do
@@ -451,13 +562,16 @@ class FileStat
                return stat.size
        end
 
-       # Is this a regular file and not a device file, pipe, socket, etc.?
+       # Is self a regular file and not a device file, pipe, socket, etc.?
        fun is_file: Bool
        do
                assert not finalized
                return stat.is_reg
        end
 
+       # Alias for `is_file`
+       fun is_reg: Bool do return is_file
+
        # Is this a directory?
        fun is_dir: Bool
        do
@@ -481,6 +595,11 @@ class FileStat
                return stat.ctime
        end
 
+       # Returns the last status change time
+       #
+       # alias for `last_status_change_time`
+       fun ctime: Int do return last_status_change_time
+
        # Returns the permission bits of file
        fun mode: Int
        do
@@ -527,10 +646,20 @@ redef class String
        fun file_exists: Bool do return to_cstring.file_exists
 
        # The status of a file. see POSIX stat(2).
-       fun file_stat: NativeFileStat do return to_cstring.file_stat
+       fun file_stat: nullable FileStat
+       do
+               var stat = to_cstring.file_stat
+               if stat.address_is_null then return null
+               return new FileStat(stat)
+       end
 
        # The status of a file or of a symlink. see POSIX lstat(2).
-       fun file_lstat: NativeFileStat do return to_cstring.file_lstat
+       fun file_lstat: nullable FileStat
+       do
+               var stat = to_cstring.file_lstat
+               if stat.address_is_null then return null
+               return new FileStat(stat)
+       end
 
        # Remove a file, return true if success
        fun file_delete: Bool do return to_cstring.file_delete
@@ -613,11 +742,12 @@ redef class String
        end
 
        # Simplify a file path by remove useless ".", removing "//", and resolving ".."
-       # ".." are not resolved if they start the path
-       # starting "/" is not removed
-       # trainling "/" is removed
        #
-       # Note that the method only wonrk on the string:
+       # * ".." are not resolved if they start the path
+       # * starting "/" is not removed
+       # * trailing "/" is removed
+       #
+       # Note that the method only work on the string:
        #
        #  * no I/O access is performed
        #  * the validity of the path is not checked
@@ -653,7 +783,6 @@ redef class String
        # Using a standard "{self}/{path}" does not work in the following cases:
        #
        # * `self` is empty.
-       # * `path` ends with `'/'`.
        # * `path` starts with `'/'`.
        #
        # This method ensures that the join is valid.
@@ -887,7 +1016,7 @@ redef class NativeString
 end
 
 # This class is system dependent ... must reify the vfs
-extern class NativeFileStat `{ struct stat * `}
+private extern class NativeFileStat `{ struct stat * `}
        # Returns the permission bits of file
        fun mode: Int is extern "file_FileStat_FileStat_mode_0"
        # Returns the last access time
@@ -936,18 +1065,14 @@ end
 
 redef class Sys
 
-       init do
-               if stdout isa FStream then stdout.as(FStream).set_buffering_mode(256, buffer_mode_line)
-       end
-
        # Standard input
-       var stdin: PollableIStream = new Stdin is protected writable
+       var stdin: PollableReader = new Stdin is protected writable, lazy
 
        # Standard output
-       var stdout: OStream = new Stdout is protected writable
+       var stdout: Writer = new Stdout is protected writable, lazy
 
        # Standard output for errors
-       var stderr: OStream = new Stderr is protected writable
+       var stderr: Writer = new Stderr is protected writable, lazy
 
        # Enumeration for buffer mode full (flushes when buffer is full)
        fun buffer_mode_full: Int is extern "file_Sys_Sys_buffer_mode_full_0"
@@ -958,15 +1083,15 @@ redef class Sys
 
        # returns first available stream to read or write to
        # return null on interruption (possibly a signal)
-       protected fun poll( streams : Sequence[FStream] ) : nullable FStream
+       protected fun poll( streams : Sequence[FileStream] ) : nullable FileStream
        do
                var in_fds = new Array[Int]
                var out_fds = new Array[Int]
-               var fd_to_stream = new HashMap[Int,FStream]
+               var fd_to_stream = new HashMap[Int,FileStream]
                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 )
+                       if s isa FileReader then in_fds.add( fd )
+                       if s isa FileWriter then out_fds.add( fd )
 
                        fd_to_stream[fd] = s
                end
@@ -1035,30 +1160,30 @@ redef class Sys
 end
 
 # Print `objects` on the standard output (`stdout`).
-protected fun printn(objects: Object...)
+fun printn(objects: Object...)
 do
        sys.stdout.write(objects.to_s)
 end
 
 # Print an `object` on the standard output (`stdout`) and add a newline.
-protected fun print(object: Object)
+fun print(object: Object)
 do
        sys.stdout.write(object.to_s)
        sys.stdout.write("\n")
 end
 
 # Read a character from the standard input (`stdin`).
-protected fun getc: Char
+fun getc: Char
 do
        return sys.stdin.read_char.ascii
 end
 
 # Read a line from the standard input (`stdin`).
-protected fun gets: String
+fun gets: String
 do
        return sys.stdin.read_line
 end
 
 # Return the working (current) directory
-protected fun getcwd: String do return file_getcwd.to_s
+fun getcwd: String do return file_getcwd.to_s
 private fun file_getcwd: NativeString is extern "string_NativeString_NativeString_file_getcwd_0"