lib/file: move implementation code to light FFI
[nit.git] / lib / standard / file.nit
index 5a89d10..f786f6d 100644 (file)
@@ -16,8 +16,8 @@
 module file
 
 intrude import stream
-intrude import ropes
-import string_search
+intrude import text::ropes
+import text
 import time
 import gc
 
@@ -135,7 +135,7 @@ class FileReader
        end
 
        # End of file?
-       redef var end_reached: Bool = false
+       redef var end_reached = false
 
        # Open the file at `path` for reading.
        #
@@ -299,7 +299,15 @@ class Stdin
                prepare_buffer(1)
        end
 
-       redef fun poll_in: Bool is extern "file_stdin_poll_in"
+       redef fun poll_in `{
+               struct pollfd fd = {0, POLLIN, 0};
+               int res = poll(&fd, 1, 0);
+               if (res == -1) {
+                       perror("Error poll stdin");
+                       exit(EXIT_FAILURE);
+               }
+               return res > 0;
+       `}
 end
 
 # Standard output stream.
@@ -767,11 +775,12 @@ redef class String
                return res
        end
 
-       # Simplify a file path by remove useless ".", removing "//", and resolving ".."
+       # Simplify a file path by remove useless `.`, removing `//`, and resolving `..`
        #
-       # * ".." are not resolved if they start the path
-       # * starting "/" is not removed
-       # * trailing "/" is removed
+       # * `..` are not resolved if they start the path
+       # * starting `.` is simplified unless the path is empty
+       # * starting `/` is not removed
+       # * trailing `/` is removed
        #
        # Note that the method only work on the string:
        #
@@ -785,17 +794,29 @@ redef class String
        # assert "dir/..".simplify_path            ==  "."
        # assert "//absolute//path/".simplify_path ==  "/absolute/path"
        # assert "//absolute//../".simplify_path   ==  "/"
+       # assert "/".simplify_path                 == "/"
+       # assert "../".simplify_path               == ".."
+       # assert "./".simplify_path                == "."
+       # assert "././././././".simplify_path      == "."
+       # assert "./../dir".simplify_path                  == "../dir"
+       # assert "./dir".simplify_path                     == "dir"
        # ~~~
        fun simplify_path: String
        do
                var a = self.split_with("/")
                var a2 = new Array[String]
                for x in a do
-                       if x == "." then continue
-                       if x == "" and not a2.is_empty then continue
+                       if x == "." and not a2.is_empty then continue # skip `././`
+                       if x == "" and not a2.is_empty then continue # skip `//`
                        if x == ".." and not a2.is_empty and a2.last != ".." then
-                               a2.pop
-                               continue
+                               if a2.last == "." then # do not skip `./../`
+                                       a2.pop # reduce `./../` in `../`
+                               else # reduce `dir/../` in `/`
+                                       a2.pop
+                                       continue
+                               end
+                       else if not a2.is_empty and a2.last == "." then
+                               a2.pop # reduce `./dir` in `dir`
                        end
                        a2.push(x)
                end
@@ -962,7 +983,7 @@ redef class String
        #
        # Return an error object in case of error.
        #
-       #    assert "/fail/does not/exist".rmdir != null
+       #     assert "/fail/does not/exist".rmdir != null
        fun rmdir: nullable Error
        do
                var res = to_path.rmdir
@@ -1050,8 +1071,24 @@ redef class String
 end
 
 redef class NativeString
-       private fun file_exists: Bool is extern "string_NativeString_NativeString_file_exists_0"
-       private fun file_stat: NativeFileStat is extern "string_NativeString_NativeString_file_stat_0"
+       private fun file_exists: Bool `{
+               FILE *hdl = fopen(self,"r");
+               if(hdl != NULL){
+                       fclose(hdl);
+               }
+               return hdl != NULL;
+       `}
+
+       private fun file_stat: NativeFileStat `{
+               struct stat buff;
+               if(stat(self, &buff) != -1) {
+                       struct stat* stat_element;
+                       stat_element = malloc(sizeof(struct stat));
+                       return memcpy(stat_element, &buff, sizeof(struct stat));
+               }
+               return 0;
+       `}
+
        private fun file_lstat: NativeFileStat `{
                struct stat* stat_element;
                int res;
@@ -1082,16 +1119,22 @@ private extern class NativeFileStat `{ struct stat * `}
 
        # Returns true if it is a regular file (not a device file, pipe, sockect, ...)
        fun is_reg: Bool `{ return S_ISREG(self->st_mode); `}
+
        # Returns true if it is a directory
        fun is_dir: Bool `{ return S_ISDIR(self->st_mode); `}
+
        # Returns true if it is a character device
        fun is_chr: Bool `{ return S_ISCHR(self->st_mode); `}
+
        # Returns true if it is a block device
        fun is_blk: Bool `{ return S_ISBLK(self->st_mode); `}
+
        # Returns true if the type is fifo
        fun is_fifo: Bool `{ return S_ISFIFO(self->st_mode); `}
+
        # Returns true if the type is a link
        fun is_lnk: Bool `{ return S_ISLNK(self->st_mode); `}
+
        # Returns true if the type is a socket
        fun is_sock: Bool `{ return S_ISSOCK(self->st_mode); `}
 end
@@ -1105,8 +1148,18 @@ private extern class NativeFile `{ FILE* `}
                return fwrite(&b, 1, 1, self);
        `}
        fun io_close: Int is extern "file_NativeFile_NativeFile_io_close_0"
-       fun file_stat: NativeFileStat is extern "file_NativeFile_NativeFile_file_stat_0"
+       fun file_stat: NativeFileStat `{
+               struct stat buff;
+               if(fstat(fileno(self), &buff) != -1) {
+                       struct stat* stat_element;
+                       stat_element = malloc(sizeof(struct stat));
+                       return memcpy(stat_element, &buff, sizeof(struct stat));
+               }
+               return 0;
+       `}
+
        fun fileno: Int `{ return fileno(self); `}
+
        # 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.
@@ -1179,10 +1232,10 @@ redef class Sys
                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) `{
+       private fun intern_poll(in_fds: Array[Int], out_fds: Array[Int]): nullable Int
+       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;
@@ -1236,7 +1289,7 @@ end
 # Print `objects` on the standard output (`stdout`).
 fun printn(objects: Object...)
 do
-       sys.stdout.write(objects.to_s)
+       sys.stdout.write(objects.plain_to_s)
 end
 
 # Print an `object` on the standard output (`stdout`) and add a newline.