add rdoc.
[ruby-svn.git] / file.c
blob970875f9469762b5d627489fa96026085ad812e5
1 /**********************************************************************
3 file.c -
5 $Author$
6 created at: Mon Nov 15 12:24:34 JST 1993
8 Copyright (C) 1993-2007 Yukihiro Matsumoto
9 Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
10 Copyright (C) 2000 Information-technology Promotion Agency, Japan
12 **********************************************************************/
14 #ifdef _WIN32
15 #include "missing/file.h"
16 #endif
17 #ifdef __CYGWIN__
18 #include <windows.h>
19 #include <sys/cygwin.h>
20 #endif
22 #include "ruby/ruby.h"
23 #include "ruby/io.h"
24 #include "ruby/signal.h"
25 #include "ruby/util.h"
26 #include "dln.h"
28 #ifdef HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
32 #ifdef HAVE_SYS_FILE_H
33 # include <sys/file.h>
34 #else
35 int flock(int, int);
36 #endif
38 #ifdef HAVE_SYS_PARAM_H
39 # include <sys/param.h>
40 #endif
41 #ifndef MAXPATHLEN
42 # define MAXPATHLEN 1024
43 #endif
45 #include <ctype.h>
47 #include <time.h>
49 #ifdef HAVE_UTIME_H
50 #include <utime.h>
51 #elif defined HAVE_SYS_UTIME_H
52 #include <sys/utime.h>
53 #endif
55 #ifdef HAVE_PWD_H
56 #include <pwd.h>
57 #endif
59 #include <sys/types.h>
60 #include <sys/stat.h>
62 #ifdef HAVE_SYS_MKDEV_H
63 #include <sys/mkdev.h>
64 #endif
66 #if !defined HAVE_LSTAT && !defined lstat
67 #define lstat stat
68 #endif
70 #ifdef __BEOS__ /* should not change ID if -1 */
71 static int
72 be_chown(const char *path, uid_t owner, gid_t group)
74 if (owner == -1 || group == -1) {
75 struct stat st;
76 if (stat(path, &st) < 0) return -1;
77 if (owner == -1) owner = st.st_uid;
78 if (group == -1) group = st.st_gid;
80 return chown(path, owner, group);
82 #define chown be_chown
83 static int
84 be_fchown(int fd, uid_t owner, gid_t group)
86 if (owner == -1 || group == -1) {
87 struct stat st;
88 if (fstat(fd, &st) < 0) return -1;
89 if (owner == -1) owner = st.st_uid;
90 if (group == -1) group = st.st_gid;
92 return fchown(fd, owner, group);
94 #define fchown be_fchown
95 #endif /* __BEOS__ */
97 VALUE rb_cFile;
98 VALUE rb_mFileTest;
99 VALUE rb_cStat;
101 static VALUE
102 rb_get_path_check(VALUE obj, int check)
104 VALUE tmp;
105 ID to_path;
107 if (check) rb_check_safe_obj(obj);
108 tmp = rb_check_string_type(obj);
109 if (!NIL_P(tmp)) goto exit;
112 CONST_ID(to_path, "to_path");
113 if (rb_respond_to(obj, to_path)) {
114 tmp = rb_funcall(obj, to_path, 0, 0);
116 else {
117 tmp = obj;
119 exit:
120 StringValueCStr(tmp);
121 if (check && obj != tmp) {
122 rb_check_safe_obj(tmp);
124 return rb_str_new4(tmp);
127 VALUE
128 rb_get_path_no_checksafe(VALUE obj)
130 return rb_get_path_check(obj, 0);
133 VALUE
134 rb_get_path(VALUE obj)
136 return rb_get_path_check(obj, 1);
139 static long
140 apply2files(void (*func)(const char *, void *), VALUE vargs, void *arg)
142 long i;
143 volatile VALUE path;
145 rb_secure(4);
146 for (i=0; i<RARRAY_LEN(vargs); i++) {
147 path = rb_get_path(RARRAY_PTR(vargs)[i]);
148 (*func)(StringValueCStr(path), arg);
151 return RARRAY_LEN(vargs);
155 * call-seq:
156 * file.path -> filename
158 * Returns the pathname used to create <i>file</i> as a string. Does
159 * not normalize the name.
161 * File.new("testfile").path #=> "testfile"
162 * File.new("/tmp/../tmp/xxx", "w").path #=> "/tmp/../tmp/xxx"
166 static VALUE
167 rb_file_path(VALUE obj)
169 rb_io_t *fptr;
171 fptr = RFILE(rb_io_taint_check(obj))->fptr;
172 rb_io_check_initialized(fptr);
173 if (!fptr->path) return Qnil;
174 return rb_tainted_str_new2(fptr->path);
177 static VALUE
178 stat_new_0(VALUE klass, struct stat *st)
180 struct stat *nst = 0;
182 if (st) {
183 nst = ALLOC(struct stat);
184 *nst = *st;
186 return Data_Wrap_Struct(klass, NULL, -1, nst);
189 static VALUE
190 stat_new(struct stat *st)
192 return stat_new_0(rb_cStat, st);
195 static struct stat*
196 get_stat(VALUE self)
198 struct stat* st;
199 Data_Get_Struct(self, struct stat, st);
200 if (!st) rb_raise(rb_eTypeError, "uninitialized File::Stat");
201 return st;
204 static struct timespec stat_mtimespec(struct stat *st);
207 * call-seq:
208 * stat <=> other_stat => -1, 0, 1
210 * Compares <code>File::Stat</code> objects by comparing their
211 * respective modification times.
213 * f1 = File.new("f1", "w")
214 * sleep 1
215 * f2 = File.new("f2", "w")
216 * f1.stat <=> f2.stat #=> -1
219 static VALUE
220 rb_stat_cmp(VALUE self, VALUE other)
222 if (rb_obj_is_kind_of(other, rb_obj_class(self))) {
223 struct timespec ts1 = stat_mtimespec(get_stat(self));
224 struct timespec ts2 = stat_mtimespec(get_stat(other));
225 if (ts1.tv_sec == ts2.tv_sec) {
226 if (ts1.tv_nsec == ts2.tv_nsec) return INT2FIX(0);
227 if (ts1.tv_nsec < ts2.tv_nsec) return INT2FIX(-1);
228 return INT2FIX(1);
230 if (ts1.tv_sec < ts2.tv_sec) return INT2FIX(-1);
231 return INT2FIX(1);
233 return Qnil;
236 #define ST2UINT(val) ((val) & ~(~1UL << (sizeof(val) * CHAR_BIT - 1)))
239 * call-seq:
240 * stat.dev => fixnum
242 * Returns an integer representing the device on which <i>stat</i>
243 * resides.
245 * File.stat("testfile").dev #=> 774
248 static VALUE
249 rb_stat_dev(VALUE self)
251 return INT2NUM(get_stat(self)->st_dev);
255 * call-seq:
256 * stat.dev_major => fixnum
258 * Returns the major part of <code>File_Stat#dev</code> or
259 * <code>nil</code>.
261 * File.stat("/dev/fd1").dev_major #=> 2
262 * File.stat("/dev/tty").dev_major #=> 5
265 static VALUE
266 rb_stat_dev_major(VALUE self)
268 #if defined(major)
269 long dev = get_stat(self)->st_dev;
270 return ULONG2NUM(major(dev));
271 #else
272 return Qnil;
273 #endif
277 * call-seq:
278 * stat.dev_minor => fixnum
280 * Returns the minor part of <code>File_Stat#dev</code> or
281 * <code>nil</code>.
283 * File.stat("/dev/fd1").dev_minor #=> 1
284 * File.stat("/dev/tty").dev_minor #=> 0
287 static VALUE
288 rb_stat_dev_minor(VALUE self)
290 #if defined(minor)
291 long dev = get_stat(self)->st_dev;
292 return ULONG2NUM(minor(dev));
293 #else
294 return Qnil;
295 #endif
300 * call-seq:
301 * stat.ino => fixnum
303 * Returns the inode number for <i>stat</i>.
305 * File.stat("testfile").ino #=> 1083669
309 static VALUE
310 rb_stat_ino(VALUE self)
312 #ifdef HUGE_ST_INO
313 return ULL2NUM(get_stat(self)->st_ino);
314 #else
315 return ULONG2NUM(get_stat(self)->st_ino);
316 #endif
320 * call-seq:
321 * stat.mode => fixnum
323 * Returns an integer representing the permission bits of
324 * <i>stat</i>. The meaning of the bits is platform dependent; on
325 * Unix systems, see <code>stat(2)</code>.
327 * File.chmod(0644, "testfile") #=> 1
328 * s = File.stat("testfile")
329 * sprintf("%o", s.mode) #=> "100644"
332 static VALUE
333 rb_stat_mode(VALUE self)
335 return UINT2NUM(ST2UINT(get_stat(self)->st_mode));
339 * call-seq:
340 * stat.nlink => fixnum
342 * Returns the number of hard links to <i>stat</i>.
344 * File.stat("testfile").nlink #=> 1
345 * File.link("testfile", "testfile.bak") #=> 0
346 * File.stat("testfile").nlink #=> 2
350 static VALUE
351 rb_stat_nlink(VALUE self)
353 return UINT2NUM(get_stat(self)->st_nlink);
358 * call-seq:
359 * stat.uid => fixnum
361 * Returns the numeric user id of the owner of <i>stat</i>.
363 * File.stat("testfile").uid #=> 501
367 static VALUE
368 rb_stat_uid(VALUE self)
370 return UIDT2NUM(get_stat(self)->st_uid);
374 * call-seq:
375 * stat.gid => fixnum
377 * Returns the numeric group id of the owner of <i>stat</i>.
379 * File.stat("testfile").gid #=> 500
383 static VALUE
384 rb_stat_gid(VALUE self)
386 return GIDT2NUM(get_stat(self)->st_gid);
391 * call-seq:
392 * stat.rdev => fixnum or nil
394 * Returns an integer representing the device type on which
395 * <i>stat</i> resides. Returns <code>nil</code> if the operating
396 * system doesn't support this feature.
398 * File.stat("/dev/fd1").rdev #=> 513
399 * File.stat("/dev/tty").rdev #=> 1280
402 static VALUE
403 rb_stat_rdev(VALUE self)
405 #ifdef HAVE_ST_RDEV
406 return ULONG2NUM(get_stat(self)->st_rdev);
407 #else
408 return Qnil;
409 #endif
413 * call-seq:
414 * stat.rdev_major => fixnum
416 * Returns the major part of <code>File_Stat#rdev</code> or
417 * <code>nil</code>.
419 * File.stat("/dev/fd1").rdev_major #=> 2
420 * File.stat("/dev/tty").rdev_major #=> 5
423 static VALUE
424 rb_stat_rdev_major(VALUE self)
426 #if defined(HAVE_ST_RDEV) && defined(major)
427 long rdev = get_stat(self)->st_rdev;
428 return ULONG2NUM(major(rdev));
429 #else
430 return Qnil;
431 #endif
435 * call-seq:
436 * stat.rdev_minor => fixnum
438 * Returns the minor part of <code>File_Stat#rdev</code> or
439 * <code>nil</code>.
441 * File.stat("/dev/fd1").rdev_minor #=> 1
442 * File.stat("/dev/tty").rdev_minor #=> 0
445 static VALUE
446 rb_stat_rdev_minor(VALUE self)
448 #if defined(HAVE_ST_RDEV) && defined(minor)
449 long rdev = get_stat(self)->st_rdev;
450 return ULONG2NUM(minor(rdev));
451 #else
452 return Qnil;
453 #endif
457 * call-seq:
458 * stat.size => fixnum
460 * Returns the size of <i>stat</i> in bytes.
462 * File.stat("testfile").size #=> 66
465 static VALUE
466 rb_stat_size(VALUE self)
468 return OFFT2NUM(get_stat(self)->st_size);
472 * call-seq:
473 * stat.blksize => integer or nil
475 * Returns the native file system's block size. Will return <code>nil</code>
476 * on platforms that don't support this information.
478 * File.stat("testfile").blksize #=> 4096
482 static VALUE
483 rb_stat_blksize(VALUE self)
485 #ifdef HAVE_ST_BLKSIZE
486 return ULONG2NUM(get_stat(self)->st_blksize);
487 #else
488 return Qnil;
489 #endif
493 * call-seq:
494 * stat.blocks => integer or nil
496 * Returns the number of native file system blocks allocated for this
497 * file, or <code>nil</code> if the operating system doesn't
498 * support this feature.
500 * File.stat("testfile").blocks #=> 2
503 static VALUE
504 rb_stat_blocks(VALUE self)
506 #ifdef HAVE_ST_BLOCKS
507 return ULONG2NUM(get_stat(self)->st_blocks);
508 #else
509 return Qnil;
510 #endif
513 static struct timespec
514 stat_atimespec(struct stat *st)
516 struct timespec ts;
517 ts.tv_sec = st->st_atime;
518 #if defined(HAVE_STRUCT_STAT_ST_ATIM)
519 ts.tv_nsec = st->st_atim.tv_nsec;
520 #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
521 ts.tv_nsec = st->st_atimespec.tv_nsec;
522 #elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC)
523 ts.tv_nsec = st->st_atimensec;
524 #else
525 ts.tv_nsec = 0;
526 #endif
527 return ts;
530 static VALUE
531 stat_atime(struct stat *st)
533 struct timespec ts = stat_atimespec(st);
534 return rb_time_nano_new(ts.tv_sec, ts.tv_nsec);
537 static struct timespec
538 stat_mtimespec(struct stat *st)
540 struct timespec ts;
541 ts.tv_sec = st->st_mtime;
542 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
543 ts.tv_nsec = st->st_mtim.tv_nsec;
544 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
545 ts.tv_nsec = st->st_mtimespec.tv_nsec;
546 #elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
547 ts.tv_nsec = st->st_mtimensec;
548 #else
549 ts.tv_nsec = 0;
550 #endif
551 return ts;
554 static VALUE
555 stat_mtime(struct stat *st)
557 struct timespec ts = stat_mtimespec(st);
558 return rb_time_nano_new(ts.tv_sec, ts.tv_nsec);
561 static struct timespec
562 stat_ctimespec(struct stat *st)
564 struct timespec ts;
565 ts.tv_sec = st->st_ctime;
566 #if defined(HAVE_STRUCT_STAT_ST_CTIM)
567 ts.tv_nsec = st->st_ctim.tv_nsec;
568 #elif defined(HAVE_STRUCT_STAT_ST_CTIMESPEC)
569 ts.tv_nsec = st->st_ctimespec.tv_nsec;
570 #elif defined(HAVE_STRUCT_STAT_ST_CTIMENSEC)
571 ts.tv_nsec = st->st_ctimensec;
572 #else
573 ts.tv_nsec = 0;
574 #endif
575 return ts;
578 static VALUE
579 stat_ctime(struct stat *st)
581 struct timespec ts = stat_ctimespec(st);
582 return rb_time_nano_new(ts.tv_sec, ts.tv_nsec);
586 * call-seq:
587 * stat.atime => time
589 * Returns the last access time for this file as an object of class
590 * <code>Time</code>.
592 * File.stat("testfile").atime #=> Wed Dec 31 18:00:00 CST 1969
596 static VALUE
597 rb_stat_atime(VALUE self)
599 return stat_atime(get_stat(self));
603 * call-seq:
604 * stat.mtime -> aTime
606 * Returns the modification time of <i>stat</i>.
608 * File.stat("testfile").mtime #=> Wed Apr 09 08:53:14 CDT 2003
612 static VALUE
613 rb_stat_mtime(VALUE self)
615 return stat_mtime(get_stat(self));
619 * call-seq:
620 * stat.ctime -> aTime
622 * Returns the change time for <i>stat</i> (that is, the time
623 * directory information about the file was changed, not the file
624 * itself).
626 * File.stat("testfile").ctime #=> Wed Apr 09 08:53:14 CDT 2003
630 static VALUE
631 rb_stat_ctime(VALUE self)
633 return stat_ctime(get_stat(self));
637 * call-seq:
638 * stat.inspect => string
640 * Produce a nicely formatted description of <i>stat</i>.
642 * File.stat("/etc/passwd").inspect
643 * #=> "#<File::Stat dev=0xe000005, ino=1078078, mode=0100644,
644 * nlink=1, uid=0, gid=0, rdev=0x0, size=1374, blksize=4096,
645 * blocks=8, atime=Wed Dec 10 10:16:12 CST 2003,
646 * mtime=Fri Sep 12 15:41:41 CDT 2003,
647 * ctime=Mon Oct 27 11:20:27 CST 2003>"
650 static VALUE
651 rb_stat_inspect(VALUE self)
653 VALUE str;
654 int i;
655 static const struct {
656 const char *name;
657 VALUE (*func)(VALUE);
658 } member[] = {
659 {"dev", rb_stat_dev},
660 {"ino", rb_stat_ino},
661 {"mode", rb_stat_mode},
662 {"nlink", rb_stat_nlink},
663 {"uid", rb_stat_uid},
664 {"gid", rb_stat_gid},
665 {"rdev", rb_stat_rdev},
666 {"size", rb_stat_size},
667 {"blksize", rb_stat_blksize},
668 {"blocks", rb_stat_blocks},
669 {"atime", rb_stat_atime},
670 {"mtime", rb_stat_mtime},
671 {"ctime", rb_stat_ctime},
674 str = rb_str_buf_new2("#<");
675 rb_str_buf_cat2(str, rb_obj_classname(self));
676 rb_str_buf_cat2(str, " ");
678 for (i = 0; i < sizeof(member)/sizeof(member[0]); i++) {
679 VALUE v;
681 if (i > 0) {
682 rb_str_buf_cat2(str, ", ");
684 rb_str_buf_cat2(str, member[i].name);
685 rb_str_buf_cat2(str, "=");
686 v = (*member[i].func)(self);
687 if (i == 2) { /* mode */
688 rb_str_catf(str, "0%lo", NUM2ULONG(v));
690 else if (i == 0 || i == 6) { /* dev/rdev */
691 rb_str_catf(str, "0x%lx", NUM2ULONG(v));
693 else {
694 rb_str_append(str, rb_inspect(v));
697 rb_str_buf_cat2(str, ">");
698 OBJ_INFECT(str, self);
700 return str;
703 static int
704 rb_stat(VALUE file, struct stat *st)
706 VALUE tmp;
708 rb_secure(2);
709 tmp = rb_check_convert_type(file, T_FILE, "IO", "to_io");
710 if (!NIL_P(tmp)) {
711 rb_io_t *fptr;
713 GetOpenFile(tmp, fptr);
714 return fstat(fptr->fd, st);
716 FilePathValue(file);
717 return stat(StringValueCStr(file), st);
720 #ifdef _WIN32
721 static HANDLE
722 w32_io_info(VALUE *file, BY_HANDLE_FILE_INFORMATION *st)
724 VALUE tmp;
725 HANDLE f, ret = 0;
727 tmp = rb_check_convert_type(*file, T_FILE, "IO", "to_io");
728 if (!NIL_P(tmp)) {
729 rb_io_t *fptr;
731 GetOpenFile(tmp, fptr);
732 f = (HANDLE)rb_w32_get_osfhandle(fptr->fd);
733 if (f == (HANDLE)-1) return INVALID_HANDLE_VALUE;
735 else {
736 FilePathValue(*file);
737 f = CreateFile(StringValueCStr(*file), 0,
738 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
739 rb_w32_iswin95() ? 0 : FILE_FLAG_BACKUP_SEMANTICS, NULL);
740 if (f == INVALID_HANDLE_VALUE) return f;
741 ret = f;
743 if (GetFileType(f) == FILE_TYPE_DISK) {
744 ZeroMemory(st, sizeof(*st));
745 if (GetFileInformationByHandle(f, st)) return ret;
747 if (ret) CloseHandle(ret);
748 return INVALID_HANDLE_VALUE;
750 #endif
753 * call-seq:
754 * File.stat(file_name) => stat
756 * Returns a <code>File::Stat</code> object for the named file (see
757 * <code>File::Stat</code>).
759 * File.stat("testfile").mtime #=> Tue Apr 08 12:58:04 CDT 2003
763 static VALUE
764 rb_file_s_stat(VALUE klass, VALUE fname)
766 struct stat st;
768 rb_secure(4);
769 FilePathValue(fname);
770 if (rb_stat(fname, &st) < 0) {
771 rb_sys_fail(StringValueCStr(fname));
773 return stat_new(&st);
777 * call-seq:
778 * ios.stat => stat
780 * Returns status information for <em>ios</em> as an object of type
781 * <code>File::Stat</code>.
783 * f = File.new("testfile")
784 * s = f.stat
785 * "%o" % s.mode #=> "100644"
786 * s.blksize #=> 4096
787 * s.atime #=> Wed Apr 09 08:53:54 CDT 2003
791 static VALUE
792 rb_io_stat(VALUE obj)
794 rb_io_t *fptr;
795 struct stat st;
797 GetOpenFile(obj, fptr);
798 if (fstat(fptr->fd, &st) == -1) {
799 rb_sys_fail(fptr->path);
801 return stat_new(&st);
805 * call-seq:
806 * File.lstat(file_name) => stat
808 * Same as <code>File::stat</code>, but does not follow the last symbolic
809 * link. Instead, reports on the link itself.
811 * File.symlink("testfile", "link2test") #=> 0
812 * File.stat("testfile").size #=> 66
813 * File.lstat("link2test").size #=> 8
814 * File.stat("link2test").size #=> 66
818 static VALUE
819 rb_file_s_lstat(VALUE klass, VALUE fname)
821 #ifdef HAVE_LSTAT
822 struct stat st;
824 rb_secure(2);
825 FilePathValue(fname);
826 if (lstat(StringValueCStr(fname), &st) == -1) {
827 rb_sys_fail(RSTRING_PTR(fname));
829 return stat_new(&st);
830 #else
831 return rb_file_s_stat(klass, fname);
832 #endif
837 * call-seq:
838 * file.lstat => stat
840 * Same as <code>IO#stat</code>, but does not follow the last symbolic
841 * link. Instead, reports on the link itself.
843 * File.symlink("testfile", "link2test") #=> 0
844 * File.stat("testfile").size #=> 66
845 * f = File.new("link2test")
846 * f.lstat.size #=> 8
847 * f.stat.size #=> 66
850 static VALUE
851 rb_file_lstat(VALUE obj)
853 #ifdef HAVE_LSTAT
854 rb_io_t *fptr;
855 struct stat st;
857 rb_secure(2);
858 GetOpenFile(obj, fptr);
859 if (!fptr->path) return Qnil;
860 if (lstat(fptr->path, &st) == -1) {
861 rb_sys_fail(fptr->path);
863 return stat_new(&st);
864 #else
865 return rb_io_stat(obj);
866 #endif
869 #ifndef HAVE_GROUP_MEMBER
870 static int
871 group_member(GETGROUPS_T gid)
873 #ifndef _WIN32
874 if (getgid() == gid || getegid() == gid)
875 return Qtrue;
877 # ifdef HAVE_GETGROUPS
878 # ifndef NGROUPS
879 # ifdef NGROUPS_MAX
880 # define NGROUPS NGROUPS_MAX
881 # else
882 # define NGROUPS 32
883 # endif
884 # endif
886 GETGROUPS_T gary[NGROUPS];
887 int anum;
889 anum = getgroups(NGROUPS, gary);
890 while (--anum >= 0)
891 if (gary[anum] == gid)
892 return Qtrue;
894 # endif
895 #endif
896 return Qfalse;
898 #endif
900 #ifndef S_IXUGO
901 # define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
902 #endif
904 #if defined(S_IXGRP) && !defined(_WIN32) && !defined(__CYGWIN__)
905 #define USE_GETEUID 1
906 #endif
908 #ifndef HAVE_EACCESS
910 eaccess(const char *path, int mode)
912 #ifdef USE_GETEUID
913 struct stat st;
914 rb_uid_t euid;
916 if (stat(path, &st) < 0) return -1;
918 euid = geteuid();
920 if (euid == 0) {
921 /* Root can read or write any file. */
922 if (!(mode & X_OK))
923 return 0;
925 /* Root can execute any file that has any one of the execute
926 bits set. */
927 if (st.st_mode & S_IXUGO)
928 return 0;
930 return -1;
933 if (st.st_uid == euid) /* owner */
934 mode <<= 6;
935 else if (group_member(st.st_gid))
936 mode <<= 3;
938 if ((st.st_mode & mode) == mode) return 0;
940 return -1;
941 #else
942 # if defined(_MSC_VER) || defined(__MINGW32__)
943 mode &= ~1;
944 # endif
945 return access(path, mode);
946 #endif
948 #endif
952 * Document-class: FileTest
954 * <code>FileTest</code> implements file test operations similar to
955 * those used in <code>File::Stat</code>. It exists as a standalone
956 * module, and its methods are also insinuated into the <code>File</code>
957 * class. (Note that this is not done by inclusion: the interpreter cheats).
963 * File.directory?(file_name) => true or false
964 * File.directory?(file_name) => true or false
966 * Returns <code>true</code> if the named file is a directory,
967 * <code>false</code> otherwise.
969 * File.directory?(".")
973 * Document-method: exist?
975 * call-seq:
976 * Dir.exist?(file_name) => true or false
977 * Dir.exists?(file_name) => true or false
979 * Returns <code>true</code> if the named file is a directory,
980 * <code>false</code> otherwise.
985 * Document-method: directory?
987 * call-seq:
988 * File.directory?(file_name) => true or false
990 * Returns <code>true</code> if the named file is a directory,
991 * <code>false</code> otherwise.
993 * File.directory?(".")
996 VALUE
997 rb_file_directory_p(VALUE obj, VALUE fname)
999 #ifndef S_ISDIR
1000 # define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR)
1001 #endif
1003 struct stat st;
1005 if (rb_stat(fname, &st) < 0) return Qfalse;
1006 if (S_ISDIR(st.st_mode)) return Qtrue;
1007 return Qfalse;
1012 * call-seq:
1013 * File.pipe?(file_name) => true or false
1015 * Returns <code>true</code> if the named file is a pipe.
1018 static VALUE
1019 rb_file_pipe_p(VALUE obj, VALUE fname)
1021 #ifdef S_IFIFO
1022 # ifndef S_ISFIFO
1023 # define S_ISFIFO(m) ((m & S_IFMT) == S_IFIFO)
1024 # endif
1026 struct stat st;
1028 if (rb_stat(fname, &st) < 0) return Qfalse;
1029 if (S_ISFIFO(st.st_mode)) return Qtrue;
1031 #endif
1032 return Qfalse;
1036 * call-seq:
1037 * File.symlink?(file_name) => true or false
1039 * Returns <code>true</code> if the named file is a symbolic link.
1042 static VALUE
1043 rb_file_symlink_p(VALUE obj, VALUE fname)
1045 #ifndef S_ISLNK
1046 # ifdef _S_ISLNK
1047 # define S_ISLNK(m) _S_ISLNK(m)
1048 # else
1049 # ifdef _S_IFLNK
1050 # define S_ISLNK(m) ((m & S_IFMT) == _S_IFLNK)
1051 # else
1052 # ifdef S_IFLNK
1053 # define S_ISLNK(m) ((m & S_IFMT) == S_IFLNK)
1054 # endif
1055 # endif
1056 # endif
1057 #endif
1059 #ifdef S_ISLNK
1060 struct stat st;
1062 rb_secure(2);
1063 FilePathValue(fname);
1064 if (lstat(StringValueCStr(fname), &st) < 0) return Qfalse;
1065 if (S_ISLNK(st.st_mode)) return Qtrue;
1066 #endif
1068 return Qfalse;
1072 * call-seq:
1073 * File.socket?(file_name) => true or false
1075 * Returns <code>true</code> if the named file is a socket.
1078 static VALUE
1079 rb_file_socket_p(VALUE obj, VALUE fname)
1081 #ifndef S_ISSOCK
1082 # ifdef _S_ISSOCK
1083 # define S_ISSOCK(m) _S_ISSOCK(m)
1084 # else
1085 # ifdef _S_IFSOCK
1086 # define S_ISSOCK(m) ((m & S_IFMT) == _S_IFSOCK)
1087 # else
1088 # ifdef S_IFSOCK
1089 # define S_ISSOCK(m) ((m & S_IFMT) == S_IFSOCK)
1090 # endif
1091 # endif
1092 # endif
1093 #endif
1095 #ifdef S_ISSOCK
1096 struct stat st;
1098 if (rb_stat(fname, &st) < 0) return Qfalse;
1099 if (S_ISSOCK(st.st_mode)) return Qtrue;
1101 #endif
1102 return Qfalse;
1106 * call-seq:
1107 * File.blockdev?(file_name) => true or false
1109 * Returns <code>true</code> if the named file is a block device.
1112 static VALUE
1113 rb_file_blockdev_p(VALUE obj, VALUE fname)
1115 #ifndef S_ISBLK
1116 # ifdef S_IFBLK
1117 # define S_ISBLK(m) ((m & S_IFMT) == S_IFBLK)
1118 # else
1119 # define S_ISBLK(m) (0) /* anytime false */
1120 # endif
1121 #endif
1123 #ifdef S_ISBLK
1124 struct stat st;
1126 if (rb_stat(fname, &st) < 0) return Qfalse;
1127 if (S_ISBLK(st.st_mode)) return Qtrue;
1129 #endif
1130 return Qfalse;
1134 * call-seq:
1135 * File.chardev?(file_name) => true or false
1137 * Returns <code>true</code> if the named file is a character device.
1139 static VALUE
1140 rb_file_chardev_p(VALUE obj, VALUE fname)
1142 #ifndef S_ISCHR
1143 # define S_ISCHR(m) ((m & S_IFMT) == S_IFCHR)
1144 #endif
1146 struct stat st;
1148 if (rb_stat(fname, &st) < 0) return Qfalse;
1149 if (S_ISCHR(st.st_mode)) return Qtrue;
1151 return Qfalse;
1156 * call-seq:
1157 * File.exist?(file_name) => true or false
1158 * File.exists?(file_name) => true or false
1160 * Return <code>true</code> if the named file exists.
1163 static VALUE
1164 rb_file_exist_p(VALUE obj, VALUE fname)
1166 struct stat st;
1168 if (rb_stat(fname, &st) < 0) return Qfalse;
1169 return Qtrue;
1173 * call-seq:
1174 * File.readable?(file_name) => true or false
1176 * Returns <code>true</code> if the named file is readable by the effective
1177 * user id of this process.
1180 static VALUE
1181 rb_file_readable_p(VALUE obj, VALUE fname)
1183 rb_secure(2);
1184 FilePathValue(fname);
1185 if (eaccess(StringValueCStr(fname), R_OK) < 0) return Qfalse;
1186 return Qtrue;
1190 * call-seq:
1191 * File.readable_real?(file_name) => true or false
1193 * Returns <code>true</code> if the named file is readable by the real
1194 * user id of this process.
1197 static VALUE
1198 rb_file_readable_real_p(VALUE obj, VALUE fname)
1200 rb_secure(2);
1201 FilePathValue(fname);
1202 if (access(StringValueCStr(fname), R_OK) < 0) return Qfalse;
1203 return Qtrue;
1206 #ifndef S_IRUGO
1207 # define S_IRUGO (S_IRUSR | S_IRGRP | S_IROTH)
1208 #endif
1210 #ifndef S_IWUGO
1211 # define S_IWUGO (S_IWUSR | S_IWGRP | S_IWOTH)
1212 #endif
1215 * call-seq:
1216 * File.world_readable?(file_name) => fixnum or nil
1218 * If <i>file_name</i> is readable by others, returns an integer
1219 * representing the file permission bits of <i>file_name</i>. Returns
1220 * <code>nil</code> otherwise. The meaning of the bits is platform
1221 * dependent; on Unix systems, see <code>stat(2)</code>.
1223 * File.world_readable?("/etc/passwd") # => 420
1224 * m = File.world_readable?("/etc/passwd")
1225 * sprintf("%o", m) # => "644"
1228 static VALUE
1229 rb_file_world_readable_p(VALUE obj, VALUE fname)
1231 #ifdef S_IROTH
1232 struct stat st;
1234 if (rb_stat(fname, &st) < 0) return Qnil;
1235 if ((st.st_mode & (S_IROTH)) == S_IROTH) {
1236 return UINT2NUM(st.st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
1238 #endif
1239 return Qnil;
1243 * call-seq:
1244 * File.writable?(file_name) => true or false
1246 * Returns <code>true</code> if the named file is writable by the effective
1247 * user id of this process.
1250 static VALUE
1251 rb_file_writable_p(VALUE obj, VALUE fname)
1253 rb_secure(2);
1254 FilePathValue(fname);
1255 if (eaccess(StringValueCStr(fname), W_OK) < 0) return Qfalse;
1256 return Qtrue;
1260 * call-seq:
1261 * File.writable_real?(file_name) => true or false
1263 * Returns <code>true</code> if the named file is writable by the real
1264 * user id of this process.
1267 static VALUE
1268 rb_file_writable_real_p(VALUE obj, VALUE fname)
1270 rb_secure(2);
1271 FilePathValue(fname);
1272 if (access(StringValueCStr(fname), W_OK) < 0) return Qfalse;
1273 return Qtrue;
1277 * call-seq:
1278 * File.world_writable?(file_name) => fixnum or nil
1280 * If <i>file_name</i> is writable by others, returns an integer
1281 * representing the file permission bits of <i>file_name</i>. Returns
1282 * <code>nil</code> otherwise. The meaning of the bits is platform
1283 * dependent; on Unix systems, see <code>stat(2)</code>.
1285 * File.world_writable?("/tmp") #=> 511
1286 * m = File.world_writable?("/tmp")
1287 * sprintf("%o", m) #=> "777"
1290 static VALUE
1291 rb_file_world_writable_p(VALUE obj, VALUE fname)
1293 #ifdef S_IWOTH
1294 struct stat st;
1296 if (rb_stat(fname, &st) < 0) return Qfalse;
1297 if ((st.st_mode & (S_IWOTH)) == S_IWOTH) {
1298 return UINT2NUM(st.st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
1300 #endif
1301 return Qnil;
1305 * call-seq:
1306 * File.executable?(file_name) => true or false
1308 * Returns <code>true</code> if the named file is executable by the effective
1309 * user id of this process.
1312 static VALUE
1313 rb_file_executable_p(VALUE obj, VALUE fname)
1315 rb_secure(2);
1316 FilePathValue(fname);
1317 if (eaccess(StringValueCStr(fname), X_OK) < 0) return Qfalse;
1318 return Qtrue;
1322 * call-seq:
1323 * File.executable_real?(file_name) => true or false
1325 * Returns <code>true</code> if the named file is executable by the real
1326 * user id of this process.
1329 static VALUE
1330 rb_file_executable_real_p(VALUE obj, VALUE fname)
1332 rb_secure(2);
1333 FilePathValue(fname);
1334 if (access(StringValueCStr(fname), X_OK) < 0) return Qfalse;
1335 return Qtrue;
1338 #ifndef S_ISREG
1339 # define S_ISREG(m) ((m & S_IFMT) == S_IFREG)
1340 #endif
1343 * call-seq:
1344 * File.file?(file_name) => true or false
1346 * Returns <code>true</code> if the named file exists and is a
1347 * regular file.
1350 static VALUE
1351 rb_file_file_p(VALUE obj, VALUE fname)
1353 struct stat st;
1355 if (rb_stat(fname, &st) < 0) return Qfalse;
1356 if (S_ISREG(st.st_mode)) return Qtrue;
1357 return Qfalse;
1361 * call-seq:
1362 * File.zero?(file_name) => true or false
1364 * Returns <code>true</code> if the named file exists and has
1365 * a zero size.
1368 static VALUE
1369 rb_file_zero_p(VALUE obj, VALUE fname)
1371 struct stat st;
1373 if (rb_stat(fname, &st) < 0) return Qfalse;
1374 if (st.st_size == 0) return Qtrue;
1375 return Qfalse;
1379 * call-seq:
1380 * File.size?(file_name) => Integer or nil
1382 * Returns +nil+ if +file_name+ doesn't exist or has zero size, the size of the
1383 * file otherwise.
1386 static VALUE
1387 rb_file_size_p(VALUE obj, VALUE fname)
1389 struct stat st;
1391 if (rb_stat(fname, &st) < 0) return Qnil;
1392 if (st.st_size == 0) return Qnil;
1393 return OFFT2NUM(st.st_size);
1397 * call-seq:
1398 * File.owned?(file_name) => true or false
1400 * Returns <code>true</code> if the named file exists and the
1401 * effective used id of the calling process is the owner of
1402 * the file.
1405 static VALUE
1406 rb_file_owned_p(VALUE obj, VALUE fname)
1408 struct stat st;
1410 if (rb_stat(fname, &st) < 0) return Qfalse;
1411 if (st.st_uid == geteuid()) return Qtrue;
1412 return Qfalse;
1415 static VALUE
1416 rb_file_rowned_p(VALUE obj, VALUE fname)
1418 struct stat st;
1420 if (rb_stat(fname, &st) < 0) return Qfalse;
1421 if (st.st_uid == getuid()) return Qtrue;
1422 return Qfalse;
1426 * call-seq:
1427 * File.grpowned?(file_name) => true or false
1429 * Returns <code>true</code> if the named file exists and the
1430 * effective group id of the calling process is the owner of
1431 * the file. Returns <code>false</code> on Windows.
1434 static VALUE
1435 rb_file_grpowned_p(VALUE obj, VALUE fname)
1437 #ifndef _WIN32
1438 struct stat st;
1440 if (rb_stat(fname, &st) < 0) return Qfalse;
1441 if (group_member(st.st_gid)) return Qtrue;
1442 #endif
1443 return Qfalse;
1446 #if defined(S_ISUID) || defined(S_ISGID) || defined(S_ISVTX)
1447 static VALUE
1448 check3rdbyte(VALUE fname, int mode)
1450 struct stat st;
1452 rb_secure(2);
1453 FilePathValue(fname);
1454 if (stat(StringValueCStr(fname), &st) < 0) return Qfalse;
1455 if (st.st_mode & mode) return Qtrue;
1456 return Qfalse;
1458 #endif
1461 * call-seq:
1462 * File.setuid?(file_name) => true or false
1464 * Returns <code>true</code> if the named file has the setuid bit set.
1467 static VALUE
1468 rb_file_suid_p(VALUE obj, VALUE fname)
1470 #ifdef S_ISUID
1471 return check3rdbyte(fname, S_ISUID);
1472 #else
1473 return Qfalse;
1474 #endif
1478 * call-seq:
1479 * File.setgid?(file_name) => true or false
1481 * Returns <code>true</code> if the named file has the setgid bit set.
1484 static VALUE
1485 rb_file_sgid_p(VALUE obj, VALUE fname)
1487 #ifdef S_ISGID
1488 return check3rdbyte(fname, S_ISGID);
1489 #else
1490 return Qfalse;
1491 #endif
1495 * call-seq:
1496 * File.sticky?(file_name) => true or false
1498 * Returns <code>true</code> if the named file has the sticky bit set.
1501 static VALUE
1502 rb_file_sticky_p(VALUE obj, VALUE fname)
1504 #ifdef S_ISVTX
1505 return check3rdbyte(fname, S_ISVTX);
1506 #else
1507 return Qnil;
1508 #endif
1512 * call-seq:
1513 * File.identical?(file_1, file_2) => true or false
1515 * Returns <code>true</code> if the named files are identical.
1517 * open("a", "w") {}
1518 * p File.identical?("a", "a") #=> true
1519 * p File.identical?("a", "./a") #=> true
1520 * File.link("a", "b")
1521 * p File.identical?("a", "b") #=> true
1522 * File.symlink("a", "c")
1523 * p File.identical?("a", "c") #=> true
1524 * open("d", "w") {}
1525 * p File.identical?("a", "d") #=> false
1528 static VALUE
1529 rb_file_identical_p(VALUE obj, VALUE fname1, VALUE fname2)
1531 #ifndef DOSISH
1532 struct stat st1, st2;
1534 if (rb_stat(fname1, &st1) < 0) return Qfalse;
1535 if (rb_stat(fname2, &st2) < 0) return Qfalse;
1536 if (st1.st_dev != st2.st_dev) return Qfalse;
1537 if (st1.st_ino != st2.st_ino) return Qfalse;
1538 #else
1539 #ifdef _WIN32
1540 BY_HANDLE_FILE_INFORMATION st1, st2;
1541 HANDLE f1 = 0, f2 = 0;
1542 #endif
1544 rb_secure(2);
1545 #ifdef _WIN32
1546 f1 = w32_io_info(&fname1, &st1);
1547 if (f1 == INVALID_HANDLE_VALUE) return Qfalse;
1548 f2 = w32_io_info(&fname2, &st2);
1549 if (f1) CloseHandle(f1);
1550 if (f2 == INVALID_HANDLE_VALUE) return Qfalse;
1551 if (f2) CloseHandle(f2);
1553 if (st1.dwVolumeSerialNumber == st2.dwVolumeSerialNumber &&
1554 st1.nFileIndexHigh == st2.nFileIndexHigh &&
1555 st1.nFileIndexLow == st2.nFileIndexLow)
1556 return Qtrue;
1557 if (!f1 || !f2) return Qfalse;
1558 if (rb_w32_iswin95()) return Qfalse;
1559 #else
1560 FilePathValue(fname1);
1561 fname1 = rb_str_new4(fname1);
1562 FilePathValue(fname2);
1563 if (access(RSTRING_PTR(fname1), 0)) return Qfalse;
1564 if (access(RSTRING_PTR(fname2), 0)) return Qfalse;
1565 #endif
1566 fname1 = rb_file_expand_path(fname1, Qnil);
1567 fname2 = rb_file_expand_path(fname2, Qnil);
1568 if (RSTRING_LEN(fname1) != RSTRING_LEN(fname2)) return Qfalse;
1569 if (rb_memcicmp(RSTRING_PTR(fname1), RSTRING_PTR(fname2), RSTRING_LEN(fname1)))
1570 return Qfalse;
1571 #endif
1572 return Qtrue;
1576 * call-seq:
1577 * File.size(file_name) => integer
1579 * Returns the size of <code>file_name</code>.
1582 static VALUE
1583 rb_file_s_size(VALUE klass, VALUE fname)
1585 struct stat st;
1587 if (rb_stat(fname, &st) < 0)
1588 rb_sys_fail(StringValueCStr(fname));
1589 return OFFT2NUM(st.st_size);
1592 static VALUE
1593 rb_file_ftype(const struct stat *st)
1595 const char *t;
1597 if (S_ISREG(st->st_mode)) {
1598 t = "file";
1600 else if (S_ISDIR(st->st_mode)) {
1601 t = "directory";
1603 else if (S_ISCHR(st->st_mode)) {
1604 t = "characterSpecial";
1606 #ifdef S_ISBLK
1607 else if (S_ISBLK(st->st_mode)) {
1608 t = "blockSpecial";
1610 #endif
1611 #ifdef S_ISFIFO
1612 else if (S_ISFIFO(st->st_mode)) {
1613 t = "fifo";
1615 #endif
1616 #ifdef S_ISLNK
1617 else if (S_ISLNK(st->st_mode)) {
1618 t = "link";
1620 #endif
1621 #ifdef S_ISSOCK
1622 else if (S_ISSOCK(st->st_mode)) {
1623 t = "socket";
1625 #endif
1626 else {
1627 t = "unknown";
1630 return rb_usascii_str_new2(t);
1634 * call-seq:
1635 * File.ftype(file_name) => string
1637 * Identifies the type of the named file; the return string is one of
1638 * ``<code>file</code>'', ``<code>directory</code>'',
1639 * ``<code>characterSpecial</code>'', ``<code>blockSpecial</code>'',
1640 * ``<code>fifo</code>'', ``<code>link</code>'',
1641 * ``<code>socket</code>'', or ``<code>unknown</code>''.
1643 * File.ftype("testfile") #=> "file"
1644 * File.ftype("/dev/tty") #=> "characterSpecial"
1645 * File.ftype("/tmp/.X11-unix/X0") #=> "socket"
1648 static VALUE
1649 rb_file_s_ftype(VALUE klass, VALUE fname)
1651 struct stat st;
1653 rb_secure(2);
1654 FilePathValue(fname);
1655 if (lstat(StringValueCStr(fname), &st) == -1) {
1656 rb_sys_fail(RSTRING_PTR(fname));
1659 return rb_file_ftype(&st);
1663 * call-seq:
1664 * File.atime(file_name) => time
1666 * Returns the last access time for the named file as a Time object).
1668 * File.atime("testfile") #=> Wed Apr 09 08:51:48 CDT 2003
1672 static VALUE
1673 rb_file_s_atime(VALUE klass, VALUE fname)
1675 struct stat st;
1677 if (rb_stat(fname, &st) < 0)
1678 rb_sys_fail(StringValueCStr(fname));
1679 return stat_atime(&st);
1683 * call-seq:
1684 * file.atime => time
1686 * Returns the last access time (a <code>Time</code> object)
1687 * for <i>file</i>, or epoch if <i>file</i> has not been accessed.
1689 * File.new("testfile").atime #=> Wed Dec 31 18:00:00 CST 1969
1693 static VALUE
1694 rb_file_atime(VALUE obj)
1696 rb_io_t *fptr;
1697 struct stat st;
1699 GetOpenFile(obj, fptr);
1700 if (fstat(fptr->fd, &st) == -1) {
1701 rb_sys_fail(fptr->path);
1703 return stat_atime(&st);
1707 * call-seq:
1708 * File.mtime(file_name) => time
1710 * Returns the modification time for the named file as a Time object.
1712 * File.mtime("testfile") #=> Tue Apr 08 12:58:04 CDT 2003
1716 static VALUE
1717 rb_file_s_mtime(VALUE klass, VALUE fname)
1719 struct stat st;
1721 if (rb_stat(fname, &st) < 0)
1722 rb_sys_fail(RSTRING_PTR(fname));
1723 return stat_mtime(&st);
1727 * call-seq:
1728 * file.mtime -> time
1730 * Returns the modification time for <i>file</i>.
1732 * File.new("testfile").mtime #=> Wed Apr 09 08:53:14 CDT 2003
1736 static VALUE
1737 rb_file_mtime(VALUE obj)
1739 rb_io_t *fptr;
1740 struct stat st;
1742 GetOpenFile(obj, fptr);
1743 if (fstat(fptr->fd, &st) == -1) {
1744 rb_sys_fail(fptr->path);
1746 return stat_mtime(&st);
1750 * call-seq:
1751 * File.ctime(file_name) => time
1753 * Returns the change time for the named file (the time at which
1754 * directory information about the file was changed, not the file
1755 * itself).
1757 * File.ctime("testfile") #=> Wed Apr 09 08:53:13 CDT 2003
1761 static VALUE
1762 rb_file_s_ctime(VALUE klass, VALUE fname)
1764 struct stat st;
1766 if (rb_stat(fname, &st) < 0)
1767 rb_sys_fail(RSTRING_PTR(fname));
1768 return stat_ctime(&st);
1772 * call-seq:
1773 * file.ctime -> time
1775 * Returns the change time for <i>file</i> (that is, the time directory
1776 * information about the file was changed, not the file itself).
1778 * File.new("testfile").ctime #=> Wed Apr 09 08:53:14 CDT 2003
1782 static VALUE
1783 rb_file_ctime(VALUE obj)
1785 rb_io_t *fptr;
1786 struct stat st;
1788 GetOpenFile(obj, fptr);
1789 if (fstat(fptr->fd, &st) == -1) {
1790 rb_sys_fail(fptr->path);
1792 return stat_ctime(&st);
1795 static void
1796 chmod_internal(const char *path, void *mode)
1798 if (chmod(path, *(int *)mode) < 0)
1799 rb_sys_fail(path);
1803 * call-seq:
1804 * File.chmod(mode_int, file_name, ... ) -> integer
1806 * Changes permission bits on the named file(s) to the bit pattern
1807 * represented by <i>mode_int</i>. Actual effects are operating system
1808 * dependent (see the beginning of this section). On Unix systems, see
1809 * <code>chmod(2)</code> for details. Returns the number of files
1810 * processed.
1812 * File.chmod(0644, "testfile", "out") #=> 2
1815 static VALUE
1816 rb_file_s_chmod(int argc, VALUE *argv)
1818 VALUE vmode;
1819 VALUE rest;
1820 int mode;
1821 long n;
1823 rb_secure(2);
1824 rb_scan_args(argc, argv, "1*", &vmode, &rest);
1825 mode = NUM2INT(vmode);
1827 n = apply2files(chmod_internal, rest, &mode);
1828 return LONG2FIX(n);
1832 * call-seq:
1833 * file.chmod(mode_int) => 0
1835 * Changes permission bits on <i>file</i> to the bit pattern
1836 * represented by <i>mode_int</i>. Actual effects are platform
1837 * dependent; on Unix systems, see <code>chmod(2)</code> for details.
1838 * Follows symbolic links. Also see <code>File#lchmod</code>.
1840 * f = File.new("out", "w");
1841 * f.chmod(0644) #=> 0
1844 static VALUE
1845 rb_file_chmod(VALUE obj, VALUE vmode)
1847 rb_io_t *fptr;
1848 int mode;
1850 rb_secure(2);
1851 mode = NUM2INT(vmode);
1853 GetOpenFile(obj, fptr);
1854 #ifdef HAVE_FCHMOD
1855 if (fchmod(fptr->fd, mode) == -1)
1856 rb_sys_fail(fptr->path);
1857 #else
1858 if (!fptr->path) return Qnil;
1859 if (chmod(fptr->path, mode) == -1)
1860 rb_sys_fail(fptr->path);
1861 #endif
1863 return INT2FIX(0);
1866 #if defined(HAVE_LCHMOD)
1867 static void
1868 lchmod_internal(const char *path, void *mode)
1870 if (lchmod(path, (int)(VALUE)mode) < 0)
1871 rb_sys_fail(path);
1875 * call-seq:
1876 * File.lchmod(mode_int, file_name, ...) => integer
1878 * Equivalent to <code>File::chmod</code>, but does not follow symbolic
1879 * links (so it will change the permissions associated with the link,
1880 * not the file referenced by the link). Often not available.
1884 static VALUE
1885 rb_file_s_lchmod(int argc, VALUE *argv)
1887 VALUE vmode;
1888 VALUE rest;
1889 long mode, n;
1891 rb_secure(2);
1892 rb_scan_args(argc, argv, "1*", &vmode, &rest);
1893 mode = NUM2INT(vmode);
1895 n = apply2files(lchmod_internal, rest, (void *)(long)mode);
1896 return LONG2FIX(n);
1898 #else
1899 static VALUE
1900 rb_file_s_lchmod(int argc, VALUE *argv)
1902 rb_notimplement();
1903 return Qnil; /* not reached */
1905 #endif
1907 struct chown_args {
1908 rb_uid_t owner;
1909 rb_gid_t group;
1912 static void
1913 chown_internal(const char *path, void *arg)
1915 struct chown_args *args = arg;
1916 if (chown(path, args->owner, args->group) < 0)
1917 rb_sys_fail(path);
1921 * call-seq:
1922 * File.chown(owner_int, group_int, file_name,... ) -> integer
1924 * Changes the owner and group of the named file(s) to the given
1925 * numeric owner and group id's. Only a process with superuser
1926 * privileges may change the owner of a file. The current owner of a
1927 * file may change the file's group to any group to which the owner
1928 * belongs. A <code>nil</code> or -1 owner or group id is ignored.
1929 * Returns the number of files processed.
1931 * File.chown(nil, 100, "testfile")
1935 static VALUE
1936 rb_file_s_chown(int argc, VALUE *argv)
1938 VALUE o, g, rest;
1939 struct chown_args arg;
1940 long n;
1942 rb_secure(2);
1943 rb_scan_args(argc, argv, "2*", &o, &g, &rest);
1944 if (NIL_P(o)) {
1945 arg.owner = -1;
1947 else {
1948 arg.owner = NUM2UIDT(o);
1950 if (NIL_P(g)) {
1951 arg.group = -1;
1953 else {
1954 arg.group = NUM2GIDT(g);
1957 n = apply2files(chown_internal, rest, &arg);
1958 return LONG2FIX(n);
1962 * call-seq:
1963 * file.chown(owner_int, group_int ) => 0
1965 * Changes the owner and group of <i>file</i> to the given numeric
1966 * owner and group id's. Only a process with superuser privileges may
1967 * change the owner of a file. The current owner of a file may change
1968 * the file's group to any group to which the owner belongs. A
1969 * <code>nil</code> or -1 owner or group id is ignored. Follows
1970 * symbolic links. See also <code>File#lchown</code>.
1972 * File.new("testfile").chown(502, 1000)
1976 static VALUE
1977 rb_file_chown(VALUE obj, VALUE owner, VALUE group)
1979 rb_io_t *fptr;
1980 int o, g;
1982 rb_secure(2);
1983 o = NIL_P(owner) ? -1 : NUM2INT(owner);
1984 g = NIL_P(group) ? -1 : NUM2INT(group);
1985 GetOpenFile(obj, fptr);
1986 #if defined(DJGPP) || defined(__CYGWIN32__) || defined(_WIN32) || defined(__EMX__)
1987 if (!fptr->path) return Qnil;
1988 if (chown(fptr->path, o, g) == -1)
1989 rb_sys_fail(fptr->path);
1990 #else
1991 if (fchown(fptr->fd, o, g) == -1)
1992 rb_sys_fail(fptr->path);
1993 #endif
1995 return INT2FIX(0);
1998 #if defined(HAVE_LCHOWN) && !defined(__CHECKER__)
1999 static void
2000 lchown_internal(const char *path, void *arg)
2002 struct chown_args *args = arg;
2003 if (lchown(path, args->owner, args->group) < 0)
2004 rb_sys_fail(path);
2009 * call-seq:
2010 * file.lchown(owner_int, group_int, file_name,..) => integer
2012 * Equivalent to <code>File::chown</code>, but does not follow symbolic
2013 * links (so it will change the owner associated with the link, not the
2014 * file referenced by the link). Often not available. Returns number
2015 * of files in the argument list.
2019 static VALUE
2020 rb_file_s_lchown(int argc, VALUE *argv)
2022 VALUE o, g, rest;
2023 struct chown_args arg;
2024 long n;
2026 rb_secure(2);
2027 rb_scan_args(argc, argv, "2*", &o, &g, &rest);
2028 if (NIL_P(o)) {
2029 arg.owner = -1;
2031 else {
2032 arg.owner = NUM2UIDT(o);
2034 if (NIL_P(g)) {
2035 arg.group = -1;
2037 else {
2038 arg.group = NUM2GIDT(g);
2041 n = apply2files(lchown_internal, rest, &arg);
2042 return LONG2FIX(n);
2044 #else
2045 static VALUE
2046 rb_file_s_lchown(int argc, VALUE *argv)
2048 rb_notimplement();
2050 #endif
2052 struct timespec rb_time_timespec(VALUE time);
2054 #if defined(HAVE_UTIMES)
2056 static void
2057 utime_internal(const char *path, void *arg)
2059 struct timespec *tsp = arg;
2060 struct timeval tvbuf[2], *tvp = arg;
2062 #ifdef HAVE_UTIMENSAT
2063 static int try_utimensat = 1;
2065 if (try_utimensat) {
2066 struct timespec *tsp = arg;
2067 if (utimensat(AT_FDCWD, path, tsp, 0) < 0) {
2068 if (errno == ENOSYS) {
2069 try_utimensat = 0;
2070 goto no_utimensat;
2072 rb_sys_fail(path);
2074 return;
2076 no_utimensat:
2077 #endif
2079 if (tsp) {
2080 tvbuf[0].tv_sec = tsp[0].tv_sec;
2081 tvbuf[0].tv_usec = tsp[0].tv_nsec / 1000;
2082 tvbuf[1].tv_sec = tsp[1].tv_sec;
2083 tvbuf[1].tv_usec = tsp[1].tv_nsec / 1000;
2084 tvp = tvbuf;
2086 if (utimes(path, tvp) < 0)
2087 rb_sys_fail(path);
2090 #else
2092 #if !defined HAVE_UTIME_H && !defined HAVE_SYS_UTIME_H
2093 struct utimbuf {
2094 long actime;
2095 long modtime;
2097 #endif
2099 static void
2100 utime_internal(const char *path, void *arg)
2102 struct timespec *tsp = arg;
2103 struct utimbuf utbuf, *utp = NULL;
2104 if (tsp) {
2105 utbuf.actime = tsp[0].tv_sec;
2106 utbuf.modtime = tsp[1].tv_sec;
2107 utp = &utbuf;
2109 if (utime(path, utp) < 0)
2110 rb_sys_fail(path);
2113 #endif
2116 * call-seq:
2117 * File.utime(atime, mtime, file_name,...) => integer
2119 * Sets the access and modification times of each
2120 * named file to the first two arguments. Returns
2121 * the number of file names in the argument list.
2124 static VALUE
2125 rb_file_s_utime(int argc, VALUE *argv)
2127 VALUE atime, mtime, rest;
2128 struct timespec tss[2], *tsp = NULL;
2129 long n;
2131 rb_secure(2);
2132 rb_scan_args(argc, argv, "2*", &atime, &mtime, &rest);
2134 if (!NIL_P(atime) || !NIL_P(mtime)) {
2135 tsp = tss;
2136 tsp[0] = rb_time_timespec(atime);
2137 tsp[1] = rb_time_timespec(mtime);
2140 n = apply2files(utime_internal, rest, tsp);
2141 return LONG2FIX(n);
2145 NORETURN(static void sys_fail2(VALUE,VALUE));
2146 static void
2147 sys_fail2(VALUE s1, VALUE s2)
2149 char *buf;
2150 #ifdef MAX_PATH
2151 const int max_pathlen = MAX_PATH;
2152 #else
2153 const int max_pathlen = MAXPATHLEN;
2154 #endif
2155 const char *e1, *e2;
2156 int len = 5;
2157 int l1 = RSTRING_LEN(s1), l2 = RSTRING_LEN(s2);
2159 e1 = e2 = "";
2160 if (l1 > max_pathlen) {
2161 l1 = max_pathlen - 3;
2162 e1 = "...";
2163 len += 3;
2165 if (l2 > max_pathlen) {
2166 l2 = max_pathlen - 3;
2167 e2 = "...";
2168 len += 3;
2170 len += l1 + l2;
2171 buf = ALLOCA_N(char, len);
2172 snprintf(buf, len, "(%.*s%s, %.*s%s)",
2173 l1, RSTRING_PTR(s1), e1,
2174 l2, RSTRING_PTR(s2), e2);
2175 rb_sys_fail(buf);
2179 * call-seq:
2180 * File.link(old_name, new_name) => 0
2182 * Creates a new name for an existing file using a hard link. Will not
2183 * overwrite <i>new_name</i> if it already exists (raising a subclass
2184 * of <code>SystemCallError</code>). Not available on all platforms.
2186 * File.link("testfile", ".testfile") #=> 0
2187 * IO.readlines(".testfile")[0] #=> "This is line one\n"
2190 static VALUE
2191 rb_file_s_link(VALUE klass, VALUE from, VALUE to)
2193 #ifdef HAVE_LINK
2194 rb_secure(2);
2195 FilePathValue(from);
2196 FilePathValue(to);
2198 if (link(StringValueCStr(from), StringValueCStr(to)) < 0) {
2199 sys_fail2(from, to);
2201 return INT2FIX(0);
2202 #else
2203 rb_notimplement();
2204 return Qnil; /* not reached */
2205 #endif
2209 * call-seq:
2210 * File.symlink(old_name, new_name) => 0
2212 * Creates a symbolic link called <i>new_name</i> for the existing file
2213 * <i>old_name</i>. Raises a <code>NotImplemented</code> exception on
2214 * platforms that do not support symbolic links.
2216 * File.symlink("testfile", "link2test") #=> 0
2220 static VALUE
2221 rb_file_s_symlink(VALUE klass, VALUE from, VALUE to)
2223 #ifdef HAVE_SYMLINK
2224 rb_secure(2);
2225 FilePathValue(from);
2226 FilePathValue(to);
2228 if (symlink(StringValueCStr(from), StringValueCStr(to)) < 0) {
2229 sys_fail2(from, to);
2231 return INT2FIX(0);
2232 #else
2233 rb_notimplement();
2234 return Qnil; /* not reached */
2235 #endif
2239 * call-seq:
2240 * File.readlink(link_name) -> file_name
2242 * Returns the name of the file referenced by the given link.
2243 * Not available on all platforms.
2245 * File.symlink("testfile", "link2test") #=> 0
2246 * File.readlink("link2test") #=> "testfile"
2249 static VALUE
2250 rb_file_s_readlink(VALUE klass, VALUE path)
2252 #ifdef HAVE_READLINK
2253 char *buf;
2254 int size = 100;
2255 int rv;
2256 VALUE v;
2258 rb_secure(2);
2259 FilePathValue(path);
2260 buf = xmalloc(size);
2261 while ((rv = readlink(RSTRING_PTR(path), buf, size)) == size
2262 #ifdef _AIX
2263 || (rv < 0 && errno == ERANGE) /* quirky behavior of GPFS */
2264 #endif
2266 size *= 2;
2267 buf = xrealloc(buf, size);
2269 if (rv < 0) {
2270 xfree(buf);
2271 rb_sys_fail(RSTRING_PTR(path));
2273 v = rb_tainted_str_new(buf, rv);
2274 xfree(buf);
2276 return v;
2277 #else
2278 rb_notimplement();
2279 return Qnil; /* not reached */
2280 #endif
2283 static void
2284 unlink_internal(const char *path, void *arg)
2286 if (unlink(path) < 0)
2287 rb_sys_fail(path);
2291 * call-seq:
2292 * File.delete(file_name, ...) => integer
2293 * File.unlink(file_name, ...) => integer
2295 * Deletes the named files, returning the number of names
2296 * passed as arguments. Raises an exception on any error.
2297 * See also <code>Dir::rmdir</code>.
2300 static VALUE
2301 rb_file_s_unlink(VALUE klass, VALUE args)
2303 long n;
2305 rb_secure(2);
2306 n = apply2files(unlink_internal, args, 0);
2307 return LONG2FIX(n);
2311 * call-seq:
2312 * File.rename(old_name, new_name) => 0
2314 * Renames the given file to the new name. Raises a
2315 * <code>SystemCallError</code> if the file cannot be renamed.
2317 * File.rename("afile", "afile.bak") #=> 0
2320 static VALUE
2321 rb_file_s_rename(VALUE klass, VALUE from, VALUE to)
2323 const char *src, *dst;
2325 rb_secure(2);
2326 FilePathValue(from);
2327 FilePathValue(to);
2328 src = StringValueCStr(from);
2329 dst = StringValueCStr(to);
2330 #if defined __CYGWIN__
2331 errno = 0;
2332 #endif
2333 if (rename(src, dst) < 0) {
2334 #if defined DOSISH && !defined _WIN32
2335 switch (errno) {
2336 case EEXIST:
2337 #if defined (__EMX__)
2338 case EACCES:
2339 #endif
2340 if (chmod(dst, 0666) == 0 &&
2341 unlink(dst) == 0 &&
2342 rename(src, dst) == 0)
2343 return INT2FIX(0);
2345 #endif
2346 sys_fail2(from, to);
2349 return INT2FIX(0);
2353 * call-seq:
2354 * File.umask() => integer
2355 * File.umask(integer) => integer
2357 * Returns the current umask value for this process. If the optional
2358 * argument is given, set the umask to that value and return the
2359 * previous value. Umask values are <em>subtracted</em> from the
2360 * default permissions, so a umask of <code>0222</code> would make a
2361 * file read-only for everyone.
2363 * File.umask(0006) #=> 18
2364 * File.umask #=> 6
2367 static VALUE
2368 rb_file_s_umask(int argc, VALUE *argv)
2370 int omask = 0;
2372 rb_secure(2);
2373 if (argc == 0) {
2374 omask = umask(0);
2375 umask(omask);
2377 else if (argc == 1) {
2378 omask = umask(NUM2INT(argv[0]));
2380 else {
2381 rb_raise(rb_eArgError, "wrong number of arguments");
2383 return INT2FIX(omask);
2386 #ifdef __CYGWIN__
2387 #undef DOSISH
2388 #endif
2389 #if defined __CYGWIN__ || defined DOSISH
2390 #define DOSISH_UNC
2391 #define DOSISH_DRIVE_LETTER
2392 #define isdirsep(x) ((x) == '/' || (x) == '\\')
2393 #else
2394 #define isdirsep(x) ((x) == '/')
2395 #endif
2397 #if defined _WIN32 || defined __CYGWIN__
2398 #define USE_NTFS 1
2399 #else
2400 #define USE_NTFS 0
2401 #endif
2403 #if USE_NTFS
2404 #define istrailinggabage(x) ((x) == '.' || (x) == ' ')
2405 #else
2406 #define istrailinggabage(x) 0
2407 #endif
2409 #ifndef CharNext /* defined as CharNext[AW] on Windows. */
2410 # if defined(DJGPP)
2411 # define CharNext(p) ((p) + mblen(p, RUBY_MBCHAR_MAXSIZE))
2412 # else
2413 # define CharNext(p) ((p) + 1)
2414 # endif
2415 #endif
2417 #ifdef DOSISH_DRIVE_LETTER
2418 static inline int
2419 has_drive_letter(const char *buf)
2421 if (ISALPHA(buf[0]) && buf[1] == ':') {
2422 return 1;
2424 else {
2425 return 0;
2429 static char*
2430 getcwdofdrv(int drv)
2432 char drive[4];
2433 char *drvcwd, *oldcwd;
2435 drive[0] = drv;
2436 drive[1] = ':';
2437 drive[2] = '\0';
2439 /* the only way that I know to get the current directory
2440 of a particular drive is to change chdir() to that drive,
2441 so save the old cwd before chdir()
2443 oldcwd = my_getcwd();
2444 if (chdir(drive) == 0) {
2445 drvcwd = my_getcwd();
2446 chdir(oldcwd);
2447 xfree(oldcwd);
2449 else {
2450 /* perhaps the drive is not exist. we return only drive letter */
2451 drvcwd = strdup(drive);
2453 return drvcwd;
2455 #endif
2457 static inline char *
2458 skiproot(const char *path)
2460 #ifdef DOSISH_DRIVE_LETTER
2461 if (has_drive_letter(path)) path += 2;
2462 #endif
2463 while (isdirsep(*path)) path++;
2464 return (char *)path;
2467 #define nextdirsep rb_path_next
2468 char *
2469 rb_path_next(const char *s)
2471 while (*s && !isdirsep(*s)) {
2472 s = CharNext(s);
2474 return (char *)s;
2477 #if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
2478 #define skipprefix rb_path_skip_prefix
2479 #else
2480 #define skipprefix(path) (path)
2481 #endif
2482 char *
2483 rb_path_skip_prefix(const char *path)
2485 #if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
2486 #ifdef DOSISH_UNC
2487 if (isdirsep(path[0]) && isdirsep(path[1])) {
2488 path += 2;
2489 while (isdirsep(*path)) path++;
2490 if (*(path = nextdirsep(path)) && path[1] && !isdirsep(path[1]))
2491 path = nextdirsep(path + 1);
2492 return (char *)path;
2494 #endif
2495 #ifdef DOSISH_DRIVE_LETTER
2496 if (has_drive_letter(path))
2497 return (char *)(path + 2);
2498 #endif
2499 #endif
2500 return (char *)path;
2503 #define strrdirsep rb_path_last_separator
2504 char *
2505 rb_path_last_separator(const char *path)
2507 char *last = NULL;
2508 while (*path) {
2509 if (isdirsep(*path)) {
2510 const char *tmp = path++;
2511 while (isdirsep(*path)) path++;
2512 if (!*path) break;
2513 last = (char *)tmp;
2515 else {
2516 path = CharNext(path);
2519 return last;
2522 static char *
2523 chompdirsep(const char *path)
2525 while (*path) {
2526 if (isdirsep(*path)) {
2527 const char *last = path++;
2528 while (isdirsep(*path)) path++;
2529 if (!*path) return (char *)last;
2531 else {
2532 path = CharNext(path);
2535 return (char *)path;
2538 char *
2539 rb_path_end(const char *path)
2541 if (isdirsep(*path)) path++;
2542 return chompdirsep(path);
2545 #if USE_NTFS
2546 static char *
2547 ntfs_tail(const char *path)
2549 while (*path == '.') path++;
2550 while (*path && *path != ':') {
2551 if (istrailinggabage(*path)) {
2552 const char *last = path++;
2553 while (istrailinggabage(*path)) path++;
2554 if (!*path || *path == ':') return (char *)last;
2556 else if (isdirsep(*path)) {
2557 const char *last = path++;
2558 while (isdirsep(*path)) path++;
2559 if (!*path) return (char *)last;
2560 if (*path == ':') path++;
2562 else {
2563 path = CharNext(path);
2566 return (char *)path;
2568 #endif
2570 #define BUFCHECK(cond) do {\
2571 long bdiff = p - buf;\
2572 if (cond) {\
2573 do {buflen *= 2;} while (cond);\
2574 rb_str_resize(result, buflen);\
2575 buf = RSTRING_PTR(result);\
2576 p = buf + bdiff;\
2577 pend = buf + buflen;\
2579 } while (0)
2581 #define BUFINIT() (\
2582 p = buf = RSTRING_PTR(result),\
2583 buflen = RSTRING_LEN(result),\
2584 pend = p + buflen)
2586 #define SET_EXTERNAL_ENCODING() (\
2587 (void)(extenc || (extenc = rb_default_external_encoding())),\
2588 rb_enc_associate(result, extenc))
2590 static int is_absolute_path(const char*);
2592 static VALUE
2593 file_expand_path(VALUE fname, VALUE dname, VALUE result)
2595 const char *s, *b;
2596 char *buf, *p, *pend, *root;
2597 long buflen, dirlen;
2598 int tainted;
2599 rb_encoding *extenc = 0;
2601 FilePathValue(fname);
2602 s = StringValuePtr(fname);
2603 BUFINIT();
2604 tainted = OBJ_TAINTED(fname);
2606 if (s[0] == '~') {
2607 if (isdirsep(s[1]) || s[1] == '\0') {
2608 const char *dir = getenv("HOME");
2610 if (!dir) {
2611 rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `%s'", s);
2613 dirlen = strlen(dir);
2614 BUFCHECK(dirlen > buflen);
2615 strcpy(buf, dir);
2616 #if defined DOSISH || defined __CYGWIN__
2617 for (p = buf; *p; p = CharNext(p)) {
2618 if (*p == '\\') {
2619 *p = '/';
2622 #else
2623 p = buf + strlen(dir);
2624 #endif
2625 s++;
2626 tainted = 1;
2627 SET_EXTERNAL_ENCODING();
2629 else {
2630 #ifdef HAVE_PWD_H
2631 struct passwd *pwPtr;
2632 s++;
2633 #endif
2634 s = nextdirsep(b = s);
2635 BUFCHECK(bdiff + (s-b) >= buflen);
2636 memcpy(p, b, s-b);
2637 p += s-b;
2638 *p = '\0';
2639 #ifdef HAVE_PWD_H
2640 pwPtr = getpwnam(buf);
2641 if (!pwPtr) {
2642 endpwent();
2643 rb_raise(rb_eArgError, "user %s doesn't exist", buf);
2645 dirlen = strlen(pwPtr->pw_dir);
2646 BUFCHECK(dirlen > buflen);
2647 strcpy(buf, pwPtr->pw_dir);
2648 p = buf + strlen(pwPtr->pw_dir);
2649 endpwent();
2650 #endif
2653 #ifdef DOSISH_DRIVE_LETTER
2654 /* skip drive letter */
2655 else if (has_drive_letter(s)) {
2656 if (isdirsep(s[2])) {
2657 /* specified drive letter, and full path */
2658 /* skip drive letter */
2659 BUFCHECK(bdiff + 2 >= buflen);
2660 memcpy(p, s, 2);
2661 p += 2;
2662 s += 2;
2664 else {
2665 /* specified drive, but not full path */
2666 int same = 0;
2667 if (!NIL_P(dname)) {
2668 file_expand_path(dname, Qnil, result);
2669 BUFINIT();
2670 if (has_drive_letter(p) && TOLOWER(p[0]) == TOLOWER(s[0])) {
2671 /* ok, same drive */
2672 same = 1;
2675 if (!same) {
2676 char *dir = getcwdofdrv(*s);
2678 tainted = 1;
2679 dirlen = strlen(dir);
2680 BUFCHECK(dirlen > buflen);
2681 strcpy(buf, dir);
2682 xfree(dir);
2683 SET_EXTERNAL_ENCODING();
2685 p = chompdirsep(skiproot(buf));
2686 s += 2;
2689 #endif
2690 else if (!is_absolute_path(s)) {
2691 if (!NIL_P(dname)) {
2692 file_expand_path(dname, Qnil, result);
2693 BUFINIT();
2695 else {
2696 char *dir = my_getcwd();
2698 tainted = 1;
2699 dirlen = strlen(dir);
2700 BUFCHECK(dirlen > buflen);
2701 strcpy(buf, dir);
2702 xfree(dir);
2703 SET_EXTERNAL_ENCODING();
2705 #if defined DOSISH || defined __CYGWIN__
2706 if (isdirsep(*s)) {
2707 /* specified full path, but not drive letter nor UNC */
2708 /* we need to get the drive letter or UNC share name */
2709 p = skipprefix(buf);
2711 else
2712 #endif
2713 p = chompdirsep(skiproot(buf));
2715 else {
2716 b = s;
2717 do s++; while (isdirsep(*s));
2718 p = buf + (s - b);
2719 BUFCHECK(bdiff >= buflen);
2720 memset(buf, '/', p - buf);
2722 if (p > buf && p[-1] == '/')
2723 --p;
2724 else {
2725 ++buflen;
2726 BUFCHECK(bdiff >= buflen);
2727 *p = '/';
2730 p[1] = 0;
2731 root = skipprefix(buf);
2733 b = s;
2734 while (*s) {
2735 switch (*s) {
2736 case '.':
2737 if (b == s++) { /* beginning of path element */
2738 switch (*s) {
2739 case '\0':
2740 b = s;
2741 break;
2742 case '.':
2743 if (*(s+1) == '\0' || isdirsep(*(s+1))) {
2744 /* We must go back to the parent */
2745 char *n;
2746 *p = '\0';
2747 if (!(n = strrdirsep(root))) {
2748 *p = '/';
2750 else {
2751 p = n;
2753 b = ++s;
2755 #if USE_NTFS
2756 else {
2757 do *++s; while (istrailinggabage(*s));
2759 #endif
2760 break;
2761 case '/':
2762 #if defined DOSISH || defined __CYGWIN__
2763 case '\\':
2764 #endif
2765 b = ++s;
2766 break;
2767 default:
2768 /* ordinary path element, beginning don't move */
2769 break;
2772 #if USE_NTFS
2773 else {
2774 --s;
2775 case ' ': {
2776 const char *e = s;
2777 while (istrailinggabage(*s)) s++;
2778 if (!*s) {
2779 s = e;
2780 goto endpath;
2784 #endif
2785 break;
2786 case '/':
2787 #if defined DOSISH || defined __CYGWIN__
2788 case '\\':
2789 #endif
2790 if (s > b) {
2791 long rootdiff = root - buf;
2792 BUFCHECK(bdiff + (s-b+1) >= buflen);
2793 root = buf + rootdiff;
2794 memcpy(++p, b, s-b);
2795 p += s-b;
2796 *p = '/';
2798 b = ++s;
2799 break;
2800 default:
2801 s = CharNext(s);
2802 break;
2806 if (s > b) {
2807 #if USE_NTFS
2808 endpath:
2809 if (s > b + 6 && strncasecmp(s - 6, ":$DATA", 6) == 0) {
2810 /* alias of stream */
2811 /* get rid of a bug of x64 VC++ */
2812 if (*(s-7) == ':') s -= 7; /* prime */
2813 else if (memchr(b, ':', s - 6 - b)) s -= 6; /* alternative */
2815 #endif
2816 BUFCHECK(bdiff + (s-b) >= buflen);
2817 memcpy(++p, b, s-b);
2818 p += s-b;
2820 if (p == skiproot(buf) - 1) p++;
2822 #if USE_NTFS
2823 *p = '\0';
2824 if ((s = strrdirsep(b = buf)) != 0 && !strpbrk(s, "*?")) {
2825 size_t len;
2826 WIN32_FIND_DATA wfd;
2827 #ifdef __CYGWIN__
2828 int lnk_added = 0, is_symlink = 0;
2829 struct stat st;
2830 char w32buf[MAXPATHLEN];
2831 p = (char *)s;
2832 if (lstat(buf, &st) == 0 && S_ISLNK(st.st_mode)) {
2833 is_symlink = 1;
2834 *p = '\0';
2836 if (cygwin_conv_to_win32_path((*buf ? buf : "/"), w32buf) == 0) {
2837 b = w32buf;
2839 if (is_symlink && b == w32buf) {
2840 *p = '\\';
2841 strlcat(w32buf, p, sizeof(w32buf));
2842 len = strlen(p);
2843 if (len > 4 && STRCASECMP(p + len - 4, ".lnk") != 0) {
2844 lnk_added = 1;
2845 strlcat(w32buf, ".lnk", sizeof(w32buf));
2848 *p = '/';
2849 #endif
2850 HANDLE h = FindFirstFile(b, &wfd);
2851 if (h != INVALID_HANDLE_VALUE) {
2852 FindClose(h);
2853 len = strlen(wfd.cFileName);
2854 #ifdef __CYGWIN__
2855 if (lnk_added && len > 4 &&
2856 STRCASECMP(wfd.cFileName + len - 4, ".lnk") == 0) {
2857 wfd.cFileName[len -= 4] = '\0';
2859 #else
2860 p = (char *)s;
2861 #endif
2862 ++p;
2863 BUFCHECK(bdiff + len >= buflen);
2864 memcpy(p, wfd.cFileName, len + 1);
2865 p += len;
2867 #ifdef __CYGWIN__
2868 else {
2869 p += strlen(p);
2871 #endif
2873 #endif
2875 if (tainted) OBJ_TAINT(result);
2876 rb_str_set_len(result, p - buf);
2877 rb_enc_check(fname, result);
2878 return result;
2881 VALUE
2882 rb_file_expand_path(VALUE fname, VALUE dname)
2884 return file_expand_path(fname, dname, rb_usascii_str_new(0, MAXPATHLEN + 2));
2888 * call-seq:
2889 * File.expand_path(file_name [, dir_string] ) -> abs_file_name
2891 * Converts a pathname to an absolute pathname. Relative paths are
2892 * referenced from the current working directory of the process unless
2893 * <i>dir_string</i> is given, in which case it will be used as the
2894 * starting point. The given pathname may start with a
2895 * ``<code>~</code>'', which expands to the process owner's home
2896 * directory (the environment variable <code>HOME</code> must be set
2897 * correctly). ``<code>~</code><i>user</i>'' expands to the named
2898 * user's home directory.
2900 * File.expand_path("~oracle/bin") #=> "/home/oracle/bin"
2901 * File.expand_path("../../bin", "/tmp/x") #=> "/bin"
2904 VALUE
2905 rb_file_s_expand_path(int argc, VALUE *argv)
2907 VALUE fname, dname;
2909 if (argc == 1) {
2910 return rb_file_expand_path(argv[0], Qnil);
2912 rb_scan_args(argc, argv, "11", &fname, &dname);
2914 return rb_file_expand_path(fname, dname);
2917 static int
2918 rmext(const char *p, int l1, const char *e)
2920 int l2;
2922 if (!e) return 0;
2924 l2 = strlen(e);
2925 if (l2 == 2 && e[1] == '*') {
2926 unsigned char c = *e;
2927 e = p + l1;
2928 do {
2929 if (e <= p) return 0;
2930 } while (*--e != c);
2931 return e - p;
2933 if (l1 < l2) return l1;
2935 #if CASEFOLD_FILESYSTEM
2936 #define fncomp strncasecmp
2937 #else
2938 #define fncomp strncmp
2939 #endif
2940 if (fncomp(p+l1-l2, e, l2) == 0) {
2941 return l1-l2;
2943 return 0;
2947 * call-seq:
2948 * File.basename(file_name [, suffix] ) -> base_name
2950 * Returns the last component of the filename given in <i>file_name</i>,
2951 * which must be formed using forward slashes (``<code>/</code>'')
2952 * regardless of the separator used on the local file system. If
2953 * <i>suffix</i> is given and present at the end of <i>file_name</i>,
2954 * it is removed.
2956 * File.basename("/home/gumby/work/ruby.rb") #=> "ruby.rb"
2957 * File.basename("/home/gumby/work/ruby.rb", ".rb") #=> "ruby"
2960 static VALUE
2961 rb_file_s_basename(int argc, VALUE *argv)
2963 VALUE fname, fext, basename;
2964 const char *name, *p;
2965 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
2966 const char *root;
2967 #endif
2968 int f, n;
2970 if (rb_scan_args(argc, argv, "11", &fname, &fext) == 2) {
2971 StringValue(fext);
2973 FilePathStringValue(fname);
2974 if (RSTRING_LEN(fname) == 0 || !*(name = RSTRING_PTR(fname)))
2975 return fname;
2976 name = skipprefix(name);
2977 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
2978 root = name;
2979 #endif
2980 while (isdirsep(*name))
2981 name++;
2982 if (!*name) {
2983 p = name - 1;
2984 f = 1;
2985 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
2986 if (name != root) {
2987 /* has slashes */
2989 #ifdef DOSISH_DRIVE_LETTER
2990 else if (*p == ':') {
2991 p++;
2992 f = 0;
2994 #endif
2995 #ifdef DOSISH_UNC
2996 else {
2997 p = "/";
2999 #endif
3000 #endif
3002 else {
3003 if (!(p = strrdirsep(name))) {
3004 p = name;
3006 else {
3007 while (isdirsep(*p)) p++; /* skip last / */
3009 #if USE_NTFS
3010 n = ntfs_tail(p) - p;
3011 #else
3012 n = chompdirsep(p) - p;
3013 #endif
3014 if (NIL_P(fext) || !(f = rmext(p, n, StringValueCStr(fext)))) {
3015 f = n;
3017 if (f == RSTRING_LEN(fname)) return fname;
3019 basename = rb_str_new(p, f);
3020 rb_enc_copy(basename, fname);
3021 OBJ_INFECT(basename, fname);
3022 return basename;
3026 * call-seq:
3027 * File.dirname(file_name ) -> dir_name
3029 * Returns all components of the filename given in <i>file_name</i>
3030 * except the last one. The filename must be formed using forward
3031 * slashes (``<code>/</code>'') regardless of the separator used on the
3032 * local file system.
3034 * File.dirname("/home/gumby/work/ruby.rb") #=> "/home/gumby/work"
3037 static VALUE
3038 rb_file_s_dirname(VALUE klass, VALUE fname)
3040 const char *name, *root, *p;
3041 VALUE dirname;
3043 FilePathStringValue(fname);
3044 name = StringValueCStr(fname);
3045 root = skiproot(name);
3046 #ifdef DOSISH_UNC
3047 if (root > name + 1 && isdirsep(*name))
3048 root = skipprefix(name = root - 2);
3049 #else
3050 if (root > name + 1)
3051 name = root - 1;
3052 #endif
3053 p = strrdirsep(root);
3054 if (!p) {
3055 p = root;
3057 if (p == name)
3058 return rb_usascii_str_new2(".");
3059 #ifdef DOSISH_DRIVE_LETTER
3060 if (has_drive_letter(name) && isdirsep(*(name + 2))) {
3061 const char *top = skiproot(name + 2);
3062 dirname = rb_str_new(name, 3);
3063 rb_str_cat(dirname, top, p - top);
3065 else
3066 #endif
3067 dirname = rb_str_new(name, p - name);
3068 #ifdef DOSISH_DRIVE_LETTER
3069 if (has_drive_letter(name) && root == name + 2 && p - name == 2)
3070 rb_str_cat(dirname, ".", 1);
3071 #endif
3072 rb_enc_copy(dirname, fname);
3073 OBJ_INFECT(dirname, fname);
3074 return dirname;
3078 * call-seq:
3079 * File.extname(path) -> string
3081 * Returns the extension (the portion of file name in <i>path</i>
3082 * after the period).
3084 * File.extname("test.rb") #=> ".rb"
3085 * File.extname("a/b/d/test.rb") #=> ".rb"
3086 * File.extname("test") #=> ""
3087 * File.extname(".profile") #=> ""
3091 static VALUE
3092 rb_file_s_extname(VALUE klass, VALUE fname)
3094 const char *name, *p, *e;
3095 VALUE extname;
3097 FilePathStringValue(fname);
3098 name = StringValueCStr(fname);
3099 p = strrdirsep(name); /* get the last path component */
3100 if (!p)
3101 p = name;
3102 else
3103 name = ++p;
3105 e = 0;
3106 while (*p) {
3107 if (*p == '.' || istrailinggabage(*p)) {
3108 #if USE_NTFS
3109 const char *last = p++, *dot = last;
3110 while (istrailinggabage(*p)) {
3111 if (*p == '.') dot = p;
3112 p++;
3114 if (!*p || *p == ':') {
3115 p = last;
3116 break;
3118 if (*last == '.') e = dot;
3119 continue;
3120 #else
3121 e = p; /* get the last dot of the last component */
3122 #endif
3124 #if USE_NTFS
3125 else if (*p == ':') {
3126 break;
3128 #endif
3129 else if (isdirsep(*p))
3130 break;
3131 p = CharNext(p);
3133 if (!e || e == name || e+1 == p) /* no dot, or the only dot is first or end? */
3134 return rb_str_new(0, 0);
3135 extname = rb_str_new(e, p - e); /* keep the dot, too! */
3136 rb_enc_copy(extname, fname);
3137 OBJ_INFECT(extname, fname);
3138 return extname;
3142 * call-seq:
3143 * File.path(path) -> string
3145 * Returns the string representation of the path
3147 * File.path("/dev/null") #=> "/dev/null"
3148 * File.path(Pathname.new("/tmp")) #=> "/tmp"
3152 static VALUE
3153 rb_file_s_path(VALUE klass, VALUE fname)
3155 return rb_get_path(fname);
3159 * call-seq:
3160 * File.split(file_name) => array
3162 * Splits the given string into a directory and a file component and
3163 * returns them in a two-element array. See also
3164 * <code>File::dirname</code> and <code>File::basename</code>.
3166 * File.split("/home/gumby/.profile") #=> ["/home/gumby", ".profile"]
3169 static VALUE
3170 rb_file_s_split(VALUE klass, VALUE path)
3172 FilePathStringValue(path); /* get rid of converting twice */
3173 return rb_assoc_new(rb_file_s_dirname(Qnil, path), rb_file_s_basename(1,&path));
3176 static VALUE separator;
3178 static VALUE rb_file_join(VALUE ary, VALUE sep);
3180 static VALUE
3181 file_inspect_join(VALUE ary, VALUE argp, int recur)
3183 VALUE *arg = (VALUE *)argp;
3184 if (recur) return rb_usascii_str_new2("[...]");
3185 return rb_file_join(arg[0], arg[1]);
3188 static VALUE
3189 rb_file_join(VALUE ary, VALUE sep)
3191 long len, i;
3192 VALUE result, tmp;
3193 const char *name, *tail;
3195 if (RARRAY_LEN(ary) == 0) return rb_str_new(0, 0);
3197 len = 1;
3198 for (i=0; i<RARRAY_LEN(ary); i++) {
3199 if (TYPE(RARRAY_PTR(ary)[i]) == T_STRING) {
3200 len += RSTRING_LEN(RARRAY_PTR(ary)[i]);
3202 else {
3203 len += 10;
3206 if (!NIL_P(sep)) {
3207 StringValue(sep);
3208 len += RSTRING_LEN(sep) * RARRAY_LEN(ary) - 1;
3210 result = rb_str_buf_new(len);
3211 OBJ_INFECT(result, ary);
3212 for (i=0; i<RARRAY_LEN(ary); i++) {
3213 tmp = RARRAY_PTR(ary)[i];
3214 switch (TYPE(tmp)) {
3215 case T_STRING:
3216 break;
3217 case T_ARRAY:
3219 VALUE args[2];
3221 args[0] = tmp;
3222 args[1] = sep;
3223 tmp = rb_exec_recursive(file_inspect_join, ary, (VALUE)args);
3225 break;
3226 default:
3227 FilePathStringValue(tmp);
3229 name = StringValueCStr(result);
3230 if (i > 0 && !NIL_P(sep)) {
3231 tail = chompdirsep(name);
3232 if (RSTRING_PTR(tmp) && isdirsep(RSTRING_PTR(tmp)[0])) {
3233 rb_str_set_len(result, tail - name);
3235 else if (!*tail) {
3236 rb_str_buf_append(result, sep);
3239 rb_str_buf_append(result, tmp);
3242 return result;
3246 * call-seq:
3247 * File.join(string, ...) -> path
3249 * Returns a new string formed by joining the strings using
3250 * <code>File::SEPARATOR</code>.
3252 * File.join("usr", "mail", "gumby") #=> "usr/mail/gumby"
3256 static VALUE
3257 rb_file_s_join(VALUE klass, VALUE args)
3259 return rb_file_join(args, separator);
3263 * call-seq:
3264 * File.truncate(file_name, integer) => 0
3266 * Truncates the file <i>file_name</i> to be at most <i>integer</i>
3267 * bytes long. Not available on all platforms.
3269 * f = File.new("out", "w")
3270 * f.write("1234567890") #=> 10
3271 * f.close #=> nil
3272 * File.truncate("out", 5) #=> 0
3273 * File.size("out") #=> 5
3277 static VALUE
3278 rb_file_s_truncate(VALUE klass, VALUE path, VALUE len)
3280 off_t pos;
3282 rb_secure(2);
3283 pos = NUM2OFFT(len);
3284 FilePathValue(path);
3285 #ifdef HAVE_TRUNCATE
3286 if (truncate(StringValueCStr(path), pos) < 0)
3287 rb_sys_fail(RSTRING_PTR(path));
3288 #else
3289 # ifdef HAVE_CHSIZE
3291 int tmpfd;
3293 # ifdef _WIN32
3294 if ((tmpfd = open(StringValueCStr(path), O_RDWR)) < 0) {
3295 rb_sys_fail(RSTRING_PTR(path));
3297 # else
3298 if ((tmpfd = open(StringValueCStr(path), 0)) < 0) {
3299 rb_sys_fail(RSTRING_PTR(path));
3301 # endif
3302 if (chsize(tmpfd, pos) < 0) {
3303 close(tmpfd);
3304 rb_sys_fail(RSTRING_PTR(path));
3306 close(tmpfd);
3308 # else
3309 rb_notimplement();
3310 # endif
3311 #endif
3312 return INT2FIX(0);
3316 * call-seq:
3317 * file.truncate(integer) => 0
3319 * Truncates <i>file</i> to at most <i>integer</i> bytes. The file
3320 * must be opened for writing. Not available on all platforms.
3322 * f = File.new("out", "w")
3323 * f.syswrite("1234567890") #=> 10
3324 * f.truncate(5) #=> 0
3325 * f.close() #=> nil
3326 * File.size("out") #=> 5
3329 static VALUE
3330 rb_file_truncate(VALUE obj, VALUE len)
3332 rb_io_t *fptr;
3333 off_t pos;
3335 rb_secure(2);
3336 pos = NUM2OFFT(len);
3337 GetOpenFile(obj, fptr);
3338 if (!(fptr->mode & FMODE_WRITABLE)) {
3339 rb_raise(rb_eIOError, "not opened for writing");
3341 rb_io_flush(obj);
3342 #ifdef HAVE_FTRUNCATE
3343 if (ftruncate(fptr->fd, pos) < 0)
3344 rb_sys_fail(fptr->path);
3345 #else
3346 # ifdef HAVE_CHSIZE
3347 if (chsize(fptr->fd, pos) < 0)
3348 rb_sys_fail(fptr->path);
3349 # else
3350 rb_notimplement();
3351 # endif
3352 #endif
3353 return INT2FIX(0);
3356 # ifndef LOCK_SH
3357 # define LOCK_SH 1
3358 # endif
3359 # ifndef LOCK_EX
3360 # define LOCK_EX 2
3361 # endif
3362 # ifndef LOCK_NB
3363 # define LOCK_NB 4
3364 # endif
3365 # ifndef LOCK_UN
3366 # define LOCK_UN 8
3367 # endif
3369 #ifdef __CYGWIN__
3370 #include <winerror.h>
3371 extern unsigned long __attribute__((stdcall)) GetLastError(void);
3372 #endif
3374 static VALUE
3375 rb_thread_flock(void *data)
3377 #ifdef __CYGWIN__
3378 int old_errno = errno;
3379 #endif
3380 int *op = data, ret = flock(op[0], op[1]);
3382 #ifdef __CYGWIN__
3383 if (GetLastError() == ERROR_NOT_LOCKED) {
3384 ret = 0;
3385 errno = old_errno;
3387 #endif
3388 return (VALUE)ret;
3392 * call-seq:
3393 * file.flock (locking_constant ) => 0 or false
3395 * Locks or unlocks a file according to <i>locking_constant</i> (a
3396 * logical <em>or</em> of the values in the table below).
3397 * Returns <code>false</code> if <code>File::LOCK_NB</code> is
3398 * specified and the operation would otherwise have blocked. Not
3399 * available on all platforms.
3401 * Locking constants (in class File):
3403 * LOCK_EX | Exclusive lock. Only one process may hold an
3404 * | exclusive lock for a given file at a time.
3405 * ----------+------------------------------------------------
3406 * LOCK_NB | Don't block when locking. May be combined
3407 * | with other lock options using logical or.
3408 * ----------+------------------------------------------------
3409 * LOCK_SH | Shared lock. Multiple processes may each hold a
3410 * | shared lock for a given file at the same time.
3411 * ----------+------------------------------------------------
3412 * LOCK_UN | Unlock.
3414 * Example:
3416 * File.new("testfile").flock(File::LOCK_UN) #=> 0
3420 static VALUE
3421 rb_file_flock(VALUE obj, VALUE operation)
3423 #ifndef __CHECKER__
3424 rb_io_t *fptr;
3425 int op[2], op1;
3427 rb_secure(2);
3428 op[1] = op1 = NUM2INT(operation);
3429 GetOpenFile(obj, fptr);
3430 op[0] = fptr->fd;
3432 if (fptr->mode & FMODE_WRITABLE) {
3433 rb_io_flush(obj);
3435 while ((int)rb_thread_blocking_region(rb_thread_flock, op, RB_UBF_DFL, 0) < 0) {
3436 switch (errno) {
3437 case EAGAIN:
3438 case EACCES:
3439 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
3440 case EWOULDBLOCK:
3441 #endif
3442 if (op1 & LOCK_NB) return Qfalse;
3443 rb_thread_polling();
3444 rb_io_check_closed(fptr);
3445 continue;
3447 case EINTR:
3448 #if defined(ERESTART)
3449 case ERESTART:
3450 #endif
3451 break;
3453 default:
3454 rb_sys_fail(fptr->path);
3457 #endif
3458 return INT2FIX(0);
3460 #undef flock
3462 static void
3463 test_check(int n, int argc, VALUE *argv)
3465 int i;
3467 rb_secure(2);
3468 n+=1;
3469 if (n != argc) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, n);
3470 for (i=1; i<n; i++) {
3471 switch (TYPE(argv[i])) {
3472 case T_STRING:
3473 default:
3474 FilePathValue(argv[i]);
3475 break;
3476 case T_FILE:
3477 break;
3482 #define CHECK(n) test_check((n), argc, argv)
3485 * call-seq:
3486 * test(int_cmd, file1 [, file2] ) => obj
3488 * Uses the integer <i>aCmd</i> to perform various tests on
3489 * <i>file1</i> (first table below) or on <i>file1</i> and
3490 * <i>file2</i> (second table).
3492 * File tests on a single file:
3494 * Test Returns Meaning
3495 * ?A | Time | Last access time for file1
3496 * ?b | boolean | True if file1 is a block device
3497 * ?c | boolean | True if file1 is a character device
3498 * ?C | Time | Last change time for file1
3499 * ?d | boolean | True if file1 exists and is a directory
3500 * ?e | boolean | True if file1 exists
3501 * ?f | boolean | True if file1 exists and is a regular file
3502 * ?g | boolean | True if file1 has the \CF{setgid} bit
3503 * | | set (false under NT)
3504 * ?G | boolean | True if file1 exists and has a group
3505 * | | ownership equal to the caller's group
3506 * ?k | boolean | True if file1 exists and has the sticky bit set
3507 * ?l | boolean | True if file1 exists and is a symbolic link
3508 * ?M | Time | Last modification time for file1
3509 * ?o | boolean | True if file1 exists and is owned by
3510 * | | the caller's effective uid
3511 * ?O | boolean | True if file1 exists and is owned by
3512 * | | the caller's real uid
3513 * ?p | boolean | True if file1 exists and is a fifo
3514 * ?r | boolean | True if file1 is readable by the effective
3515 * | | uid/gid of the caller
3516 * ?R | boolean | True if file is readable by the real
3517 * | | uid/gid of the caller
3518 * ?s | int/nil | If file1 has nonzero size, return the size,
3519 * | | otherwise return nil
3520 * ?S | boolean | True if file1 exists and is a socket
3521 * ?u | boolean | True if file1 has the setuid bit set
3522 * ?w | boolean | True if file1 exists and is writable by
3523 * | | the effective uid/gid
3524 * ?W | boolean | True if file1 exists and is writable by
3525 * | | the real uid/gid
3526 * ?x | boolean | True if file1 exists and is executable by
3527 * | | the effective uid/gid
3528 * ?X | boolean | True if file1 exists and is executable by
3529 * | | the real uid/gid
3530 * ?z | boolean | True if file1 exists and has a zero length
3532 * Tests that take two files:
3534 * ?- | boolean | True if file1 and file2 are identical
3535 * ?= | boolean | True if the modification times of file1
3536 * | | and file2 are equal
3537 * ?< | boolean | True if the modification time of file1
3538 * | | is prior to that of file2
3539 * ?> | boolean | True if the modification time of file1
3540 * | | is after that of file2
3543 static VALUE
3544 rb_f_test(int argc, VALUE *argv)
3546 int cmd;
3548 if (argc == 0) rb_raise(rb_eArgError, "wrong number of arguments");
3549 cmd = NUM2CHR(argv[0]);
3550 if (cmd == 0) goto unknown;
3551 if (strchr("bcdefgGkloOprRsSuwWxXz", cmd)) {
3552 CHECK(1);
3553 switch (cmd) {
3554 case 'b':
3555 return rb_file_blockdev_p(0, argv[1]);
3557 case 'c':
3558 return rb_file_chardev_p(0, argv[1]);
3560 case 'd':
3561 return rb_file_directory_p(0, argv[1]);
3563 case 'a':
3564 case 'e':
3565 return rb_file_exist_p(0, argv[1]);
3567 case 'f':
3568 return rb_file_file_p(0, argv[1]);
3570 case 'g':
3571 return rb_file_sgid_p(0, argv[1]);
3573 case 'G':
3574 return rb_file_grpowned_p(0, argv[1]);
3576 case 'k':
3577 return rb_file_sticky_p(0, argv[1]);
3579 case 'l':
3580 return rb_file_symlink_p(0, argv[1]);
3582 case 'o':
3583 return rb_file_owned_p(0, argv[1]);
3585 case 'O':
3586 return rb_file_rowned_p(0, argv[1]);
3588 case 'p':
3589 return rb_file_pipe_p(0, argv[1]);
3591 case 'r':
3592 return rb_file_readable_p(0, argv[1]);
3594 case 'R':
3595 return rb_file_readable_real_p(0, argv[1]);
3597 case 's':
3598 return rb_file_size_p(0, argv[1]);
3600 case 'S':
3601 return rb_file_socket_p(0, argv[1]);
3603 case 'u':
3604 return rb_file_suid_p(0, argv[1]);
3606 case 'w':
3607 return rb_file_writable_p(0, argv[1]);
3609 case 'W':
3610 return rb_file_world_writable_p(0, argv[1]);
3612 case 'x':
3613 return rb_file_executable_p(0, argv[1]);
3615 case 'X':
3616 return rb_file_executable_real_p(0, argv[1]);
3618 case 'z':
3619 return rb_file_zero_p(0, argv[1]);
3623 if (strchr("MAC", cmd)) {
3624 struct stat st;
3626 CHECK(1);
3627 if (rb_stat(argv[1], &st) == -1) {
3628 rb_sys_fail(RSTRING_PTR(argv[1]));
3631 switch (cmd) {
3632 case 'A':
3633 return stat_atime(&st);
3634 case 'M':
3635 return stat_mtime(&st);
3636 case 'C':
3637 return stat_ctime(&st);
3641 if (cmd == '-') {
3642 CHECK(2);
3643 return rb_file_identical_p(0, argv[1], argv[2]);
3646 if (strchr("=<>", cmd)) {
3647 struct stat st1, st2;
3649 CHECK(2);
3650 if (rb_stat(argv[1], &st1) < 0) return Qfalse;
3651 if (rb_stat(argv[2], &st2) < 0) return Qfalse;
3653 switch (cmd) {
3654 case '=':
3655 if (st1.st_mtime == st2.st_mtime) return Qtrue;
3656 return Qfalse;
3658 case '>':
3659 if (st1.st_mtime > st2.st_mtime) return Qtrue;
3660 return Qfalse;
3662 case '<':
3663 if (st1.st_mtime < st2.st_mtime) return Qtrue;
3664 return Qfalse;
3667 unknown:
3668 /* unknown command */
3669 if (ISPRINT(cmd)) {
3670 rb_raise(rb_eArgError, "unknown command ?%c", cmd);
3672 else {
3673 rb_raise(rb_eArgError, "unknown command ?\\x%02X", cmd);
3675 return Qnil; /* not reached */
3681 * Document-class: File::Stat
3683 * Objects of class <code>File::Stat</code> encapsulate common status
3684 * information for <code>File</code> objects. The information is
3685 * recorded at the moment the <code>File::Stat</code> object is
3686 * created; changes made to the file after that point will not be
3687 * reflected. <code>File::Stat</code> objects are returned by
3688 * <code>IO#stat</code>, <code>File::stat</code>,
3689 * <code>File#lstat</code>, and <code>File::lstat</code>. Many of these
3690 * methods return platform-specific values, and not all values are
3691 * meaningful on all systems. See also <code>Kernel#test</code>.
3694 static VALUE
3695 rb_stat_s_alloc(VALUE klass)
3697 return stat_new_0(klass, 0);
3701 * call-seq:
3703 * File::Stat.new(file_name) => stat
3705 * Create a File::Stat object for the given file name (raising an
3706 * exception if the file doesn't exist).
3709 static VALUE
3710 rb_stat_init(VALUE obj, VALUE fname)
3712 struct stat st, *nst;
3714 rb_secure(2);
3715 FilePathValue(fname);
3716 if (stat(StringValueCStr(fname), &st) == -1) {
3717 rb_sys_fail(RSTRING_PTR(fname));
3719 if (DATA_PTR(obj)) {
3720 xfree(DATA_PTR(obj));
3721 DATA_PTR(obj) = NULL;
3723 nst = ALLOC(struct stat);
3724 *nst = st;
3725 DATA_PTR(obj) = nst;
3727 return Qnil;
3730 /* :nodoc: */
3731 static VALUE
3732 rb_stat_init_copy(VALUE copy, VALUE orig)
3734 struct stat *nst;
3736 if (copy == orig) return orig;
3737 rb_check_frozen(copy);
3738 /* need better argument type check */
3739 if (!rb_obj_is_instance_of(orig, rb_obj_class(copy))) {
3740 rb_raise(rb_eTypeError, "wrong argument class");
3742 if (DATA_PTR(copy)) {
3743 xfree(DATA_PTR(copy));
3744 DATA_PTR(copy) = 0;
3746 if (DATA_PTR(orig)) {
3747 nst = ALLOC(struct stat);
3748 *nst = *(struct stat*)DATA_PTR(orig);
3749 DATA_PTR(copy) = nst;
3752 return copy;
3756 * call-seq:
3757 * stat.ftype => string
3759 * Identifies the type of <i>stat</i>. The return string is one of:
3760 * ``<code>file</code>'', ``<code>directory</code>'',
3761 * ``<code>characterSpecial</code>'', ``<code>blockSpecial</code>'',
3762 * ``<code>fifo</code>'', ``<code>link</code>'',
3763 * ``<code>socket</code>'', or ``<code>unknown</code>''.
3765 * File.stat("/dev/tty").ftype #=> "characterSpecial"
3769 static VALUE
3770 rb_stat_ftype(VALUE obj)
3772 return rb_file_ftype(get_stat(obj));
3776 * call-seq:
3777 * stat.directory? => true or false
3779 * Returns <code>true</code> if <i>stat</i> is a directory,
3780 * <code>false</code> otherwise.
3782 * File.stat("testfile").directory? #=> false
3783 * File.stat(".").directory? #=> true
3786 static VALUE
3787 rb_stat_d(VALUE obj)
3789 if (S_ISDIR(get_stat(obj)->st_mode)) return Qtrue;
3790 return Qfalse;
3794 * call-seq:
3795 * stat.pipe? => true or false
3797 * Returns <code>true</code> if the operating system supports pipes and
3798 * <i>stat</i> is a pipe; <code>false</code> otherwise.
3801 static VALUE
3802 rb_stat_p(VALUE obj)
3804 #ifdef S_IFIFO
3805 if (S_ISFIFO(get_stat(obj)->st_mode)) return Qtrue;
3807 #endif
3808 return Qfalse;
3812 * call-seq:
3813 * stat.symlink? => true or false
3815 * Returns <code>true</code> if <i>stat</i> is a symbolic link,
3816 * <code>false</code> if it isn't or if the operating system doesn't
3817 * support this feature. As <code>File::stat</code> automatically
3818 * follows symbolic links, <code>symlink?</code> will always be
3819 * <code>false</code> for an object returned by
3820 * <code>File::stat</code>.
3822 * File.symlink("testfile", "alink") #=> 0
3823 * File.stat("alink").symlink? #=> false
3824 * File.lstat("alink").symlink? #=> true
3828 static VALUE
3829 rb_stat_l(VALUE obj)
3831 #ifdef S_ISLNK
3832 if (S_ISLNK(get_stat(obj)->st_mode)) return Qtrue;
3833 #endif
3834 return Qfalse;
3838 * call-seq:
3839 * stat.socket? => true or false
3841 * Returns <code>true</code> if <i>stat</i> is a socket,
3842 * <code>false</code> if it isn't or if the operating system doesn't
3843 * support this feature.
3845 * File.stat("testfile").socket? #=> false
3849 static VALUE
3850 rb_stat_S(VALUE obj)
3852 #ifdef S_ISSOCK
3853 if (S_ISSOCK(get_stat(obj)->st_mode)) return Qtrue;
3855 #endif
3856 return Qfalse;
3860 * call-seq:
3861 * stat.blockdev? => true or false
3863 * Returns <code>true</code> if the file is a block device,
3864 * <code>false</code> if it isn't or if the operating system doesn't
3865 * support this feature.
3867 * File.stat("testfile").blockdev? #=> false
3868 * File.stat("/dev/hda1").blockdev? #=> true
3872 static VALUE
3873 rb_stat_b(VALUE obj)
3875 #ifdef S_ISBLK
3876 if (S_ISBLK(get_stat(obj)->st_mode)) return Qtrue;
3878 #endif
3879 return Qfalse;
3883 * call-seq:
3884 * stat.chardev? => true or false
3886 * Returns <code>true</code> if the file is a character device,
3887 * <code>false</code> if it isn't or if the operating system doesn't
3888 * support this feature.
3890 * File.stat("/dev/tty").chardev? #=> true
3894 static VALUE
3895 rb_stat_c(VALUE obj)
3897 if (S_ISCHR(get_stat(obj)->st_mode)) return Qtrue;
3899 return Qfalse;
3903 * call-seq:
3904 * stat.owned? => true or false
3906 * Returns <code>true</code> if the effective user id of the process is
3907 * the same as the owner of <i>stat</i>.
3909 * File.stat("testfile").owned? #=> true
3910 * File.stat("/etc/passwd").owned? #=> false
3914 static VALUE
3915 rb_stat_owned(VALUE obj)
3917 if (get_stat(obj)->st_uid == geteuid()) return Qtrue;
3918 return Qfalse;
3921 static VALUE
3922 rb_stat_rowned(VALUE obj)
3924 if (get_stat(obj)->st_uid == getuid()) return Qtrue;
3925 return Qfalse;
3929 * call-seq:
3930 * stat.grpowned? => true or false
3932 * Returns true if the effective group id of the process is the same as
3933 * the group id of <i>stat</i>. On Windows NT, returns <code>false</code>.
3935 * File.stat("testfile").grpowned? #=> true
3936 * File.stat("/etc/passwd").grpowned? #=> false
3940 static VALUE
3941 rb_stat_grpowned(VALUE obj)
3943 #ifndef _WIN32
3944 if (group_member(get_stat(obj)->st_gid)) return Qtrue;
3945 #endif
3946 return Qfalse;
3950 * call-seq:
3951 * stat.readable? => true or false
3953 * Returns <code>true</code> if <i>stat</i> is readable by the
3954 * effective user id of this process.
3956 * File.stat("testfile").readable? #=> true
3960 static VALUE
3961 rb_stat_r(VALUE obj)
3963 struct stat *st = get_stat(obj);
3965 #ifdef USE_GETEUID
3966 if (geteuid() == 0) return Qtrue;
3967 #endif
3968 #ifdef S_IRUSR
3969 if (rb_stat_owned(obj))
3970 return st->st_mode & S_IRUSR ? Qtrue : Qfalse;
3971 #endif
3972 #ifdef S_IRGRP
3973 if (rb_stat_grpowned(obj))
3974 return st->st_mode & S_IRGRP ? Qtrue : Qfalse;
3975 #endif
3976 #ifdef S_IROTH
3977 if (!(st->st_mode & S_IROTH)) return Qfalse;
3978 #endif
3979 return Qtrue;
3985 * call-seq:
3986 * stat.readable_real? -> true or false
3988 * Returns <code>true</code> if <i>stat</i> is readable by the real
3989 * user id of this process.
3991 * File.stat("testfile").readable_real? #=> true
3995 static VALUE
3996 rb_stat_R(VALUE obj)
3998 struct stat *st = get_stat(obj);
4000 #ifdef USE_GETEUID
4001 if (getuid() == 0) return Qtrue;
4002 #endif
4003 #ifdef S_IRUSR
4004 if (rb_stat_rowned(obj))
4005 return st->st_mode & S_IRUSR ? Qtrue : Qfalse;
4006 #endif
4007 #ifdef S_IRGRP
4008 if (group_member(get_stat(obj)->st_gid))
4009 return st->st_mode & S_IRGRP ? Qtrue : Qfalse;
4010 #endif
4011 #ifdef S_IROTH
4012 if (!(st->st_mode & S_IROTH)) return Qfalse;
4013 #endif
4014 return Qtrue;
4018 * call-seq:
4019 * stat.world_readable? => fixnum or nil
4021 * If <i>stat</i> is readable by others, returns an integer
4022 * representing the file permission bits of <i>stat</i>. Returns
4023 * <code>nil</code> otherwise. The meaning of the bits is platform
4024 * dependent; on Unix systems, see <code>stat(2)</code>.
4026 * m = File.stat("/etc/passwd").world_readable? # => 420
4027 * sprintf("%o", m) # => "644"
4030 static VALUE
4031 rb_stat_wr(VALUE obj)
4033 #ifdef S_IROTH
4034 if ((get_stat(obj)->st_mode & (S_IROTH)) == S_IROTH) {
4035 return UINT2NUM(get_stat(obj)->st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
4037 else {
4038 return Qnil;
4040 #endif
4044 * call-seq:
4045 * stat.writable? -> true or false
4047 * Returns <code>true</code> if <i>stat</i> is writable by the
4048 * effective user id of this process.
4050 * File.stat("testfile").writable? #=> true
4054 static VALUE
4055 rb_stat_w(VALUE obj)
4057 struct stat *st = get_stat(obj);
4059 #ifdef USE_GETEUID
4060 if (geteuid() == 0) return Qtrue;
4061 #endif
4062 #ifdef S_IWUSR
4063 if (rb_stat_owned(obj))
4064 return st->st_mode & S_IWUSR ? Qtrue : Qfalse;
4065 #endif
4066 #ifdef S_IWGRP
4067 if (rb_stat_grpowned(obj))
4068 return st->st_mode & S_IWGRP ? Qtrue : Qfalse;
4069 #endif
4070 #ifdef S_IWOTH
4071 if (!(st->st_mode & S_IWOTH)) return Qfalse;
4072 #endif
4073 return Qtrue;
4077 * call-seq:
4078 * stat.writable_real? -> true or false
4080 * Returns <code>true</code> if <i>stat</i> is writable by the real
4081 * user id of this process.
4083 * File.stat("testfile").writable_real? #=> true
4087 static VALUE
4088 rb_stat_W(VALUE obj)
4090 struct stat *st = get_stat(obj);
4092 #ifdef USE_GETEUID
4093 if (getuid() == 0) return Qtrue;
4094 #endif
4095 #ifdef S_IWUSR
4096 if (rb_stat_rowned(obj))
4097 return st->st_mode & S_IWUSR ? Qtrue : Qfalse;
4098 #endif
4099 #ifdef S_IWGRP
4100 if (group_member(get_stat(obj)->st_gid))
4101 return st->st_mode & S_IWGRP ? Qtrue : Qfalse;
4102 #endif
4103 #ifdef S_IWOTH
4104 if (!(st->st_mode & S_IWOTH)) return Qfalse;
4105 #endif
4106 return Qtrue;
4110 * call-seq:
4111 * stat.world_writable? => fixnum or nil
4113 * If <i>stat</i> is writable by others, returns an integer
4114 * representing the file permission bits of <i>stat</i>. Returns
4115 * <code>nil</code> otherwise. The meaning of the bits is platform
4116 * dependent; on Unix systems, see <code>stat(2)</code>.
4118 * m = File.stat("/tmp").world_writable? # => 511
4119 * sprintf("%o", m) # => "777"
4122 static VALUE
4123 rb_stat_ww(VALUE obj)
4125 #ifdef S_IROTH
4126 if ((get_stat(obj)->st_mode & (S_IWOTH)) == S_IWOTH) {
4127 return UINT2NUM(get_stat(obj)->st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
4129 else {
4130 return Qnil;
4132 #endif
4136 * call-seq:
4137 * stat.executable? => true or false
4139 * Returns <code>true</code> if <i>stat</i> is executable or if the
4140 * operating system doesn't distinguish executable files from
4141 * nonexecutable files. The tests are made using the effective owner of
4142 * the process.
4144 * File.stat("testfile").executable? #=> false
4148 static VALUE
4149 rb_stat_x(VALUE obj)
4151 struct stat *st = get_stat(obj);
4153 #ifdef USE_GETEUID
4154 if (geteuid() == 0) {
4155 return st->st_mode & S_IXUGO ? Qtrue : Qfalse;
4157 #endif
4158 #ifdef S_IXUSR
4159 if (rb_stat_owned(obj))
4160 return st->st_mode & S_IXUSR ? Qtrue : Qfalse;
4161 #endif
4162 #ifdef S_IXGRP
4163 if (rb_stat_grpowned(obj))
4164 return st->st_mode & S_IXGRP ? Qtrue : Qfalse;
4165 #endif
4166 #ifdef S_IXOTH
4167 if (!(st->st_mode & S_IXOTH)) return Qfalse;
4168 #endif
4169 return Qtrue;
4173 * call-seq:
4174 * stat.executable_real? => true or false
4176 * Same as <code>executable?</code>, but tests using the real owner of
4177 * the process.
4181 static VALUE
4182 rb_stat_X(VALUE obj)
4184 struct stat *st = get_stat(obj);
4186 #ifdef USE_GETEUID
4187 if (getuid() == 0) {
4188 return st->st_mode & S_IXUGO ? Qtrue : Qfalse;
4190 #endif
4191 #ifdef S_IXUSR
4192 if (rb_stat_rowned(obj))
4193 return st->st_mode & S_IXUSR ? Qtrue : Qfalse;
4194 #endif
4195 #ifdef S_IXGRP
4196 if (group_member(get_stat(obj)->st_gid))
4197 return st->st_mode & S_IXGRP ? Qtrue : Qfalse;
4198 #endif
4199 #ifdef S_IXOTH
4200 if (!(st->st_mode & S_IXOTH)) return Qfalse;
4201 #endif
4202 return Qtrue;
4206 * call-seq:
4207 * stat.file? => true or false
4209 * Returns <code>true</code> if <i>stat</i> is a regular file (not
4210 * a device file, pipe, socket, etc.).
4212 * File.stat("testfile").file? #=> true
4216 static VALUE
4217 rb_stat_f(VALUE obj)
4219 if (S_ISREG(get_stat(obj)->st_mode)) return Qtrue;
4220 return Qfalse;
4224 * call-seq:
4225 * stat.zero? => true or false
4227 * Returns <code>true</code> if <i>stat</i> is a zero-length file;
4228 * <code>false</code> otherwise.
4230 * File.stat("testfile").zero? #=> false
4234 static VALUE
4235 rb_stat_z(VALUE obj)
4237 if (get_stat(obj)->st_size == 0) return Qtrue;
4238 return Qfalse;
4243 * call-seq:
4244 * state.size => integer
4246 * Returns the size of <i>stat</i> in bytes.
4248 * File.stat("testfile").size #=> 66
4252 static VALUE
4253 rb_stat_s(VALUE obj)
4255 off_t size = get_stat(obj)->st_size;
4257 if (size == 0) return Qnil;
4258 return OFFT2NUM(size);
4262 * call-seq:
4263 * stat.setuid? => true or false
4265 * Returns <code>true</code> if <i>stat</i> has the set-user-id
4266 * permission bit set, <code>false</code> if it doesn't or if the
4267 * operating system doesn't support this feature.
4269 * File.stat("/bin/su").setuid? #=> true
4272 static VALUE
4273 rb_stat_suid(VALUE obj)
4275 #ifdef S_ISUID
4276 if (get_stat(obj)->st_mode & S_ISUID) return Qtrue;
4277 #endif
4278 return Qfalse;
4282 * call-seq:
4283 * stat.setgid? => true or false
4285 * Returns <code>true</code> if <i>stat</i> has the set-group-id
4286 * permission bit set, <code>false</code> if it doesn't or if the
4287 * operating system doesn't support this feature.
4289 * File.stat("/usr/sbin/lpc").setgid? #=> true
4293 static VALUE
4294 rb_stat_sgid(VALUE obj)
4296 #ifdef S_ISGID
4297 if (get_stat(obj)->st_mode & S_ISGID) return Qtrue;
4298 #endif
4299 return Qfalse;
4303 * call-seq:
4304 * stat.sticky? => true or false
4306 * Returns <code>true</code> if <i>stat</i> has its sticky bit set,
4307 * <code>false</code> if it doesn't or if the operating system doesn't
4308 * support this feature.
4310 * File.stat("testfile").sticky? #=> false
4314 static VALUE
4315 rb_stat_sticky(VALUE obj)
4317 #ifdef S_ISVTX
4318 if (get_stat(obj)->st_mode & S_ISVTX) return Qtrue;
4319 #endif
4320 return Qfalse;
4323 VALUE rb_mFConst;
4325 void
4326 rb_file_const(const char *name, VALUE value)
4328 rb_define_const(rb_mFConst, name, value);
4331 static int
4332 is_absolute_path(const char *path)
4334 #ifdef DOSISH_DRIVE_LETTER
4335 if (has_drive_letter(path) && isdirsep(path[2])) return 1;
4336 #endif
4337 #ifdef DOSISH_UNC
4338 if (isdirsep(path[0]) && isdirsep(path[1])) return 1;
4339 #endif
4340 #ifndef DOSISH
4341 if (path[0] == '/') return 1;
4342 #endif
4343 return 0;
4346 #ifndef ENABLE_PATH_CHECK
4347 # if defined DOSISH || defined __CYGWIN__
4348 # define ENABLE_PATH_CHECK 0
4349 # else
4350 # define ENABLE_PATH_CHECK 1
4351 # endif
4352 #endif
4354 #if ENABLE_PATH_CHECK
4355 static int
4356 path_check_0(VALUE path, int execpath)
4358 struct stat st;
4359 const char *p0 = StringValueCStr(path);
4360 char *p = 0, *s;
4362 if (!is_absolute_path(p0)) {
4363 char *buf = my_getcwd();
4364 VALUE newpath;
4366 newpath = rb_str_new2(buf);
4367 xfree(buf);
4369 rb_str_cat2(newpath, "/");
4370 rb_str_cat2(newpath, p0);
4371 p0 = RSTRING_PTR(path = newpath);
4373 for (;;) {
4374 #ifndef S_IWOTH
4375 # define S_IWOTH 002
4376 #endif
4377 if (stat(p0, &st) == 0 && S_ISDIR(st.st_mode) && (st.st_mode & S_IWOTH)
4378 #ifdef S_ISVTX
4379 && !(p && execpath && (st.st_mode & S_ISVTX))
4380 #endif
4381 && !access(p0, W_OK)) {
4382 rb_warn("Insecure world writable dir %s in %sPATH, mode 0%o",
4383 p0, (execpath ? "" : "LOAD_"), st.st_mode);
4384 if (p) *p = '/';
4385 return 0;
4387 s = strrdirsep(p0);
4388 if (p) *p = '/';
4389 if (!s || s == p0) return 1;
4390 p = s;
4391 *p = '\0';
4394 #endif
4396 static int
4397 fpath_check(const char *path)
4399 #if ENABLE_PATH_CHECK
4400 return path_check_0(rb_str_new2(path), Qfalse);
4401 #else
4402 return 1;
4403 #endif
4407 rb_path_check(const char *path)
4409 #if ENABLE_PATH_CHECK
4410 const char *p0, *p, *pend;
4411 const char sep = PATH_SEP_CHAR;
4413 if (!path) return 1;
4415 pend = path + strlen(path);
4416 p0 = path;
4417 p = strchr(path, sep);
4418 if (!p) p = pend;
4420 for (;;) {
4421 if (!path_check_0(rb_str_new(p0, p - p0), Qtrue)) {
4422 return 0; /* not safe */
4424 p0 = p + 1;
4425 if (p0 > pend) break;
4426 p = strchr(p0, sep);
4427 if (!p) p = pend;
4429 #endif
4430 return 1;
4433 #if defined(__MACOS__) || defined(riscos)
4434 static int
4435 is_macos_native_path(const char *path)
4437 if (strchr(path, ':')) return 1;
4438 return 0;
4440 #endif
4442 static int
4443 file_load_ok(const char *path)
4445 return eaccess(path, R_OK) == 0;
4448 static int
4449 is_explicit_relative(const char *path)
4451 if (*path++ != '.') return 0;
4452 if (*path == '.') path++;
4453 return isdirsep(*path);
4456 VALUE rb_get_load_path(void);
4459 rb_find_file_ext(VALUE *filep, const char *const *ext)
4461 const char *f = RSTRING_PTR(*filep);
4462 VALUE fname, load_path, tmp;
4463 long i, j, fnlen;
4465 if (!ext[0]) return 0;
4467 if (f[0] == '~') {
4468 fname = rb_file_expand_path(*filep, Qnil);
4469 if (rb_safe_level() >= 2 && OBJ_TAINTED(fname)) {
4470 rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
4472 OBJ_FREEZE(fname);
4473 f = StringValueCStr(fname);
4474 *filep = fname;
4477 if (is_absolute_path(f) || is_explicit_relative(f)) {
4478 fname = rb_str_dup(*filep);
4479 fnlen = RSTRING_LEN(fname);
4480 for (i=0; ext[i]; i++) {
4481 rb_str_cat2(fname, ext[i]);
4482 if (file_load_ok(StringValueCStr(fname))) {
4483 if (!is_absolute_path(f)) fname = rb_file_expand_path(fname, Qnil);
4484 OBJ_FREEZE(fname);
4485 *filep = fname;
4486 return i+1;
4488 rb_str_set_len(fname, fnlen);
4490 return 0;
4493 load_path = rb_get_load_path();
4494 if (!load_path) return 0;
4496 fname = rb_str_dup(*filep);
4497 RBASIC(fname)->klass = 0;
4498 fnlen = RSTRING_LEN(fname);
4499 tmp = rb_str_tmp_new(MAXPATHLEN + 2);
4500 for (j=0; ext[j]; j++) {
4501 rb_str_cat2(fname, ext[j]);
4502 for (i = 0; i < RARRAY_LEN(load_path); i++) {
4503 VALUE str = RARRAY_PTR(load_path)[i];
4505 FilePathValue(str);
4506 if (RSTRING_LEN(str) == 0) continue;
4507 file_expand_path(fname, str, tmp);
4508 if (file_load_ok(RSTRING_PTR(tmp))) {
4509 RBASIC(tmp)->klass = rb_obj_class(*filep);
4510 OBJ_FREEZE(tmp);
4511 *filep = tmp;
4512 return j+1;
4515 rb_str_set_len(fname, fnlen);
4517 RB_GC_GUARD(load_path);
4518 return 0;
4521 VALUE
4522 rb_find_file(VALUE path)
4524 VALUE tmp, load_path;
4525 const char *f = StringValueCStr(path);
4527 if (f[0] == '~') {
4528 path = rb_file_expand_path(path, Qnil);
4529 if (rb_safe_level() >= 1 && OBJ_TAINTED(path)) {
4530 rb_raise(rb_eSecurityError, "loading from unsafe path %s", f);
4532 OBJ_FREEZE(path);
4533 f = StringValueCStr(path);
4536 #if defined(__MACOS__) || defined(riscos)
4537 if (is_macos_native_path(f)) {
4538 if (rb_safe_level() >= 1 && !fpath_check(f)) {
4539 rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
4541 if (file_load_ok(f)) return path;
4542 return 0;
4544 #endif
4546 if (is_absolute_path(f) || is_explicit_relative(f)) {
4547 if (rb_safe_level() >= 1 && !fpath_check(f)) {
4548 rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
4550 if (!file_load_ok(f)) return 0;
4551 if (!is_absolute_path(f)) path = rb_file_expand_path(path, Qnil);
4552 return path;
4555 if (rb_safe_level() >= 4) {
4556 rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f);
4559 load_path = rb_get_load_path();
4560 if (load_path) {
4561 long i;
4563 tmp = rb_str_tmp_new(MAXPATHLEN + 2);
4564 for (i = 0; i < RARRAY_LEN(load_path); i++) {
4565 VALUE str = RARRAY_PTR(load_path)[i];
4566 FilePathValue(str);
4567 if (RSTRING_LEN(str) > 0) {
4568 file_expand_path(path, str, tmp);
4569 f = RSTRING_PTR(tmp);
4570 if (file_load_ok(f)) goto found;
4573 return 0;
4574 found:
4575 RBASIC(tmp)->klass = rb_obj_class(path);
4576 OBJ_FREEZE(tmp);
4578 else {
4579 return 0; /* no path, no load */
4582 if (rb_safe_level() >= 1 && !fpath_check(f)) {
4583 rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
4586 return tmp;
4589 static void
4590 define_filetest_function(const char *name, VALUE (*func)(ANYARGS), int argc)
4592 rb_define_module_function(rb_mFileTest, name, func, argc);
4593 rb_define_singleton_method(rb_cFile, name, func, argc);
4598 * A <code>File</code> is an abstraction of any file object accessible
4599 * by the program and is closely associated with class <code>IO</code>
4600 * <code>File</code> includes the methods of module
4601 * <code>FileTest</code> as class methods, allowing you to write (for
4602 * example) <code>File.exist?("foo")</code>.
4604 * In the description of File methods,
4605 * <em>permission bits</em> are a platform-specific
4606 * set of bits that indicate permissions of a file. On Unix-based
4607 * systems, permissions are viewed as a set of three octets, for the
4608 * owner, the group, and the rest of the world. For each of these
4609 * entities, permissions may be set to read, write, or execute the
4610 * file:
4612 * The permission bits <code>0644</code> (in octal) would thus be
4613 * interpreted as read/write for owner, and read-only for group and
4614 * other. Higher-order bits may also be used to indicate the type of
4615 * file (plain, directory, pipe, socket, and so on) and various other
4616 * special features. If the permissions are for a directory, the
4617 * meaning of the execute bit changes; when set the directory can be
4618 * searched.
4620 * On non-Posix operating systems, there may be only the ability to
4621 * make a file read-only or read-write. In this case, the remaining
4622 * permission bits will be synthesized to resemble typical values. For
4623 * instance, on Windows NT the default permission bits are
4624 * <code>0644</code>, which means read/write for owner, read-only for
4625 * all others. The only change that can be made is to make the file
4626 * read-only, which is reported as <code>0444</code>.
4629 void
4630 Init_File(void)
4632 rb_mFileTest = rb_define_module("FileTest");
4633 rb_cFile = rb_define_class("File", rb_cIO);
4635 define_filetest_function("directory?", rb_file_directory_p, 1);
4636 define_filetest_function("exist?", rb_file_exist_p, 1);
4637 define_filetest_function("exists?", rb_file_exist_p, 1);
4638 define_filetest_function("readable?", rb_file_readable_p, 1);
4639 define_filetest_function("readable_real?", rb_file_readable_real_p, 1);
4640 define_filetest_function("world_readable?", rb_file_world_readable_p, 1);
4641 define_filetest_function("writable?", rb_file_writable_p, 1);
4642 define_filetest_function("writable_real?", rb_file_writable_real_p, 1);
4643 define_filetest_function("world_writable?", rb_file_world_writable_p, 1);
4644 define_filetest_function("executable?", rb_file_executable_p, 1);
4645 define_filetest_function("executable_real?", rb_file_executable_real_p, 1);
4646 define_filetest_function("file?", rb_file_file_p, 1);
4647 define_filetest_function("zero?", rb_file_zero_p, 1);
4648 define_filetest_function("size?", rb_file_size_p, 1);
4649 define_filetest_function("size", rb_file_s_size, 1);
4650 define_filetest_function("owned?", rb_file_owned_p, 1);
4651 define_filetest_function("grpowned?", rb_file_grpowned_p, 1);
4653 define_filetest_function("pipe?", rb_file_pipe_p, 1);
4654 define_filetest_function("symlink?", rb_file_symlink_p, 1);
4655 define_filetest_function("socket?", rb_file_socket_p, 1);
4657 define_filetest_function("blockdev?", rb_file_blockdev_p, 1);
4658 define_filetest_function("chardev?", rb_file_chardev_p, 1);
4660 define_filetest_function("setuid?", rb_file_suid_p, 1);
4661 define_filetest_function("setgid?", rb_file_sgid_p, 1);
4662 define_filetest_function("sticky?", rb_file_sticky_p, 1);
4664 define_filetest_function("identical?", rb_file_identical_p, 2);
4666 rb_define_singleton_method(rb_cFile, "stat", rb_file_s_stat, 1);
4667 rb_define_singleton_method(rb_cFile, "lstat", rb_file_s_lstat, 1);
4668 rb_define_singleton_method(rb_cFile, "ftype", rb_file_s_ftype, 1);
4670 rb_define_singleton_method(rb_cFile, "atime", rb_file_s_atime, 1);
4671 rb_define_singleton_method(rb_cFile, "mtime", rb_file_s_mtime, 1);
4672 rb_define_singleton_method(rb_cFile, "ctime", rb_file_s_ctime, 1);
4674 rb_define_singleton_method(rb_cFile, "utime", rb_file_s_utime, -1);
4675 rb_define_singleton_method(rb_cFile, "chmod", rb_file_s_chmod, -1);
4676 rb_define_singleton_method(rb_cFile, "chown", rb_file_s_chown, -1);
4677 rb_define_singleton_method(rb_cFile, "lchmod", rb_file_s_lchmod, -1);
4678 rb_define_singleton_method(rb_cFile, "lchown", rb_file_s_lchown, -1);
4680 rb_define_singleton_method(rb_cFile, "link", rb_file_s_link, 2);
4681 rb_define_singleton_method(rb_cFile, "symlink", rb_file_s_symlink, 2);
4682 rb_define_singleton_method(rb_cFile, "readlink", rb_file_s_readlink, 1);
4684 rb_define_singleton_method(rb_cFile, "unlink", rb_file_s_unlink, -2);
4685 rb_define_singleton_method(rb_cFile, "delete", rb_file_s_unlink, -2);
4686 rb_define_singleton_method(rb_cFile, "rename", rb_file_s_rename, 2);
4687 rb_define_singleton_method(rb_cFile, "umask", rb_file_s_umask, -1);
4688 rb_define_singleton_method(rb_cFile, "truncate", rb_file_s_truncate, 2);
4689 rb_define_singleton_method(rb_cFile, "expand_path", rb_file_s_expand_path, -1);
4690 rb_define_singleton_method(rb_cFile, "basename", rb_file_s_basename, -1);
4691 rb_define_singleton_method(rb_cFile, "dirname", rb_file_s_dirname, 1);
4692 rb_define_singleton_method(rb_cFile, "extname", rb_file_s_extname, 1);
4693 rb_define_singleton_method(rb_cFile, "path", rb_file_s_path, 1);
4695 separator = rb_obj_freeze(rb_usascii_str_new2("/"));
4696 rb_define_const(rb_cFile, "Separator", separator);
4697 rb_define_const(rb_cFile, "SEPARATOR", separator);
4698 rb_define_singleton_method(rb_cFile, "split", rb_file_s_split, 1);
4699 rb_define_singleton_method(rb_cFile, "join", rb_file_s_join, -2);
4701 #ifdef DOSISH
4702 rb_define_const(rb_cFile, "ALT_SEPARATOR", rb_obj_freeze(rb_usascii_str_new2("\\")));
4703 #else
4704 rb_define_const(rb_cFile, "ALT_SEPARATOR", Qnil);
4705 #endif
4706 rb_define_const(rb_cFile, "PATH_SEPARATOR", rb_obj_freeze(rb_str_new2(PATH_SEP)));
4708 rb_define_method(rb_cIO, "stat", rb_io_stat, 0); /* this is IO's method */
4709 rb_define_method(rb_cFile, "lstat", rb_file_lstat, 0);
4711 rb_define_method(rb_cFile, "atime", rb_file_atime, 0);
4712 rb_define_method(rb_cFile, "mtime", rb_file_mtime, 0);
4713 rb_define_method(rb_cFile, "ctime", rb_file_ctime, 0);
4715 rb_define_method(rb_cFile, "chmod", rb_file_chmod, 1);
4716 rb_define_method(rb_cFile, "chown", rb_file_chown, 2);
4717 rb_define_method(rb_cFile, "truncate", rb_file_truncate, 1);
4719 rb_define_method(rb_cFile, "flock", rb_file_flock, 1);
4721 rb_mFConst = rb_define_module_under(rb_cFile, "Constants");
4722 rb_include_module(rb_cIO, rb_mFConst);
4723 rb_file_const("LOCK_SH", INT2FIX(LOCK_SH));
4724 rb_file_const("LOCK_EX", INT2FIX(LOCK_EX));
4725 rb_file_const("LOCK_UN", INT2FIX(LOCK_UN));
4726 rb_file_const("LOCK_NB", INT2FIX(LOCK_NB));
4728 rb_define_method(rb_cFile, "path", rb_file_path, 0);
4729 rb_define_method(rb_cFile, "to_path", rb_file_path, 0);
4730 rb_define_global_function("test", rb_f_test, -1);
4732 rb_cStat = rb_define_class_under(rb_cFile, "Stat", rb_cObject);
4733 rb_define_alloc_func(rb_cStat, rb_stat_s_alloc);
4734 rb_define_method(rb_cStat, "initialize", rb_stat_init, 1);
4735 rb_define_method(rb_cStat, "initialize_copy", rb_stat_init_copy, 1);
4737 rb_include_module(rb_cStat, rb_mComparable);
4739 rb_define_method(rb_cStat, "<=>", rb_stat_cmp, 1);
4741 rb_define_method(rb_cStat, "dev", rb_stat_dev, 0);
4742 rb_define_method(rb_cStat, "dev_major", rb_stat_dev_major, 0);
4743 rb_define_method(rb_cStat, "dev_minor", rb_stat_dev_minor, 0);
4744 rb_define_method(rb_cStat, "ino", rb_stat_ino, 0);
4745 rb_define_method(rb_cStat, "mode", rb_stat_mode, 0);
4746 rb_define_method(rb_cStat, "nlink", rb_stat_nlink, 0);
4747 rb_define_method(rb_cStat, "uid", rb_stat_uid, 0);
4748 rb_define_method(rb_cStat, "gid", rb_stat_gid, 0);
4749 rb_define_method(rb_cStat, "rdev", rb_stat_rdev, 0);
4750 rb_define_method(rb_cStat, "rdev_major", rb_stat_rdev_major, 0);
4751 rb_define_method(rb_cStat, "rdev_minor", rb_stat_rdev_minor, 0);
4752 rb_define_method(rb_cStat, "size", rb_stat_size, 0);
4753 rb_define_method(rb_cStat, "blksize", rb_stat_blksize, 0);
4754 rb_define_method(rb_cStat, "blocks", rb_stat_blocks, 0);
4755 rb_define_method(rb_cStat, "atime", rb_stat_atime, 0);
4756 rb_define_method(rb_cStat, "mtime", rb_stat_mtime, 0);
4757 rb_define_method(rb_cStat, "ctime", rb_stat_ctime, 0);
4759 rb_define_method(rb_cStat, "inspect", rb_stat_inspect, 0);
4761 rb_define_method(rb_cStat, "ftype", rb_stat_ftype, 0);
4763 rb_define_method(rb_cStat, "directory?", rb_stat_d, 0);
4764 rb_define_method(rb_cStat, "readable?", rb_stat_r, 0);
4765 rb_define_method(rb_cStat, "readable_real?", rb_stat_R, 0);
4766 rb_define_method(rb_cStat, "world_readable?", rb_stat_wr, 0);
4767 rb_define_method(rb_cStat, "writable?", rb_stat_w, 0);
4768 rb_define_method(rb_cStat, "writable_real?", rb_stat_W, 0);
4769 rb_define_method(rb_cStat, "world_writable?", rb_stat_ww, 0);
4770 rb_define_method(rb_cStat, "executable?", rb_stat_x, 0);
4771 rb_define_method(rb_cStat, "executable_real?", rb_stat_X, 0);
4772 rb_define_method(rb_cStat, "file?", rb_stat_f, 0);
4773 rb_define_method(rb_cStat, "zero?", rb_stat_z, 0);
4774 rb_define_method(rb_cStat, "size?", rb_stat_s, 0);
4775 rb_define_method(rb_cStat, "owned?", rb_stat_owned, 0);
4776 rb_define_method(rb_cStat, "grpowned?", rb_stat_grpowned, 0);
4778 rb_define_method(rb_cStat, "pipe?", rb_stat_p, 0);
4779 rb_define_method(rb_cStat, "symlink?", rb_stat_l, 0);
4780 rb_define_method(rb_cStat, "socket?", rb_stat_S, 0);
4782 rb_define_method(rb_cStat, "blockdev?", rb_stat_b, 0);
4783 rb_define_method(rb_cStat, "chardev?", rb_stat_c, 0);
4785 rb_define_method(rb_cStat, "setuid?", rb_stat_suid, 0);
4786 rb_define_method(rb_cStat, "setgid?", rb_stat_sgid, 0);
4787 rb_define_method(rb_cStat, "sticky?", rb_stat_sticky, 0);