Merge: src/model/model_index: model index uses BKTree
[nit.git] / lib / core / file.nit
index 09b20d9..ed4c6c8 100644 (file)
@@ -101,7 +101,6 @@ end
 # `Stream` that can read from a File
 class FileReader
        super FileStream
-       super BufferedReader
        super PollableReader
        # Misc
 
@@ -114,62 +113,64 @@ class FileReader
        #     assert l == f.read_line
        fun reopen
        do
-               if not eof and not _file.as(not null).address_is_null then close
+               var fl = _file
+               if fl != null and not fl.address_is_null then close
                last_error = null
                _file = new NativeFile.io_open_read(path.as(not null).to_cstring)
                if _file.as(not null).address_is_null then
                        last_error = new IOError("Cannot open `{path.as(not null)}`: {sys.errno.strerror}")
-                       end_reached = true
                        return
                end
-               end_reached = false
-               buffer_reset
        end
 
-       redef fun close
+       redef fun raw_read_byte
        do
-               super
-               buffer_reset
-               end_reached = true
+               var nb = _file.as(not null).io_read(write_buffer, 1)
+               if last_error == null and _file.as(not null).ferror then
+                       last_error = new IOError("Cannot read `{path.as(not null)}`: {sys.errno.strerror}")
+               end
+               if nb == 0 then return -1
+               return write_buffer[0].to_i
        end
 
-       redef fun fill_buffer
+       redef fun raw_read_bytes(cstr, max)
        do
-               var nb = _file.as(not null).io_read(_buffer, _buffer_capacity)
+               var nb = _file.as(not null).io_read(cstr, max)
                if last_error == null and _file.as(not null).ferror then
                        last_error = new IOError("Cannot read `{path.as(not null)}`: {sys.errno.strerror}")
-                       end_reached = true
-               end
-               if nb <= 0 then
-                       end_reached = true
-                       nb = 0
                end
-               _buffer_length = nb
-               _buffer_pos = 0
+               return nb
        end
 
-       # End of file?
-       redef var end_reached = false
+       redef fun eof do
+               var fl = _file
+               if fl == null then return true
+               if fl.address_is_null then return true
+               if last_error != null then return true
+               if super then
+                       if last_error != null then return true
+                       return fl.feof
+               end
+               return false
+       end
 
        # Open the file at `path` for reading.
        #
        #     var f = new FileReader.open("/etc/issue")
-       #     assert not f.end_reached
+       #     assert not f.eof
        #     f.close
        #
        # In case of error, `last_error` is set
        #
        #     f = new FileReader.open("/fail/does not/exist")
-       #     assert f.end_reached
+       #     assert f.eof
        #     assert f.last_error != null
        init open(path: String)
        do
                self.path = path
-               prepare_buffer(100)
                _file = new NativeFile.io_open_read(path.to_cstring)
                if _file.as(not null).address_is_null then
                        last_error = new IOError("Cannot open `{path}`: {sys.errno.strerror}")
-                       end_reached = true
                end
        end
 
@@ -178,11 +179,9 @@ class FileReader
        # This is a low-level method.
        init from_fd(fd: Int) do
                self.path = ""
-               prepare_buffer(1)
                _file = fd.fd_to_stream(read_only)
                if _file.as(not null).address_is_null then
                        last_error = new IOError("Error: Converting fd {fd} to stream failed with '{sys.errno.strerror}'")
-                       end_reached = true
                end
        end
 
@@ -210,13 +209,13 @@ class FileWriter
        super FileStream
        super Writer
 
-       redef fun write_bytes(s) do
+       redef fun write_bytes_from_cstring(cs, len) do
                if last_error != null then return
                if not _is_writable then
                        last_error = new IOError("cannot write to non-writable stream")
                        return
                end
-               write_native(s.items, 0, s.length)
+               write_native(cs, 0, len)
        end
 
        redef fun write(s)
@@ -307,6 +306,9 @@ redef class Int
        private fun fd_to_stream(mode: CString): NativeFile `{
                return fdopen((int)self, mode);
        `}
+
+       # Does the file descriptor `self` refer to a terminal?
+       fun isatty: Bool `{ return isatty(self); `}
 end
 
 # Constant for read-only file streams
@@ -330,7 +332,6 @@ class Stdin
        init do
                _file = new NativeFile.native_stdin
                path = "/dev/stdin"
-               prepare_buffer(1)
        end
 end
 
