lib/file: expose FileStat instead of NativeFileStat
[nit.git] / lib / standard / file.nit
index 2f24cb9..4bac9d7 100644 (file)
@@ -42,11 +42,36 @@ abstract class FileStream
        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
 
+       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
@@ -72,6 +97,11 @@ class FileReader
 
        # 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,11 +119,9 @@ class FileReader
 
        redef fun close
        do
-               if _file == null or _file.address_is_null then return
-               var i = _file.io_close
+               super
                _buffer.clear
                end_reached = true
-               _file = null
        end
 
        redef fun fill_buffer
@@ -111,6 +139,16 @@ class FileReader
        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
@@ -122,6 +160,9 @@ class FileReader
                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)
@@ -150,24 +191,12 @@ class FileWriter
                else
                        for i in s.substrings do write_native(i.to_cstring, i.length)
                end
-               _file.flush
        end
 
        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 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
-               _file = null
        end
        redef var is_writable = false
 
@@ -235,6 +264,8 @@ 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 FileReader
 
@@ -248,6 +279,8 @@ class Stdin
 end
 
 # Standard output stream.
+#
+# The class of the default value of `sys.stdout`.
 class Stdout
        super FileWriter
        init do
@@ -259,6 +292,8 @@ class Stdout
 end
 
 # Standard error stream.
+#
+# The class of the default value of `sys.stderr`.
 class Stderr
        super FileWriter
        init do
@@ -305,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
@@ -331,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`
@@ -500,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
@@ -507,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
@@ -514,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
@@ -544,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
@@ -590,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
@@ -676,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
@@ -716,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.
@@ -950,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
@@ -1000,13 +1066,13 @@ end
 redef class Sys
 
        # Standard input
-       var stdin: PollableReader = new Stdin is protected writable
+       var stdin: PollableReader = new Stdin is protected writable, lazy
 
        # Standard output
-       var stdout: Writer = new Stdout is protected writable
+       var stdout: Writer = new Stdout is protected writable, lazy
 
        # Standard output for errors
-       var stderr: Writer = 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"