core: move more servies to Text (receiver and args only)
[nit.git] / lib / core / file.nit
index baa3403..643fe08 100644 (file)
@@ -898,9 +898,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 +941,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`
@@ -963,35 +961,33 @@ redef class String
        #     assert "path/to".basename(".ext")                 == "to"
        #     assert "path/to/".basename(".ext")                == "to"
        #     assert "path/to".basename                         == "to"
-       #     assert "path".basename("")                        == "path"
-       #     assert "/path".basename("")                       == "path"
-       #     assert "/".basename("")                           == "/"
-       #     assert "".basename("")                            == ""
+       #     assert "path".basename                            == "path"
+       #     assert "/path".basename                           == "path"
+       #     assert "/".basename                               == "/"
+       #     assert "".basename                                == ""
+       #
+       # On Windows, '\' are replaced by '/':
+       #
+       # ~~~nitish
+       # assert "C:\\path\\to\\a_file.ext".basename(".ext")    == "a_file"
+       # assert "C:\\".basename                                == "C:"
+       # ~~~
        fun basename(extension: nullable String): String
        do
                var n = self
-               if is_windows then
-                       var l = length - 1 # Index of the last char
-                       while l > 0 and (self.chars[l] == '/' or chars[l] == '\\') do l -= 1 # remove all trailing `/`
-                       if l == 0 then return "/"
-                       var pos = chars.last_index_of_from('/', l)
-                       pos = pos.max(last_index_of_from('\\', l))
-                       if pos >= 0 then
-                               n = substring(pos+1, l-pos)
-                       end
-               else
-                       var l = length - 1 # Index of the last char
-                       while l > 0 and self.chars[l] == '/' do l -= 1 # remove all trailing `/`
-                       if l == 0 then return "/"
-                       var pos = chars.last_index_of_from('/', l)
-                       if pos >= 0 then
-                               n = substring(pos+1, l-pos)
-                       end
+               if is_windows then n = n.replace("\\", "/")
+
+               var l = length - 1 # Index of the last char
+               while l > 0 and self.chars[l] == '/' do l -= 1 # remove all trailing `/`
+               if l == 0 then return "/"
+               var pos = chars.last_index_of_from('/', l)
+               if pos >= 0 then
+                       n = substring(pos+1, l-pos)
                end
 
                if extension != null then
                        return n.strip_extension(extension)
-               else return n
+               else return n.to_s
        end
 
        # Extract the dirname of a path
@@ -1004,13 +1000,23 @@ redef class String
        #     assert "/path".dirname                       == "/"
        #     assert "/".dirname                           == "/"
        #     assert "".dirname                            == "."
+       #
+       # On Windows, '\' are replaced by '/':
+       #
+       # ~~~nitish
+       # assert "C:\\path\\to\\a_file.ext".dirname        == "C:/path/to"
+       # assert "C:\\file".dirname                        == "C:"
+       # ~~~
        fun dirname: String
        do
+               var s = self
+               if is_windows then s = s.replace("\\", "/")
+
                var l = length - 1 # Index of the last char
-               while l > 0 and self.chars[l] == '/' do l -= 1 # remove all trailing `/`
-               var pos = chars.last_index_of_from('/', l)
+               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 substring(0, pos)
+                       return s.substring(0, pos).to_s
                else if pos == 0 then
                        return "/"
                else
@@ -1055,10 +1061,18 @@ redef class String
        # assert "./../dir".simplify_path                  == "../dir"
        # assert "./dir".simplify_path                     == "dir"
        # ~~~
+       #
+       # On Windows, '\' are replaced by '/':
+       #
+       # ~~~nitish
+       # assert "C:\\some\\.\\complex\\../../path/to/a_file.ext".simplify_path == "C:/path/to/a_file.ext"
+       # assert "C:\\".simplify_path              == "C:"
+       # ~~~
        fun simplify_path: String
        do
-               var path_sep = if is_windows then "\\" else "/"
-               var a = self.split_with(path_sep)
+               var s = self
+               if is_windows then s = s.replace("\\", "/")
+               var a = s.split_with("/")
                var a2 = new Array[String]
                for x in a do
                        if x == "." and not a2.is_empty then continue # skip `././`
@@ -1099,11 +1113,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
@@ -1118,7 +1132,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
@@ -1138,7 +1152,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`.
        #
@@ -1183,6 +1197,7 @@ redef class String
        #     assert "/" + "/".relpath(".") == getcwd
        fun relpath(dest: String): String
        do
+               # TODO windows support
                var cwd = getcwd
                var from = (cwd/self).simplify_path.split("/")
                if from.last.is_empty then from.pop # case for the root directory
@@ -1215,8 +1230,10 @@ redef class String
        fun mkdir(mode: nullable Int): nullable Error
        do
                mode = mode or else 0o777
+               var s = self
+               if is_windows then s = s.replace("\\", "/")
 
-               var dirs = self.split_with("/")
+               var dirs = s.split_with("/")
                var path = new FlatBuffer
                if dirs.is_empty then return null
                if dirs[0].is_empty then
@@ -1235,7 +1252,7 @@ redef class String
                                error = new IOError("Cannot create directory `{path}`: {sys.errno.strerror}")
                        end
                end
-               var res = self.to_cstring.file_mkdir(mode)
+               var res = s.to_cstring.file_mkdir(mode)
                if not res and error == null then
                        error = new IOError("Cannot create directory `{path}`: {sys.errno.strerror}")
                end
@@ -1299,7 +1316,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
@@ -1356,29 +1373,19 @@ redef class FlatString
        end
 
        redef fun basename(extension) do
+               var s = self
+               if is_windows then s = s.replace("\\", "/").as(FlatString)
+
                var bname
-               if is_windows then
-                       var l = last_byte
-                       var its = _items
-                       var min = _first_byte
-                       var sl = '/'.ascii
-                       var ls = '\\'.ascii
-                       while l > min and (its[l] == sl or its[l] == ls) do l -= 1
-                       if l == min then return "\\"
-                       var ns = l
-                       while ns >= min and its[ns] != sl and its[ns] != ls do ns -= 1
-                       bname = new FlatString.with_infos(its, l - ns, ns + 1)
-               else
-                       var l = last_byte
-                       var its = _items
-                       var min = _first_byte
-                       var sl = '/'.ascii
-                       while l > min and its[l] == sl do l -= 1
-                       if l == min then return "/"
-                       var ns = l
-                       while ns >= min and its[ns] != sl do ns -= 1
-                       bname = new FlatString.with_infos(its, l - ns, ns + 1)
-               end
+               var l = s.last_byte
+               var its = s._items
+               var min = s._first_byte
+               var sl = '/'.ascii
+               while l > min and its[l] == sl do l -= 1
+               if l == min then return "/"
+               var ns = l
+               while ns >= min and its[ns] != sl do ns -= 1
+               bname = new FlatString.with_infos(its, l - ns, ns + 1)
 
                return if extension != null then bname.strip_extension(extension) else bname
        end
@@ -1509,7 +1516,12 @@ private extern class NativeFile `{ FILE* `}
        `}
 
        fun io_write(buf: CString, from, len: Int): Int `{
-               return fwrite(buf+from, 1, len, self);
+               size_t res = fwrite(buf+from, 1, len, self);
+#ifdef _WIN32
+               // Force flushing buffer because end of line does not trigger a flush
+               fflush(self);
+#endif
+               return (long)res;
        `}
 
        fun write_byte(value: Byte): Int `{