@@ -510,9 +511,10 @@ class Path
                var input = open_ro
                var output = dest.open_wo
 
+               var buffer = new CString(4096)
                while not input.eof do
-                       var buffer = input.read_bytes(1024)
-                       output.write_bytes buffer
+                       var read = input.read_bytes_to_cstring(buffer, 4096)
+                       output.write_bytes_from_cstring(buffer, read)
                end
 
                input.close
@@ -898,9 +900,7 @@ redef class Text
 
        # return true if a file with this names exists
        fun file_exists: Bool do return to_cstring.file_exists
-end
 
-redef class String
        # The status of a file. see POSIX stat(2).
        fun file_stat: nullable FileStat
        do
@@ -943,14 +943,14 @@ redef class String
                if extension == null then
                        extension = file_extension
                        if extension == null then
-                               return self
+                               return self.to_s
                        else extension = ".{extension}"
                end
 
                if has_suffix(extension) then
-                       return substring(0, length - extension.length)
+                       return substring(0, length - extension.length).to_s
                end
-               return self
+               return self.to_s
        end
 
        # Extract the basename of a path and strip the `extension`
@@ -989,7 +989,7 @@ redef class String
 
                if extension != null then
                        return n.strip_extension(extension)
-               else return n
+               else return n.to_s
        end
 
        # Extract the dirname of a path
@@ -1018,7 +1018,7 @@ redef class String
                while l > 0 and s.chars[l] == '/' do l -= 1 # remove all trailing `/`
                var pos = s.chars.last_index_of_from('/', l)
                if pos > 0 then
-                       return s.substring(0, pos)
+                       return s.substring(0, pos).to_s
                else if pos == 0 then
                        return "/"
                else
@@ -1115,11 +1115,11 @@ redef class String
        # Note: You may want to use `simplify_path` on the result.
        #
        # Note: This method works only with POSIX paths.
-       fun join_path(path: String): String
+       fun join_path(path: Text): String
        do
-               if path.is_empty then return self
-               if self.is_empty then return path
-               if path.chars[0] == '/' then return path
+               if path.is_empty then return self.to_s
+               if self.is_empty then return path.to_s
+               if path.chars[0] == '/' then return path.to_s
                if self.last == '/' then return "{self}{path}"
                return "{self}/{path}"
        end
@@ -1134,7 +1134,7 @@ redef class String
        #     assert "".to_program_name == "./" # At least, your shell will detect the error.
        fun to_program_name: String do
                if self.has_prefix("/") then
-                       return self
+                       return self.to_s
                else
                        return "./{self}"
                end
@@ -1154,7 +1154,7 @@ redef class String
        #     var b = "/bar"
        #     var c = "baz/foobar"
        #     assert a/b/c == "/bar/baz/foobar"
-       fun /(path: String): String do return join_path(path)
+       fun /(path: Text): String do return join_path(path)
 
        # Returns the relative path needed to go from `self` to `dest`.
        #
@@ -1318,7 +1318,7 @@ redef class String
        do
                var last_slash = chars.last_index_of('.')
                if last_slash > 0 then
-                       return substring( last_slash+1, length )
+                       return substring( last_slash+1, length ).to_s
                else
                        return null
                end
@@ -1365,7 +1365,7 @@ redef class FlatString
                var p = last_byte
                var c = its[p]
                var st = _first_byte
-               while p >= st and c != '.'.ascii do
+               while p >= st and c != u'.' do
                        p -= 1
                        c = its[p]
                end
@@ -1382,7 +1382,7 @@ redef class FlatString
                var l = s.last_byte
                var its = s._items
                var min = s._first_byte
-               var sl = '/'.ascii
+               var sl = u'/'
                while l > min and its[l] == sl do l -= 1
                if l == min then return "/"
                var ns = l
@@ -1526,7 +1526,7 @@ private extern class NativeFile `{ FILE* `}
                return (long)res;
        `}
 
-       fun write_byte(value: Byte): Int `{
+       fun write_byte(value: Int): Int `{
                unsigned char b = (unsigned char)value;
                return fwrite(&b, 1, 1, self);
        `}
@@ -1545,6 +1545,8 @@ private extern class NativeFile `{ FILE* `}
 
        fun ferror: Bool `{ return ferror(self); `}
 
+       fun feof: Bool `{ return feof(self); `}
+
        fun fileno: Int `{ return fileno(self); `}
 
        # Flushes the buffer, forcing the write operation