X-Git-Url: http://nitlanguage.org diff --git a/lib/standard/file.nit b/lib/standard/file.nit index f6e7c96..64094f7 100644 --- a/lib/standard/file.nit +++ b/lib/standard/file.nit @@ -13,40 +13,52 @@ # another product. # File manipulations (create, read, write, etc.) -package file +module file intrude import stream intrude import string import string_search import time +in "C Header" `{ + #include + #include + #include + #include + #include +`} + redef class Object # Simple I/O - # Print `objects' on the standard output (`stdout'). + # Print `objects` on the standard output (`stdout`). protected fun printn(objects: Object...) do stdout.write(objects.to_s) end - # Print an `object' on the standard output (`stdout') and add a newline. + # Print an `object` on the standard output (`stdout`) and add a newline. protected fun print(object: Object) do stdout.write(object.to_s) stdout.write("\n") end - # Read a character from the standard input (`stdin'). + # Read a character from the standard input (`stdin`). protected fun getc: Char do return stdin.read_char.ascii end - # Read a line from the standard input (`stdin'). + # Read a line from the standard input (`stdin`). protected fun gets: String do return stdin.read_line end + + # Return the working (current) directory + protected fun getcwd: String do return file_getcwd.to_s + private fun file_getcwd: NativeString is extern "string_NativeString_NativeString_file_getcwd_0" end # File Abstract Stream @@ -99,7 +111,7 @@ class IFStream # End of file? redef readable var _end_reached: Bool = false - # Open the file at `path' for reading. + # Open the file at `path` for reading. init open(path: String) do _path = path @@ -134,7 +146,7 @@ class OFStream # Is the file open in write mode var _writable: Bool - # Write `len' bytes from `native'. + # Write `len` bytes from `native`. private fun write_native(native: NativeString, len: Int) do assert _writable @@ -145,7 +157,7 @@ class OFStream end end - # Open the file at `path' for writing. + # Open the file at `path` for writing. init open(path: String) do _file = new NativeFile.io_open_write(path.to_cstring) @@ -203,6 +215,21 @@ redef class String # Remove a file, return true if success fun file_delete: Bool do return to_cstring.file_delete + # Copy content of file at `self` to `dest` + fun file_copy_to(dest: String) + do + var input = new IFStream.open(self) + var output = new OFStream.open(dest) + + while not input.eof do + var buffer = input.read(1024) + output.write buffer + end + + input.close + output.close + end + # remove the trailing extension "ext" fun strip_extension(ext: String): String do @@ -224,16 +251,37 @@ redef class String end # Extract the dirname of a path + # + # assert "/path/to/a_file.ext".dirname == "/path/to" + # assert "path/to/a_file.ext".dirname == "path/to" + # assert "path/to".dirname == "path" + # assert "path/to/".dirname == "path" + # assert "path".dirname == "." + # assert "/path".dirname == "/" + # assert "/".dirname == "/" + # assert "".dirname == "." fun dirname: String do - var pos = last_index_of_from('/', _length - 1) - if pos >= 0 then + var l = _length - 1 # Index of the last char + if l > 0 and self.chars[l] == '/' then l -= 1 # remove trailing `/` + var pos = last_index_of_from('/', l) + if pos > 0 then return substring(0, pos) + else if pos == 0 then + return "/" else return "." end end + # Return the canonicalized absolute pathname (see POSIX function `realpath`) + fun realpath: String do + var cs = to_cstring.file_realpath + var res = cs.to_s_with_copy + # cs.free_malloc # FIXME memory leak + return res + end + # Simplify a file path by remove useless ".", removing "//", and resolving ".." # ".." are not resolved if they start the path # starting "/" is not removed @@ -243,10 +291,11 @@ redef class String # * no I/O access is performed # * the validity of the path is not checked # - # "some/./complex/../../path/from/../to/a////file//".simplify_path # -> "path/to/a/file" - # "../dir/file" # -> "../dir/file" - # "dir/../../" # -> ".." - # "//absolute//path/" # -> "/absolute/path" + # assert "some/./complex/../../path/from/../to/a////file//".simplify_path == "path/to/a/file" + # assert "../dir/file".simplify_path == "../dir/file" + # assert "dir/../../".simplify_path == ".." + # assert "dir/..".simplify_path == "." + # assert "//absolute//path/".simplify_path == "/absolute/path" fun simplify_path: String do var a = self.split_with("/") @@ -260,20 +309,21 @@ redef class String end a2.push(x) end + if a2.is_empty then return "." return a2.join("/") end # Correctly join two path using the directory separator. # - # Using a standard "{self}/{path}" does not work when `self' is the empty string. + # Using a standard "{self}/{path}" does not work when `self` is the empty string. # This method ensure that the join is valid. # - # "hello".join_path("world") # -> "hello/world" - # "hel/lo".join_path("wor/ld") # -> "hel/lo/wor/ld" - # "".join_path("world") # -> "world" - # "/hello".join_path("/world") # -> "/world" + # assert "hello".join_path("world") == "hello/world" + # assert "hel/lo".join_path("wor/ld") == "hel/lo/wor/ld" + # assert "".join_path("world") == "world" + # assert "/hello".join_path("/world") == "/world" # - # Note: you may want to use `simplify_path' on the result + # Note: you may want to use `simplify_path` on the result # # Note: I you want to join a great number of path, you can write # @@ -282,7 +332,7 @@ redef class String do if path.is_empty then return self if self.is_empty then return path - if path[0] == '/' then return path + if path.chars[0] == '/' then return path return "{self}/{path}" end @@ -304,6 +354,16 @@ redef class String end end + # Change the current working directory + # + # "/etc".chdir + # assert getcwd == "/etc" + # "..".chdir + # assert getcwd == "/" + # + # TODO: errno + fun chdir do to_cstring.file_chdir + # Return right-most extension (without the dot) fun file_extension : nullable String do @@ -316,7 +376,36 @@ redef class String end # returns files contained within the directory represented by self - fun files : Set[ String ] is extern import HashSet, HashSet::add, String::from_cstring, String::to_cstring, HashSet[String] as( Set[String] ), String as( Object ) + fun files : Set[ String ] is extern import HashSet[String], HashSet[String].add, NativeString.to_s, String.to_cstring, HashSet[String].as(Set[String]) `{ + char *dir_path; + DIR *dir; + + dir_path = String_to_cstring( recv ); + if ((dir = opendir(dir_path)) == NULL) + { + perror( dir_path ); + exit( 1 ); + } + else + { + HashSet_of_String results; + String file_name; + struct dirent *de; + + results = new_HashSet_of_String(); + + while ( ( de = readdir( dir ) ) != NULL ) + if ( strcmp( de->d_name, ".." ) != 0 && + strcmp( de->d_name, "." ) != 0 ) + { + file_name = NativeString_to_s( strdup( de->d_name ) ); + HashSet_of_String_add( results, file_name ); + } + + closedir( dir ); + return HashSet_of_String_as_Set_of_String( results ); + } + `} end redef class NativeString @@ -332,6 +421,8 @@ redef class NativeString `} private fun file_mkdir: Bool is extern "string_NativeString_NativeString_file_mkdir_0" private fun file_delete: Bool is extern "string_NativeString_NativeString_file_delete_0" + private fun file_chdir is extern "string_NativeString_NativeString_file_chdir_0" + private fun file_realpath: NativeString is extern "file_NativeString_realpath" end extern FileStat `{ struct stat * `}