1 # depends on: io.rb class.rb module.rb enumerable.rb
4 class TimeVal < FFI::Struct
5 config 'rbx.platform.timeval', :tv_sec, :tv_usec
14 # Internal class for accessing timevals
17 class FileError < Exception; end
18 class NoFileError < FileError; end
19 class UnableToStat < FileError; end
20 class PermissionError < FileError; end
25 # TODO: "OK" constants aren't in File::Constants in MRI
26 F_OK = 0 # test for existence of file
27 X_OK = 1 # test for execute or search permission
28 W_OK = 2 # test for write permission
29 R_OK = 4 # test for read permission
38 SEPARATOR = Platform::File::SEPARATOR
39 Separator = Platform::File::SEPARATOR
40 ALT_SEPARATOR = Platform::File::ALT_SEPARATOR
41 PATH_SEPARATOR = Platform::File::PATH_SEPARATOR
42 POSIX = Platform::POSIX
44 def initialize(path_or_fd, mode = "r", perm = 0666)
45 if path_or_fd.kind_of?(Integer)
46 super(path_or_fd, mode)
51 path = StringValue(path_or_fd)
53 fd = IO.sysopen(path, mode, perm)
54 Errno.handle path if fd < 0
63 private_class_method :dirsep?, :next_path, :range, :name_match
65 # these will be necessary when we run on Windows
66 const_set :DOSISH, RUBY_PLATFORM.match("mswin")
67 const_set :CASEFOLD_FILESYSTEM, DOSISH
68 const_set :FNM_SYSCASE, CASEFOLD_FILESYSTEM ? FNM_CASEFOLD : false
72 # Returns the last access time for the named file as a Time object).
74 # File.atime("testfile") #=> Wed Apr 09 08:51:48 CDT 2003
80 # Returns the last component of the filename given
81 # in file_name, which must be formed using forward
82 # slashes (``/’’) regardless of the separator used
83 # on the local file system. If suffix is given and
84 # present at the end of file_name, it is removed.
86 # File.basename("/home/gumby/work/ruby.rb") #=> "ruby.rb"
87 # File.basename("/home/gumby/work/ruby.rb", ".rb") #=> "ruby"
88 def self.basename(path,ext = "")
89 path = StringValue(path)
90 ext = StringValue(ext)
91 Platform::File.basename(path,ext)
95 # Returns true if the named file is a block device.
96 def self.blockdev?(path)
98 st ? st.blockdev? : false
102 # Returns true if the named file is a character device.
103 def self.chardev?(path)
105 st ? st.chardev? : false
109 # Changes permission bits on the named file(s) to
110 # the bit pattern represented by mode_int. Actual
111 # effects are operating system dependent (see the
112 # beginning of this section). On Unix systems, see
113 # chmod(2) for details. Returns the number of files processed.
115 # File.chmod(0644, "testfile", "out") #=> 2
116 def self.chmod(mode, *paths)
117 mode = Type.coerce_to(mode, Integer, :to_int) unless mode.is_a? Integer
119 path = Type.coerce_to(path, String, :to_str) unless path.is_a? String
120 POSIX.chmod(path, mode)
126 # Equivalent to File::chmod, but does not follow symbolic
127 # links (so it will change the permissions associated with
128 # the link, not the file referenced by the link).
129 # Often not available.
130 def self.lchmod(mode, *paths)
131 mode = Type.coerce_to(mode, Integer, :to_int) unless mode.is_a? Integer
133 path = Type.coerce_to(path, String, :to_str) unless path.is_a? String
134 POSIX.lchmod(path, mode)
140 # Changes the owner and group of the
141 # named file(s) to the given numeric owner
142 # and group id‘s. Only a process with superuser
143 # privileges may change the owner of a file. The
144 # current owner of a file may change the file‘s
145 # group to any group to which the owner belongs.
146 # A nil or -1 owner or group id is ignored.
147 # Returns the number of files processed.
149 # File.chown(nil, 100, "testfile")
150 def self.chown(owner_int, group_int, *paths)
151 owner_int = -1 if owner_int == nil
152 group_int = -1 if group_int == nil
153 paths.each { |path| POSIX.chown(path, owner_int, group_int) }
158 # Equivalent to File::chown, but does not follow
159 # symbolic links (so it will change the owner
160 # associated with the link, not the file referenced
161 # by the link). Often not available. Returns number
162 # of files in the argument list.
163 def self.lchown(owner_int, group_int, *paths)
164 owner_int = -1 if owner_int == nil
165 group_int = -1 if group_int == nil
166 paths.each { |path| POSIX.lchown(path, owner_int, group_int) }
171 # Returns the change time for the named file (the
172 # time at which directory information about the
173 # file was changed, not the file itself).
175 # File.ctime("testfile") #=> Wed Apr 09 08:53:13 CDT 2003
181 # Returns true if the named file is a directory, false otherwise.
183 # File.directory?(".")
184 def self.directory?(path)
186 st ? st.directory? : false
190 # Returns all components of the filename given in
191 # file_name except the last one. The filename must be
192 # formed using forward slashes (``/’’) regardless of
193 # the separator used on the local file system.
195 # File.dirname("/home/gumby/work/ruby.rb") #=> "/home/gumby/work"
196 def self.dirname(path)
197 path = StringValue(path)
198 Platform::File.dirname(path)
202 # Returns true if the named file is executable by the
203 # effective user id of this process.
204 def self.executable?(path)
206 st ? st.executable? : false
210 # Returns true if the named file is executable by
211 # the real user id of this process.
212 def self.executable_real?(path)
214 st ? st.executable_real? : false
218 # Return true if the named file exists.
219 def self.exist?(path)
220 Stat.stat?(path) ? true : false
221 rescue SystemCallError
226 # Converts a pathname to an absolute pathname. Relative
227 # paths are referenced from the current working directory
228 # of the process unless dir_string is given, in which case
229 # it will be used as the starting point. The given pathname
230 # may start with a ``~’’, which expands to the process owner‘s
231 # home directory (the environment variable HOME must be set
232 # correctly). "~user" expands to the named user‘s home directory.
234 # File.expand_path("~oracle/bin") #=> "/home/oracle/bin"
235 # File.expand_path("../../bin", "/tmp/x") #=> "/bin"
236 def self.expand_path(path, dir_string = nil)
237 Platform::File.expand_path(path, dir_string)
241 # Returns the extension (the portion of file name in
242 # path after the period).
244 # File.extname("test.rb") #=> ".rb"
245 # File.extname("a/b/d/test.rb") #=> ".rb"
246 # File.extname("test") #=> ""
247 # File.extname(".profile") #=> ""
248 def self.extname(path)
249 filename = File.basename(StringValue(path))
250 idx = filename.rindex '.'
251 have_dot = idx != nil
252 first_char = idx == 0
253 last_char = idx == filename.length - 1
254 only_dots = filename.match(/[^\.]/).nil?
256 return '' unless have_dot
257 return '' if first_char || last_char
258 return '' if only_dots
259 filename.slice idx..-1
263 # Returns true if the named file exists and is a regular file.
266 st ? st.file? : false
270 # File.fnmatch and helpers. This is a port of JRuby's code
273 def self.dirsep?(char)
275 char == ?\\ || char == ?/
281 def self.next_path(str, start, strend)
282 start += 1 while start < strend and !dirsep? str[start]
286 def self.range(pattern, pstart, pend, test, flags)
288 escape = (flags & FNM_NOESCAPE) == 0
289 case_sensitive = (flags & FNM_CASEFOLD) == 0
290 neg = pattern[pstart] == ?! || pattern[pstart] == ?^
294 while pattern[pstart] != ?] do
295 pstart += 1 if escape && pattern[pstart] == ?\\
296 return -1 if pstart >= pend
297 cstart = cend = pattern[pstart]
300 if pattern[pstart] == ?- && pattern[pstart+1] != ?]
302 pstart += 1 if escape && pattern[pstart] == ?\\
303 return -1 if pstart >= pend
304 cend = pattern[pstart]
309 ok = true if cstart <= test && test <= cend
311 ok = true if cstart.tolower <= test.tolower &&
312 test.tolower <= cend.tolower
316 ok == neg ? -1 : pstart + 1
319 def self.name_match(pattern, str, flags, patstart, patend, strstart, strend)
322 escape = (flags & FNM_NOESCAPE) == 0
323 pathname = (flags & FNM_PATHNAME) != 0
324 period = (flags & FNM_DOTMATCH) == 0
325 nocase = (flags & FNM_CASEFOLD) != 0
327 while pstart < patend do
328 char = pattern[pstart]
332 if index >= strend || (pathname && dirsep?(str[index])) ||
333 (period && str[index] == ?. &&
334 (index == 0 || (pathname && dirsep?(str[index-1]))))
341 while pstart < patend
342 char = pattern[pstart]
344 break unless char == ?*
348 (period && str[index] == ?. &&
349 (index == 0 || (pathname && dirsep?(str[index-1]))))
353 if pstart > patend || (pstart == patend && char == ?*)
354 return !(pathname && next_path(str, index, strend) < strend)
355 elsif pathname && dirsep?(char)
356 index = next_path(str, index, strend)
357 return false unless index < strend
360 test = if escape && char == ?\\ && pstart < patend then
368 while index < strend do
369 if (char == ?? || char == ?[ || str[index].tolower == test) &&
370 name_match(pattern, str, flags | FNM_DOTMATCH, pstart, patend,
373 elsif pathname && dirsep?(str[index])
384 if index >= strend ||
385 (pathname && dirsep?(str[index]) ||
386 (period && str[index] == ?. &&
388 (pathname && dirsep?(str[index-1])))))
392 pstart = range pattern, pstart, patend, str[index], flags
394 return false if pstart == -1
400 (!DOSISH || (pstart < patend && "*?[]\\".index(pattern[pstart])))
401 char = pstart >= patend ? ?\\ : pattern[pstart]
406 return false if index >= strend
408 if DOSISH && (pathname && isdirsep?(char) && dirsep?(str[index]))
409 # TODO: invert this boolean expression
412 return false if char.tolower != str[index].tolower
414 return false if char != str[index]
422 index >= strend ? true : false
426 # Returns true if path matches against pattern The pattern
427 # is not a regular expression; instead it follows rules
428 # similar to shell filename globbing. It may contain the
429 # following metacharacters:
431 # *: Matches any file. Can be restricted by other values in the glob. * will match all files; c* will match all files beginning with c; *c will match all files ending with c; and c will match all files that have c in them (including at the beginning or end). Equivalent to / .* /x in regexp.
432 # **: Matches directories recursively or files expansively.
433 # ?: Matches any one character. Equivalent to /.{1}/ in regexp.
434 # [set]: Matches any one character in set. Behaves exactly like character sets in Regexp, including set negation ([^a-z]).
435 # <code></code>: Escapes the next metacharacter.
436 # flags is a bitwise OR of the FNM_xxx parameters. The same glob pattern and flags are used by Dir::glob.
438 # File.fnmatch('cat', 'cat') #=> true : match entire string
439 # File.fnmatch('cat', 'category') #=> false : only match partial string
440 # File.fnmatch('c{at,ub}s', 'cats') #=> false : { } isn't supported
442 # File.fnmatch('c?t', 'cat') #=> true : '?' match only 1 character
443 # File.fnmatch('c??t', 'cat') #=> false : ditto
444 # File.fnmatch('c*', 'cats') #=> true : '*' match 0 or more characters
445 # File.fnmatch('c*t', 'c/a/b/t') #=> true : ditto
446 # File.fnmatch('ca[a-z]', 'cat') #=> true : inclusive bracket expression
447 # File.fnmatch('ca[^t]', 'cat') #=> false : exclusive bracket expression ('^' or '!')
449 # File.fnmatch('cat', 'CAT') #=> false : case sensitive
450 # File.fnmatch('cat', 'CAT', File::FNM_CASEFOLD) #=> true : case insensitive
452 # File.fnmatch('?', '/', File::FNM_PATHNAME) #=> false : wildcard doesn't match '/' on FNM_PATHNAME
453 # File.fnmatch('*', '/', File::FNM_PATHNAME) #=> false : ditto
454 # File.fnmatch('[/]', '/', File::FNM_PATHNAME) #=> false : ditto
456 # File.fnmatch('\?', '?') #=> true : escaped wildcard becomes ordinary
457 # File.fnmatch('\a', 'a') #=> true : escaped ordinary remains ordinary
458 # File.fnmatch('\a', '\a', File::FNM_NOESCAPE) #=> true : FNM_NOESACPE makes '\' ordinary
459 # File.fnmatch('[\?]', '?') #=> true : can escape inside bracket expression
461 # File.fnmatch('*', '.profile') #=> false : wildcard doesn't match leading
462 # File.fnmatch('*', '.profile', File::FNM_DOTMATCH) #=> true period by default.
463 # File.fnmatch('.*', '.profile') #=> true
465 # rbfiles = '**' '/' '*.rb' # you don't have to do like this. just write in single string.
466 # File.fnmatch(rbfiles, 'main.rb') #=> false
467 # File.fnmatch(rbfiles, './main.rb') #=> false
468 # File.fnmatch(rbfiles, 'lib/song.rb') #=> true
469 # File.fnmatch('**.rb', 'main.rb') #=> true
470 # File.fnmatch('**.rb', './main.rb') #=> false
471 # File.fnmatch('**.rb', 'lib/song.rb') #=> true
472 # File.fnmatch('*', 'dave/.profile') #=> true
474 # pattern = '*' '/' '*'
475 # File.fnmatch(pattern, 'dave/.profile', File::FNM_PATHNAME) #=> false
476 # File.fnmatch(pattern, 'dave/.profile', File::FNM_PATHNAME | File::FNM_DOTMATCH) #=> true
478 # pattern = '**' '/' 'foo'
479 # File.fnmatch(pattern, 'a/b/c/foo', File::FNM_PATHNAME) #=> true
480 # File.fnmatch(pattern, '/a/b/c/foo', File::FNM_PATHNAME) #=> true
481 # File.fnmatch(pattern, 'c:/a/b/c/foo', File::FNM_PATHNAME) #=> true
482 # File.fnmatch(pattern, 'a/.b/c/foo', File::FNM_PATHNAME) #=> false
483 # File.fnmatch(pattern, 'a/.b/c/foo', File::FNM_PATHNAME | File::FNM_DOTMATCH) #=> true
484 def self.fnmatch(pattern, path, flags=0)
485 pattern = StringValue(pattern).dup
486 path = StringValue(path).dup
487 flags = Type.coerce_to(flags, Fixnum, :to_int) unless Fixnum === flags
489 name_match(pattern, path, flags, 0, pattern.size, 0, path.size)
493 # Identifies the type of the named file; the return string is
494 # one of "file", "directory", "characterSpecial",
495 # "blockSpecial", "fifo", "link", "socket", or "unknown".
497 # File.ftype("testfile") #=> "file"
498 # File.ftype("/dev/tty") #=> "characterSpecial"
499 # File.ftype("/tmp/.X11-unix/X0") #=> "socket"
505 # Returns true if the named file exists and the effective
506 # group id of the calling process is the owner of the file.
507 # Returns false on Windows.
508 def self.grpowned?(path)
510 lstat(path).grpowned?
517 # Returns true if the named files are identical.
520 # p File.identical?("a", "a") #=> true
521 # p File.identical?("a", "./a") #=> true
522 # File.link("a", "b")
523 # p File.identical?("a", "b") #=> true
524 # File.symlink("a", "c")
525 # p File.identical?("a", "c") #=> true
527 # p File.identical?("a", "d") #=> false
528 def self.identical?(orig, copy)
529 st_o = stat(StringValue(orig))
530 st_c = stat(StringValue(copy))
532 return false unless st_o.ino == st_c.ino
533 return false unless st_o.ftype == st_c.ftype
534 return false unless POSIX.access(orig, Constants::R_OK)
535 return false unless POSIX.access(copy, Constants::R_OK)
541 # Returns a new string formed by joining the strings using File::SEPARATOR.
543 # File.join("usr", "mail", "gumby") #=> "usr/mail/gumby"
546 o = o.to_str unless Array === o || String === o
548 } rescue raise TypeError
550 # let join/split deal with all the recursive array complexities
551 # one small hack is to replace URI header with \0 and swap back later
552 result = args.join(SEPARATOR).gsub(/\:\//, "\0").split(/#{SEPARATOR}+/o)
553 result << '' if args.empty? || args.last.empty? || args.last[-1] == SEPARATOR[0]
554 result.join(SEPARATOR).gsub(/\0/, ':/')
558 # Creates a new name for an existing file using a hard link.
559 # Will not overwrite new_name if it already exists (raising
560 # a subclass of SystemCallError). Not available on all platforms.
562 # File.link("testfile", ".testfile") #=> 0
563 # IO.readlines(".testfile")[0] #=> "This is line one\n"
564 def self.link(from, to)
566 from = StringValue(from)
568 n = POSIX.link(from, to)
569 Errno.handle if n == -1
574 # Same as File::stat, but does not follow the last symbolic link.
575 # Instead, reports on the link itself.
577 # File.symlink("testfile", "link2test") #=> 0
578 # File.stat("testfile").size #=> 66
579 # File.lstat("link2test").size #=> 8
580 # File.stat("link2test").size #=> 66
586 # Returns the modification time for the named file as a Time object.
588 # File.mtime("testfile") #=> Tue Apr 08 12:58:04 CDT 2003
594 # Returns true if the named file is a pipe.
597 st ? st.pipe? : false
601 # Returns true if the named file is readable by the effective
602 # user id of this process.
603 def self.readable?(path)
605 st ? st.readable? : false
609 # Returns true if the named file is readable by the real user
610 # id of this process.
611 def self.readable_real?(path)
613 st ? st.readable_real? : false
617 # Returns the name of the file referenced by the given link.
618 # Not available on all platforms.
620 # File.symlink("testfile", "link2test") #=> 0
621 # File.readlink("link2test") #=> "testfile"
622 def self.readlink(path)
627 n = POSIX.readlink(path, buf, buf.length)
628 Errno.handle if n == -1
634 # Renames the given file to the new name. Raises a SystemCallError
635 # if the file cannot be renamed.
637 # File.rename("afile", "afile.bak") #=> 0
638 def self.rename(from, to)
640 from = StringValue(from)
642 n = POSIX.rename(from, to)
643 Errno.handle if n == -1
648 # Returns the size of file_name.
654 # Returns nil if file_name doesn‘t exist or has zero size,
655 # the size of the file otherwise.
658 st && st.size > 0 ? st.size : nil
662 # Returns true if the named file is a socket.
663 def self.socket?(path)
665 st ? st.socket? : false
669 # Splits the given string into a directory and a file component and returns them in a two-element array. See also File::dirname and File::basename.
671 # File.split("/home/gumby/.profile") #=> ["/home/gumby", ".profile"]
673 p = StringValue(path)
674 [dirname(p), basename(p)]
678 # Returns a File::Stat object for the named file (see File::Stat).
680 # File.stat("testfile").mtime #=> Tue Apr 08 12:58:04 CDT 2003
686 # Creates a symbolic link called new_name for the
687 # existing file old_name. Raises a NotImplemented
688 # exception on platforms that do not support symbolic links.
690 # File.symlink("testfile", "link2test") #=> 0
691 def self.symlink(from, to)
693 from = StringValue(from)
695 n = POSIX.symlink(from, to)
696 Errno.handle if n == -1
701 # Returns true if the named file is a symbolic link.
702 def self.symlink?(path)
703 st = Stat.stat? path, false
704 st ? st.symlink? : false
708 # Copies a file from to to. If to is a directory, copies from to to/from.
709 def self.syscopy(from, to)
710 out = File.directory?(to) ? to + File.basename(from) : to
712 open(out, 'w') do |f|
713 f.write read(from).read
718 # Return the equivalent S-Expression of the file given.
719 # Raises +SyntaxError+ if there is a syntax issue in the
720 # file, making it unparsable.
721 # File.to_sexp("/tmp/test.rb") #=> [...]
722 def self.to_sexp(name, newlines=true)
723 out = to_sexp_full(name, newlines)
724 if out.kind_of? Tuple
725 exc = SyntaxError.new out.at(0)
726 exc.import_position out.at(1), out.at(2), out.at(3)
731 out = [:newline, 0, name, [:nil]] unless out
736 # Truncates the file file_name to be at most integer
737 # bytes long. Not available on all platforms.
739 # f = File.new("out", "w")
740 # f.write("1234567890") #=> 10
742 # File.truncate("out", 5) #=> 0
743 # File.size("out") #=> 5
744 def self.truncate(path, length)
745 unless self.exist?(path)
746 raise Errno::ENOENT, path
749 unless length.respond_to?(:to_int)
750 raise TypeError, "can't convert #{length.class} into Integer"
753 n = POSIX.truncate(path, length)
754 Errno.handle if n == -1
759 # Returns the current umask value for this process.
760 # If the optional argument is given, set the umask
761 # to that value and return the previous value. Umask
762 # values are subtracted from the default permissions,
763 # so a umask of 0222 would make a file read-only for
766 # File.umask(0006) #=> 18
768 def self.umask(mask = nil)
772 old_mask = POSIX.umask(0)
773 POSIX.umask(old_mask)
779 # Deletes the named files, returning the number of names
780 # passed as arguments. Raises an exception on any error.
782 # See also Dir::rmdir.
783 def self.unlink(*paths)
785 path = StringValue(path)
787 n = POSIX.unlink(path)
788 Errno.handle if n == -1
795 # Sets the access and modification times of each named
796 # file to the first two arguments. Returns the number
797 # of file names in the argument list.
799 def self.utime(a_in, m_in, *paths)
800 ptr = MemoryPointer.new(POSIX::TimeVal, 2)
801 atime = POSIX::TimeVal.new ptr
802 mtime = POSIX::TimeVal.new ptr[1]
803 atime[:tv_sec] = a_in.to_i
806 mtime[:tv_sec] = m_in.to_i
810 if POSIX.utimes(path, ptr) != 0
819 # Returns true if the named file is writable by the effective
820 # user id of this process.
821 def self.writable?(path)
823 st ? st.writable? : false
827 # Returns true if the named file is writable by the real user
828 # id of this process.
829 def self.writable_real?(path)
831 st ? st.writable_real? : false
835 # Returns true if the named file exists and has a zero size.
838 st ? st.zero? : false
842 # Returns true if the named file exists and the effective
843 # used id of the calling process is the owner of the file.
844 # File.owned?(file_name) => true or false
845 def self.owned?(file_name)
846 Stat.new(file_name).owned?
850 # Returns true if the named file has the setgid bit set.
851 def self.setgid?(file_name)
852 Stat.new(file_name).setgid?
856 # Returns true if the named file has the setuid bit set.
857 def self.setuid?(file_name)
858 Stat.new(file_name).setuid?
862 # Returns true if the named file has the sticky bit set.
863 def self.sticky?(file_name)
864 Stat.new(file_name).sticky?
868 # Returns true if the named file exists and the effective
869 # used id of the calling process is the owner of the file.
870 def self.owned?(file_name)
871 Stat.new(file_name).owned?
875 alias_method :delete, :unlink
876 alias_method :exists?, :exist?
877 alias_method :fnmatch?, :fnmatch
881 Stat.new(@path).atime
885 mode = Type.coerce_to(mode, Integer, :to_int) unless mode.is_a? Integer
886 POSIX.fchmod(@descriptor, mode)
889 def chown(owner_int, group_int)
890 POSIX.fchown(@descriptor, owner_int || -1, group_int || -1)
894 Stat.new(@path).ctime
897 def flock(locking_constant)
898 result = POSIX.flock(@descriptor, locking_constant)
899 return false if result == -1
904 Stat.new @path, false
908 Stat.new(@path).mtime
916 length = Type.coerce_to(length, Integer, :to_int)
918 raise Errno::EINVAL, "Can't truncate a file to a negative length" if length < 0
919 raise IOError, "File is closed" if closed?
921 n = POSIX.ftruncate(@descriptor, length)
922 Errno.handle if n == -1
927 return_string = "#<#{self.class}:0x#{object_id.to_s(16)} path=#{@path}"
928 return_string << " (closed)" if closed?
934 class Struct < FFI::Struct
935 config "rbx.platform.stat", :st_dev, :st_ino, :st_mode, :st_nlink,
936 :st_uid, :st_gid, :st_rdev, :st_size, :st_blksize, :st_blocks,
937 :st_atime, :st_mtime, :st_ctime
942 S_IRUSR = Rubinius::RUBY_CONFIG['rbx.platform.file.S_IRUSR']
943 S_IWUSR = Rubinius::RUBY_CONFIG['rbx.platform.file.S_IWUSR']
944 S_IXUSR = Rubinius::RUBY_CONFIG['rbx.platform.file.S_IXUSR']
945 S_IRGRP = Rubinius::RUBY_CONFIG['rbx.platform.file.S_IRGRP']
946 S_IWGRP = Rubinius::RUBY_CONFIG['rbx.platform.file.S_IWGRP']
947 S_IXGRP = Rubinius::RUBY_CONFIG['rbx.platform.file.S_IXGRP']
948 S_IROTH = Rubinius::RUBY_CONFIG['rbx.platform.file.S_IROTH']
949 S_IWOTH = Rubinius::RUBY_CONFIG['rbx.platform.file.S_IWOTH']
950 S_IXOTH = Rubinius::RUBY_CONFIG['rbx.platform.file.S_IXOTH']
952 S_IFMT = Rubinius::RUBY_CONFIG['rbx.platform.file.S_IFMT']
953 S_IFIFO = Rubinius::RUBY_CONFIG['rbx.platform.file.S_IFIFO']
954 S_IFCHR = Rubinius::RUBY_CONFIG['rbx.platform.file.S_IFCHR']
955 S_IFDIR = Rubinius::RUBY_CONFIG['rbx.platform.file.S_IFDIR']
956 S_IFBLK = Rubinius::RUBY_CONFIG['rbx.platform.file.S_IFBLK']
957 S_IFREG = Rubinius::RUBY_CONFIG['rbx.platform.file.S_IFREG']
958 S_IFLNK = Rubinius::RUBY_CONFIG['rbx.platform.file.S_IFLNK']
959 S_IFSOCK = Rubinius::RUBY_CONFIG['rbx.platform.file.S_IFSOCK']
960 S_IFWHT = Rubinius::RUBY_CONFIG['rbx.platform.file.S_IFWHT']
961 S_ISUID = Rubinius::RUBY_CONFIG['rbx.platform.file.S_ISUID']
962 S_ISGID = Rubinius::RUBY_CONFIG['rbx.platform.file.S_ISGID']
964 POSIX = Platform::POSIX
967 # File::Stat#from_fd is used to support IO#stat which does not necessarily
970 def self.from_fd(descriptor)
972 stat_struct = Struct.new
974 stat.instance_variable_set :@stat, stat_struct
975 stat.instance_variable_set :@path, nil
977 result = POSIX.fstat descriptor, stat_struct.pointer
979 Errno.handle "file descriptor #{descriptor}" unless result == 0
984 def initialize(path, follow_links=true)
986 @path = StringValue path
988 result = if follow_links
989 POSIX.stat @path, @stat.pointer
991 POSIX.lstat @path, @stat.pointer
994 Errno.handle @path unless result == 0
997 def self.stat?(path, follow_links=true)
998 new path, follow_links
999 rescue Errno::ENOENT, Errno::ENOTDIR
1004 Time.at @stat[:st_atime]
1016 @stat[:st_mode] & S_IFMT == S_IFBLK
1020 @stat[:st_mode] & S_IFMT == S_IFCHR
1024 Time.at @stat[:st_ctime]
1032 major = POSIX.major @stat[:st_dev]
1033 major < 0 ? nil : major
1037 minor = POSIX.major @stat[:st_dev]
1038 minor < 0 ? nil : minor
1042 @stat[:st_mode] & S_IFMT == S_IFDIR
1046 return true if superuser?
1047 return @stat[:st_mode] & S_IXUSR != 0 if owned?
1048 return @stat[:st_mode] & S_IXGRP != 0 if grpowned?
1049 return @stat[:st_mode] & S_IXOTH != 0
1052 def executable_real?
1053 return true if rsuperuser?
1054 return @stat[:st_mode] & S_IXUSR != 0 if rowned?
1055 return @stat[:st_mode] & S_IXGRP != 0 if rgrpowned?
1056 return @stat[:st_mode] & S_IXOTH != 0
1060 @stat[:st_mode] & S_IFMT == S_IFREG
1088 @stat[:st_gid] == POSIX.getegid
1096 "#<File::Stat dev=0x#{self.dev.to_s(16)}, ino=#{self.ino}, " \
1097 "mode=#{sprintf("%07d", self.mode.to_s(8).to_i)}, nlink=#{self.nlink}, " \
1098 "uid=#{self.uid}, gid=#{self.gid}, rdev=0x#{self.rdev.to_s(16)}, " \
1099 "size=#{self.size}, blksize=#{self.blksize}, blocks=#{self.blocks}, " \
1100 "atime=#{self.atime}, mtime=#{self.mtime}, ctime=#{self.ctime}>"
1108 Time.at @stat[:st_mtime]
1116 @stat[:st_uid] == POSIX.geteuid
1124 @stat[:st_mode] & S_IFMT == S_IFIFO
1132 major = POSIX.major @stat[:st_rdev]
1133 major < 0 ? nil : major
1137 minor = POSIX.minor @stat[:st_rdev]
1138 minor < 0 ? nil : minor
1142 return true if superuser?
1143 return @stat[:st_mode] & S_IRUSR != 0 if owned?
1144 return @stat[:st_mode] & S_IRGRP != 0 if grpowned?
1145 return @stat[:st_mode] & S_IROTH != 0
1149 return true if rsuperuser?
1150 return @stat[:st_mode] & S_IRUSR != 0 if rowned?
1151 return @stat[:st_mode] & S_IRGRP != 0 if rgrpowned?
1152 return @stat[:st_mode] & S_IROTH != 0
1168 size == 0 ? nil : size
1172 @stat[:st_mode] & S_IFMT == S_IFSOCK
1176 @stat[:st_mode] & S_IFMT == S_IFLNK
1184 return true if superuser?
1185 return @stat[:st_mode] & S_IWUSR != 0 if owned?
1186 return @stat[:st_mode] & S_IWGRP != 0 if grpowned?
1187 return @stat[:st_mode] & S_IWOTH != 0
1191 return true if rsuperuser?
1192 return @stat[:st_mode] & S_IWUSR != 0 if rowned?
1193 return @stat[:st_mode] & S_IWGRP != 0 if rgrpowned?
1194 return @stat[:st_mode] & S_IWOTH != 0
1198 @stat[:st_size] == 0
1202 return nil unless other.is_a?(File::Stat)
1203 self.mtime <=> other.mtime
1207 @stat[:st_gid] == POSIX.getgid
1212 @stat[:st_uid] == POSIX.getuid
1219 private :rsuperuser?