Main Page | Modules | Alphabetical List | Data Structures | File List | Data Fields | Globals

file.c

Go to the documentation of this file.
00001 /**********************************************************************
00002 
00003   file.c -
00004 
00005   $Author: akr $
00006   $Date: 2005/12/21 09:20:15 $
00007   created at: Mon Nov 15 12:24:34 JST 1993
00008 
00009   Copyright (C) 1993-2003 Yukihiro Matsumoto
00010   Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
00011   Copyright (C) 2000  Information-technology Promotion Agency, Japan
00012 
00013 **********************************************************************/
00014 
00015 #ifdef _WIN32
00016 #include "missing/file.h"
00017 #endif
00018 
00019 #include "ruby.h"
00020 #include "rubyio.h"
00021 #include "rubysig.h"
00022 #include "util.h"
00023 #include "dln.h"
00024 
00025 #ifdef HAVE_UNISTD_H
00026 #include <unistd.h>
00027 #endif
00028 
00029 #ifdef HAVE_SYS_FILE_H
00030 # include <sys/file.h>
00031 #else
00032 int flock (int, int);
00033 #endif
00034 
00035 #ifdef HAVE_SYS_PARAM_H
00036 # include <sys/param.h>
00037 #endif
00038 #ifndef MAXPATHLEN
00039 # define MAXPATHLEN 1024
00040 #endif
00041 
00042 #include <time.h>
00043 
00044 VALUE rb_time_new (time_t, time_t);
00045 
00046 #ifdef HAVE_UTIME_H
00047 #include <utime.h>
00048 #elif defined HAVE_SYS_UTIME_H
00049 #include <sys/utime.h>
00050 #endif
00051 
00052 #ifdef HAVE_PWD_H
00053 #include <pwd.h>
00054 #endif
00055 
00056 #ifndef HAVE_STRING_H
00057 char *strrchr (const char*,const char);
00058 #endif
00059 
00060 #include <sys/types.h>
00061 #include <sys/stat.h>
00062 
00063 #ifdef HAVE_SYS_MKDEV_H
00064 #include <sys/mkdev.h>
00065 #endif
00066 
00067 #if !defined HAVE_LSTAT && !defined lstat
00068 #define lstat stat
00069 #endif
00070 #if !HAVE_FSEEKO && !defined(fseeko)
00071 # define fseeko  fseek
00072 #endif
00073 
00074 #ifdef __BEOS__ /* should not change ID if -1 */
00075 static int
00076 be_chown(const char *path, uid_t owner, gid_t group)
00077 {
00078     if (owner == -1 || group == -1) {
00079         struct stat st;
00080         if (stat(path, &st) < 0) return -1;
00081         if (owner == -1) owner = st.st_uid;
00082         if (group == -1) group = st.st_gid;
00083     }
00084     return chown(path, owner, group);
00085 }
00086 #define chown be_chown
00087 static int
00088 be_fchown(int fd, uid_t owner, gid_t group)
00089 {
00090     if (owner == -1 || group == -1) {
00091         struct stat st;
00092         if (fstat(fd, &st) < 0) return -1;
00093         if (owner == -1) owner = st.st_uid;
00094         if (group == -1) group = st.st_gid;
00095     }
00096     return fchown(fd, owner, group);
00097 }
00098 #define fchown be_fchown
00099 #endif /* __BEOS__ */
00100 
00101 VALUE rb_cFile;
00102 VALUE rb_mFileTest;
00103 static VALUE rb_cStat;
00104 
00105 static long apply2files (void (*(const char *, void *), VALUE, void *));
00106 static long
00107 apply2files(func, vargs, arg)
00108     void (*func)(const char *, void *);
00109     VALUE vargs;
00110     void *arg;
00111 {
00112     long i;
00113     VALUE path;
00114     struct RArray *args = RARRAY(vargs);
00115 
00116     for (i=0; i<args->len; i++) {
00117         path = args->ptr[i];
00118         SafeStringValue(path);
00119         (*func)(StringValueCStr(path), arg);
00120     }
00121 
00122     return args->len;
00123 }
00124 
00125 /*
00126  *  call-seq:
00127  *     file.path -> filename
00128  *  
00129  *  Returns the pathname used to create <i>file</i> as a string. Does
00130  *  not normalize the name.
00131  *     
00132  *     File.new("testfile").path               #=> "testfile"
00133  *     File.new("/tmp/../tmp/xxx", "w").path   #=> "/tmp/../tmp/xxx"
00134  *     
00135  */
00136 
00137 static VALUE
00138 rb_file_path(obj)
00139     VALUE obj;
00140 {
00141     OpenFile *fptr;
00142 
00143     fptr = RFILE(rb_io_taint_check(obj))->fptr;
00144     rb_io_check_initialized(fptr);
00145     if (!fptr->path) return Qnil;
00146     return rb_tainted_str_new2(fptr->path);
00147 }
00148 
00149 static VALUE
00150 stat_new_0(klass, st)
00151     VALUE klass;
00152     struct stat *st;
00153 {
00154     struct stat *nst = 0;
00155 
00156     if (st) {
00157         nst = ALLOC(struct stat);
00158         *nst = *st;
00159     }
00160     return Data_Wrap_Struct(klass, NULL, free, nst);
00161 }
00162 
00163 static VALUE
00164 stat_new(st)
00165     struct stat *st;
00166 {
00167     return stat_new_0(rb_cStat, st);
00168 }
00169 
00170 static struct stat*
00171 get_stat(self)
00172     VALUE self;
00173 {
00174     struct stat* st;
00175     Data_Get_Struct(self, struct stat, st);
00176     if (!st) rb_raise(rb_eTypeError, "uninitialized File::Stat");
00177     return st;
00178 }
00179 
00180 /*
00181  *  call-seq:
00182  *     stat <=> other_stat    => -1, 0, 1
00183  *  
00184  *  Compares <code>File::Stat</code> objects by comparing their
00185  *  respective modification times.
00186  *     
00187  *     f1 = File.new("f1", "w")
00188  *     sleep 1
00189  *     f2 = File.new("f2", "w")
00190  *     f1.stat <=> f2.stat   #=> -1
00191  */
00192 
00193 static VALUE
00194 rb_stat_cmp(self, other)
00195     VALUE self, other;
00196 {
00197     if (rb_obj_is_kind_of(other, rb_obj_class(self))) {
00198         time_t t1 = get_stat(self)->st_mtime;
00199         time_t t2 = get_stat(other)->st_mtime;
00200         if (t1 == t2)
00201             return INT2FIX(0);
00202         else if (t1 < t2)
00203             return INT2FIX(-1);
00204         else
00205             return INT2FIX(1);
00206     }
00207     return Qnil;
00208 }
00209 
00210 static VALUE rb_stat_dev (VALUE);
00211 static VALUE rb_stat_ino (VALUE);
00212 static VALUE rb_stat_mode (VALUE);
00213 static VALUE rb_stat_nlink (VALUE);
00214 static VALUE rb_stat_uid (VALUE);
00215 static VALUE rb_stat_gid (VALUE);
00216 static VALUE rb_stat_rdev (VALUE);
00217 static VALUE rb_stat_size (VALUE);
00218 static VALUE rb_stat_blksize (VALUE);
00219 static VALUE rb_stat_blocks (VALUE);
00220 static VALUE rb_stat_atime (VALUE);
00221 static VALUE rb_stat_mtime (VALUE);
00222 static VALUE rb_stat_ctime (VALUE);
00223 
00224 /*
00225  *  call-seq:
00226  *     stat.dev    => fixnum
00227  *  
00228  *  Returns an integer representing the device on which <i>stat</i>
00229  *  resides.
00230  *     
00231  *     File.stat("testfile").dev   #=> 774
00232  */
00233 
00234 static VALUE
00235 rb_stat_dev(self)
00236     VALUE self;
00237 {
00238     return INT2NUM(get_stat(self)->st_dev);
00239 }
00240 
00241 /*
00242  *  call-seq:
00243  *     stat.dev_major   => fixnum
00244  *  
00245  *  Returns the major part of <code>File_Stat#dev</code> or
00246  *  <code>nil</code>.
00247  *     
00248  *     File.stat("/dev/fd1").dev_major   #=> 2
00249  *     File.stat("/dev/tty").dev_major   #=> 5
00250  */
00251 
00252 static VALUE
00253 rb_stat_dev_major(self)
00254     VALUE self;
00255 {
00256 #if defined(major)
00257     long dev = get_stat(self)->st_dev;
00258     return ULONG2NUM(major(dev));
00259 #else
00260     return Qnil;
00261 #endif
00262 }
00263 
00264 /*
00265  *  call-seq:
00266  *     stat.dev_minor   => fixnum
00267  *  
00268  *  Returns the minor part of <code>File_Stat#dev</code> or
00269  *  <code>nil</code>.
00270  *     
00271  *     File.stat("/dev/fd1").dev_minor   #=> 1
00272  *     File.stat("/dev/tty").dev_minor   #=> 0
00273  */
00274 
00275 static VALUE
00276 rb_stat_dev_minor(self)
00277     VALUE self;
00278 {
00279 #if defined(minor)
00280     long dev = get_stat(self)->st_dev;
00281     return ULONG2NUM(minor(dev));
00282 #else
00283     return Qnil;
00284 #endif
00285 }
00286 
00287 
00288 /*
00289  *  call-seq:
00290  *     stat.ino   => fixnum
00291  *  
00292  *  Returns the inode number for <i>stat</i>.
00293  *     
00294  *     File.stat("testfile").ino   #=> 1083669
00295  *     
00296  */
00297 
00298 static VALUE
00299 rb_stat_ino(self)
00300     VALUE self;
00301 {
00302 #ifdef HUGE_ST_INO
00303     return ULL2NUM(get_stat(self)->st_ino);
00304 #else
00305     return ULONG2NUM(get_stat(self)->st_ino);
00306 #endif
00307 }
00308 
00309 /*
00310  *  call-seq:
00311  *     stat.mode   => fixnum
00312  *  
00313  *  Returns an integer representing the permission bits of
00314  *  <i>stat</i>. The meaning of the bits is platform dependent; on
00315  *  Unix systems, see <code>stat(2)</code>.
00316  *     
00317  *     File.chmod(0644, "testfile")   #=> 1
00318  *     s = File.stat("testfile")
00319  *     sprintf("%o", s.mode)          #=> "100644"
00320  */
00321 
00322 static VALUE
00323 rb_stat_mode(self)
00324     VALUE self;
00325 {
00326 #ifdef __BORLANDC__
00327     return UINT2NUM((unsigned short)(get_stat(self)->st_mode));
00328 #else
00329     return UINT2NUM(get_stat(self)->st_mode);
00330 #endif
00331 }
00332 
00333 /*
00334  *  call-seq:
00335  *     stat.nlink   => fixnum
00336  *  
00337  *  Returns the number of hard links to <i>stat</i>.
00338  *     
00339  *     File.stat("testfile").nlink             #=> 1
00340  *     File.link("testfile", "testfile.bak")   #=> 0
00341  *     File.stat("testfile").nlink             #=> 2
00342  *     
00343  */
00344 
00345 static VALUE
00346 rb_stat_nlink(self)
00347     VALUE self;
00348 {
00349     return UINT2NUM(get_stat(self)->st_nlink);
00350 }
00351 
00352 
00353 /*
00354  *  call-seq:
00355  *     stat.uid    => fixnum
00356  *  
00357  *  Returns the numeric user id of the owner of <i>stat</i>.
00358  *     
00359  *     File.stat("testfile").uid   #=> 501
00360  *     
00361  */
00362 
00363 static VALUE
00364 rb_stat_uid(self)
00365     VALUE self;
00366 {
00367     return UINT2NUM(get_stat(self)->st_uid);
00368 }
00369 
00370 /*
00371  *  call-seq:
00372  *     stat.gid   => fixnum
00373  *  
00374  *  Returns the numeric group id of the owner of <i>stat</i>.
00375  *     
00376  *     File.stat("testfile").gid   #=> 500
00377  *     
00378  */
00379 
00380 static VALUE
00381 rb_stat_gid(self)
00382     VALUE self;
00383 {
00384     return UINT2NUM(get_stat(self)->st_gid);
00385 }
00386 
00387 
00388 /*
00389  *  call-seq:
00390  *     stat.rdev   =>  fixnum or nil
00391  *  
00392  *  Returns an integer representing the device type on which
00393  *  <i>stat</i> resides. Returns <code>nil</code> if the operating
00394  *  system doesn't support this feature.
00395  *     
00396  *     File.stat("/dev/fd1").rdev   #=> 513
00397  *     File.stat("/dev/tty").rdev   #=> 1280
00398  */
00399 
00400 static VALUE
00401 rb_stat_rdev(self)
00402     VALUE self;
00403 {
00404 #ifdef HAVE_ST_RDEV
00405     return ULONG2NUM(get_stat(self)->st_rdev);
00406 #else
00407     return Qnil;
00408 #endif
00409 }
00410 
00411 /*
00412  *  call-seq:
00413  *     stat.rdev_major   => fixnum
00414  *  
00415  *  Returns the major part of <code>File_Stat#rdev</code> or
00416  *  <code>nil</code>.
00417  *     
00418  *     File.stat("/dev/fd1").rdev_major   #=> 2
00419  *     File.stat("/dev/tty").rdev_major   #=> 5
00420  */
00421 
00422 static VALUE
00423 rb_stat_rdev_major(self)
00424     VALUE self;
00425 {
00426 #if defined(HAVE_ST_RDEV) && defined(major)
00427     long rdev = get_stat(self)->st_rdev;
00428     return ULONG2NUM(major(rdev));
00429 #else
00430     return Qnil;
00431 #endif
00432 }
00433 
00434 /*
00435  *  call-seq:
00436  *     stat.rdev_minor   => fixnum
00437  *  
00438  *  Returns the minor part of <code>File_Stat#rdev</code> or
00439  *  <code>nil</code>.
00440  *     
00441  *     File.stat("/dev/fd1").rdev_minor   #=> 1
00442  *     File.stat("/dev/tty").rdev_minor   #=> 0
00443  */
00444 
00445 static VALUE
00446 rb_stat_rdev_minor(self)
00447     VALUE self;
00448 {
00449 #if defined(HAVE_ST_RDEV) && defined(minor)
00450     long rdev = get_stat(self)->st_rdev;
00451     return ULONG2NUM(minor(rdev));
00452 #else
00453     return Qnil;
00454 #endif
00455 }
00456 
00457 /*
00458  *  call-seq:
00459  *     stat.size    => fixnum
00460  *  
00461  *  Returns the size of <i>stat</i> in bytes.
00462  *     
00463  *     File.stat("testfile").size   #=> 66
00464  */
00465 
00466 static VALUE
00467 rb_stat_size(self)
00468     VALUE self;
00469 {
00470     return OFFT2NUM(get_stat(self)->st_size);
00471 }
00472 
00473 /*
00474  *  call-seq:
00475  *     stat.blksize   => integer or nil
00476  *  
00477  *  Returns the native file system's block size. Will return <code>nil</code>
00478  *  on platforms that don't support this information.
00479  *     
00480  *     File.stat("testfile").blksize   #=> 4096
00481  *     
00482  */
00483 
00484 static VALUE
00485 rb_stat_blksize(self)
00486     VALUE self;
00487 {
00488 #ifdef HAVE_ST_BLKSIZE
00489     return ULONG2NUM(get_stat(self)->st_blksize);
00490 #else
00491     return Qnil;
00492 #endif
00493 }
00494 
00495 /*
00496  *  call-seq:
00497  *     stat.blocks    => integer or nil
00498  *  
00499  *  Returns the number of native file system blocks allocated for this
00500  *  file, or <code>nil</code> if the operating system doesn't 
00501  *  support this feature.
00502  *     
00503  *     File.stat("testfile").blocks   #=> 2
00504  */
00505 
00506 static VALUE
00507 rb_stat_blocks(self)
00508     VALUE self;
00509 {
00510 #ifdef HAVE_ST_BLOCKS
00511     return ULONG2NUM(get_stat(self)->st_blocks);
00512 #else
00513     return Qnil;
00514 #endif
00515 }
00516 
00517 
00518 /*
00519  *  call-seq:
00520  *     stat.atime   => time
00521  *  
00522  *  Returns the last access time for this file as an object of class
00523  *  <code>Time</code>.
00524  *     
00525  *     File.stat("testfile").atime   #=> Wed Dec 31 18:00:00 CST 1969
00526  *     
00527  */
00528 
00529 static VALUE
00530 rb_stat_atime(self)
00531     VALUE self;
00532 {
00533     return rb_time_new(get_stat(self)->st_atime, 0);
00534 }
00535 
00536 /*
00537  *  call-seq:
00538  *     stat.mtime -> aTime
00539  *  
00540  *  Returns the modification time of <i>stat</i>.
00541  *     
00542  *     File.stat("testfile").mtime   #=> Wed Apr 09 08:53:14 CDT 2003
00543  *     
00544  */
00545 
00546 static VALUE
00547 rb_stat_mtime(self)
00548     VALUE self;
00549 {
00550     return rb_time_new(get_stat(self)->st_mtime, 0);
00551 }
00552 
00553 /*
00554  *  call-seq:
00555  *     stat.ctime -> aTime
00556  *  
00557  *  Returns the change time for <i>stat</i> (that is, the time
00558  *  directory information about the file was changed, not the file
00559  *  itself).
00560  *     
00561  *     File.stat("testfile").ctime   #=> Wed Apr 09 08:53:14 CDT 2003
00562  *     
00563  */
00564 
00565 static VALUE
00566 rb_stat_ctime(self)
00567     VALUE self;
00568 {
00569     return rb_time_new(get_stat(self)->st_ctime, 0);
00570 }
00571 
00572 /*
00573  * call-seq:
00574  *   stat.inspect  =>  string
00575  *
00576  * Produce a nicely formatted description of <i>stat</i>.
00577  *
00578  *   File.stat("/etc/passwd").inspect
00579  *      #=> "#<File::Stat dev=0xe000005, ino=1078078, mode=0100644, 
00580  *           nlink=1, uid=0, gid=0, rdev=0x0, size=1374, blksize=4096, 
00581  *           blocks=8, atime=Wed Dec 10 10:16:12 CST 2003, 
00582  *           mtime=Fri Sep 12 15:41:41 CDT 2003, 
00583  *           ctime=Mon Oct 27 11:20:27 CST 2003>"
00584  */
00585 
00586 static VALUE
00587 rb_stat_inspect(self)
00588     VALUE self;
00589 {
00590     VALUE str;
00591     int i;
00592     static const struct {
00593         const char *name;
00594         VALUE (*func)(VALUE);
00595     } member[] = {
00596         {"dev",     rb_stat_dev},
00597         {"ino",     rb_stat_ino},
00598         {"mode",    rb_stat_mode},
00599         {"nlink",   rb_stat_nlink},
00600         {"uid",     rb_stat_uid},
00601         {"gid",     rb_stat_gid},
00602         {"rdev",    rb_stat_rdev},
00603         {"size",    rb_stat_size},
00604         {"blksize", rb_stat_blksize},
00605         {"blocks",  rb_stat_blocks},
00606         {"atime",   rb_stat_atime},
00607         {"mtime",   rb_stat_mtime},
00608         {"ctime",   rb_stat_ctime},
00609     };
00610 
00611     str = rb_str_buf_new2("#<");
00612     rb_str_buf_cat2(str, rb_obj_classname(self));
00613     rb_str_buf_cat2(str, " ");
00614 
00615     for (i = 0; i < sizeof(member)/sizeof(member[0]); i++) {
00616         VALUE v;
00617 
00618         if (i > 0) {
00619             rb_str_buf_cat2(str, ", ");
00620         }
00621         rb_str_buf_cat2(str, member[i].name);
00622         rb_str_buf_cat2(str, "=");
00623         v = (*member[i].func)(self);
00624         if (i == 2) {           /* mode */
00625             char buf[32];
00626 
00627             sprintf(buf, "0%lo", NUM2ULONG(v));
00628             rb_str_buf_cat2(str, buf);
00629         }
00630         else if (i == 0 || i == 6) { /* dev/rdev */
00631             char buf[32];
00632 
00633             sprintf(buf, "0x%lx", NUM2ULONG(v));
00634             rb_str_buf_cat2(str, buf);
00635         }
00636         else {
00637             rb_str_append(str, rb_inspect(v));
00638         }
00639     }
00640     rb_str_buf_cat2(str, ">");
00641     OBJ_INFECT(str, self);
00642 
00643     return str;
00644 }
00645 
00646 static int
00647 rb_stat(file, st)
00648     VALUE file;
00649     struct stat *st;
00650 {
00651     VALUE tmp;
00652 
00653     tmp = rb_check_convert_type(file, T_FILE, "IO", "to_io");
00654     if (!NIL_P(tmp)) {
00655         OpenFile *fptr;
00656 
00657         rb_secure(2);
00658         GetOpenFile(tmp, fptr);
00659         return fstat(fileno(fptr->f), st);
00660     }
00661     SafeStringValue(file);
00662     return stat(StringValueCStr(file), st);
00663 }
00664 
00665 #ifdef _WIN32
00666 static HANDLE
00667 w32_io_info(file, st)
00668     VALUE *file;
00669     BY_HANDLE_FILE_INFORMATION *st;
00670 {
00671     VALUE tmp;
00672     HANDLE f, ret = 0;
00673 
00674     tmp = rb_check_convert_type(*file, T_FILE, "IO", "to_io");
00675     if (!NIL_P(tmp)) {
00676         OpenFile *fptr;
00677 
00678         GetOpenFile(tmp, fptr);
00679         f = (HANDLE)rb_w32_get_osfhandle(fileno(fptr->f));
00680         if (f == (HANDLE)-1) return INVALID_HANDLE_VALUE;
00681     }
00682     else {
00683         SafeStringValue(*file);
00684         f = CreateFile(StringValueCStr(*file), 0,
00685                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
00686                        rb_w32_iswin95() ? 0 : FILE_FLAG_BACKUP_SEMANTICS, NULL);
00687         if (f == INVALID_HANDLE_VALUE) return f;
00688         ret = f;
00689     }
00690     if (GetFileType(f) == FILE_TYPE_DISK) {
00691         ZeroMemory(st, sizeof(*st));
00692         if (GetFileInformationByHandle(f, st)) return ret;
00693     }
00694     if (ret) CloseHandle(ret);
00695     return INVALID_HANDLE_VALUE;
00696 }
00697 #endif
00698 
00699 /*
00700  *  call-seq:
00701  *     File.stat(file_name)   =>  stat
00702  *  
00703  *  Returns a <code>File::Stat</code> object for the named file (see
00704  *  <code>File::Stat</code>).
00705  *     
00706  *     File.stat("testfile").mtime   #=> Tue Apr 08 12:58:04 CDT 2003
00707  *     
00708  */
00709 
00710 static VALUE
00711 rb_file_s_stat(klass, fname)
00712     VALUE klass, fname;
00713 {
00714     struct stat st;
00715 
00716     SafeStringValue(fname);
00717     if (rb_stat(fname, &st) < 0) {
00718         rb_sys_fail(StringValueCStr(fname));
00719     }
00720     return stat_new(&st);
00721 }
00722 
00723 /*
00724  *  call-seq:
00725  *     ios.stat    => stat
00726  *  
00727  *  Returns status information for <em>ios</em> as an object of type
00728  *  <code>File::Stat</code>.
00729  *     
00730  *     f = File.new("testfile")
00731  *     s = f.stat
00732  *     "%o" % s.mode   #=> "100644"
00733  *     s.blksize       #=> 4096
00734  *     s.atime         #=> Wed Apr 09 08:53:54 CDT 2003
00735  *     
00736  */
00737 
00738 static VALUE
00739 rb_io_stat(obj)
00740     VALUE obj;
00741 {
00742     OpenFile *fptr;
00743     struct stat st;
00744 
00745     GetOpenFile(obj, fptr);
00746     if (fstat(fileno(fptr->f), &st) == -1) {
00747         rb_sys_fail(fptr->path);
00748     }
00749     return stat_new(&st);
00750 }
00751 
00752 /*
00753  *  call-seq:
00754  *     File.lstat(file_name)   => stat
00755  *  
00756  *  Same as <code>File::stat</code>, but does not follow the last symbolic
00757  *  link. Instead, reports on the link itself.
00758  *     
00759  *     File.symlink("testfile", "link2test")   #=> 0
00760  *     File.stat("testfile").size              #=> 66
00761  *     File.lstat("link2test").size            #=> 8
00762  *     File.stat("link2test").size             #=> 66
00763  *     
00764  */
00765 
00766 static VALUE
00767 rb_file_s_lstat(klass, fname)
00768     VALUE klass, fname;
00769 {
00770 #ifdef HAVE_LSTAT
00771     struct stat st;
00772 
00773     SafeStringValue(fname);
00774     if (lstat(StringValueCStr(fname), &st) == -1) {
00775         rb_sys_fail(RSTRING(fname)->ptr);
00776     }
00777     return stat_new(&st);
00778 #else
00779     return rb_file_s_stat(klass, fname);
00780 #endif
00781 }
00782 
00783 
00784 /*
00785  *  call-seq:
00786  *     file.lstat   =>  stat
00787  *  
00788  *  Same as <code>IO#stat</code>, but does not follow the last symbolic
00789  *  link. Instead, reports on the link itself.
00790  *     
00791  *     File.symlink("testfile", "link2test")   #=> 0
00792  *     File.stat("testfile").size              #=> 66
00793  *     f = File.new("link2test")
00794  *     f.lstat.size                            #=> 8
00795  *     f.stat.size                             #=> 66
00796  */
00797 
00798 static VALUE
00799 rb_file_lstat(obj)
00800     VALUE obj;
00801 {
00802 #ifdef HAVE_LSTAT
00803     OpenFile *fptr;
00804     struct stat st;
00805 
00806     rb_secure(2);
00807     GetOpenFile(obj, fptr);
00808     if (!fptr->path) return Qnil;
00809     if (lstat(fptr->path, &st) == -1) {
00810         rb_sys_fail(fptr->path);
00811     }
00812     return stat_new(&st);
00813 #else
00814     return rb_io_stat(obj);
00815 #endif
00816 }
00817 
00818 static int
00819 group_member(gid)
00820     GETGROUPS_T gid;
00821 {
00822 #ifndef _WIN32
00823     if (getgid() ==  gid)
00824         return Qtrue;
00825 
00826 # ifdef HAVE_GETGROUPS
00827 #  ifndef NGROUPS
00828 #   ifdef NGROUPS_MAX
00829 #    define NGROUPS NGROUPS_MAX
00830 #   else
00831 #    define NGROUPS 32
00832 #   endif
00833 #  endif
00834     {
00835         GETGROUPS_T gary[NGROUPS];
00836         int anum;
00837 
00838         anum = getgroups(NGROUPS, gary);
00839         while (--anum >= 0)
00840             if (gary[anum] == gid)
00841                 return Qtrue;
00842     }
00843 # endif
00844 #endif
00845     return Qfalse;
00846 }
00847 
00848 #ifndef S_IXUGO
00849 #  define S_IXUGO               (S_IXUSR | S_IXGRP | S_IXOTH)
00850 #endif
00851 
00852 int
00853 eaccess(path, mode)
00854      const char *path;
00855      int mode;
00856 {
00857 #if defined(S_IXGRP) && !defined(_WIN32) && !defined(__CYGWIN__)
00858     struct stat st;
00859     int euid;
00860 
00861     if (stat(path, &st) < 0) return -1;
00862 
00863     euid = geteuid();
00864 
00865     if (euid == 0) {
00866         /* Root can read or write any file. */
00867         if (!(mode & X_OK))
00868             return 0;
00869 
00870         /* Root can execute any file that has any one of the execute
00871            bits set. */
00872         if (st.st_mode & S_IXUGO)
00873             return 0;
00874 
00875         return -1;
00876     }
00877 
00878     if (st.st_uid == euid)        /* owner */
00879         mode <<= 6;
00880     else if (getegid() == st.st_gid || group_member(st.st_gid))
00881         mode <<= 3;
00882 
00883     if ((st.st_mode & mode) == mode) return 0;
00884 
00885     return -1;
00886 #else
00887     return access(path, mode);
00888 #endif
00889 }
00890 
00891 
00892 /*
00893  * Document-class: FileTest
00894  *
00895  *  <code>FileTest</code> implements file test operations similar to
00896  *  those used in <code>File::Stat</code>. It exists as a standalone
00897  *  module, and its methods are also insinuated into the <code>File</code>
00898  *  class. (Note that this is not done by inclusion: the interpreter cheats).
00899  *     
00900  */
00901 
00902 
00903 /*
00904  * call-seq:
00905  *   File.directory?(file_name)   =>  true or false
00906  *
00907  * Returns <code>true</code> if the named file is a directory,
00908  * <code>false</code> otherwise.
00909  *
00910  *    File.directory?(".")
00911  */
00912 
00913 static VALUE
00914 test_d(obj, fname)
00915     VALUE obj, fname;
00916 {
00917 #ifndef S_ISDIR
00918 #   define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR)
00919 #endif
00920 
00921     struct stat st;
00922 
00923     if (rb_stat(fname, &st) < 0) return Qfalse;
00924     if (S_ISDIR(st.st_mode)) return Qtrue;
00925     return Qfalse;
00926 }
00927 
00928 /*
00929  * call-seq:
00930  *   File.pipe?(file_name)   =>  true or false
00931  *
00932  * Returns <code>true</code> if the named file is a pipe.
00933  */
00934 
00935 static VALUE
00936 test_p(obj, fname)
00937     VALUE obj, fname;
00938 {
00939 #ifdef S_IFIFO
00940 #  ifndef S_ISFIFO
00941 #    define S_ISFIFO(m) ((m & S_IFMT) == S_IFIFO)
00942 #  endif
00943 
00944     struct stat st;
00945 
00946     if (rb_stat(fname, &st) < 0) return Qfalse;
00947     if (S_ISFIFO(st.st_mode)) return Qtrue;
00948 
00949 #endif
00950     return Qfalse;
00951 }
00952 
00953 /*
00954  * call-seq:
00955  *   File.symlink?(file_name)   =>  true or false
00956  *
00957  * Returns <code>true</code> if the named file is a symbolic link.
00958  */
00959 
00960 static VALUE
00961 test_l(obj, fname)
00962     VALUE obj, fname;
00963 {
00964 #ifndef S_ISLNK
00965 #  ifdef _S_ISLNK
00966 #    define S_ISLNK(m) _S_ISLNK(m)
00967 #  elif defined __BORLANDC__
00968 #    ifdef _S_IFLNK
00969 #      define S_ISLNK(m) (((unsigned short)(m) & S_IFMT) == _S_IFLNK)
00970 #    else
00971 #      ifdef S_IFLNK
00972 #        define S_ISLNK(m) (((unsigned short)(m) & S_IFMT) == S_IFLNK)
00973 #      endif
00974 #    endif
00975 #  else
00976 #    ifdef _S_IFLNK
00977 #      define S_ISLNK(m) ((m & S_IFMT) == _S_IFLNK)
00978 #    else
00979 #      ifdef S_IFLNK
00980 #        define S_ISLNK(m) ((m & S_IFMT) == S_IFLNK)
00981 #      endif
00982 #    endif
00983 #  endif
00984 #endif
00985 
00986 #ifdef S_ISLNK
00987     struct stat st;
00988 
00989     SafeStringValue(fname);
00990     if (lstat(StringValueCStr(fname), &st) < 0) return Qfalse;
00991     if (S_ISLNK(st.st_mode)) return Qtrue;
00992 #endif
00993 
00994     return Qfalse;
00995 }
00996 
00997 /*
00998  * call-seq:
00999  *   File.socket?(file_name)   =>  true or false
01000  *
01001  * Returns <code>true</code> if the named file is a socket.
01002  */
01003 
01004 static VALUE
01005 test_S(obj, fname)
01006     VALUE obj, fname;
01007 {
01008 #ifndef S_ISSOCK
01009 #  ifdef _S_ISSOCK
01010 #    define S_ISSOCK(m) _S_ISSOCK(m)
01011 #  elif defined __BORLANDC__
01012 #    ifdef _S_IFSOCK
01013 #      define S_ISSOCK(m) (((unsigned short)(m) & S_IFMT) == _S_IFSOCK)
01014 #    else
01015 #      ifdef S_IFSOCK
01016 #        define S_ISSOCK(m) (((unsigned short)(m) & S_IFMT) == S_IFSOCK)
01017 #      endif
01018 #    endif
01019 #  else
01020 #    ifdef _S_IFSOCK
01021 #      define S_ISSOCK(m) ((m & S_IFMT) == _S_IFSOCK)
01022 #    else
01023 #      ifdef S_IFSOCK
01024 #        define S_ISSOCK(m) ((m & S_IFMT) == S_IFSOCK)
01025 #      endif
01026 #    endif
01027 #  endif
01028 #endif
01029 
01030 #ifdef S_ISSOCK
01031     struct stat st;
01032 
01033     if (rb_stat(fname, &st) < 0) return Qfalse;
01034     if (S_ISSOCK(st.st_mode)) return Qtrue;
01035 
01036 #endif
01037     return Qfalse;
01038 }
01039 
01040 /*
01041  * call-seq:
01042  *   File.blockdev?(file_name)   =>  true or false
01043  *
01044  * Returns <code>true</code> if the named file is a block device.
01045  */
01046 
01047 static VALUE
01048 test_b(obj, fname)
01049     VALUE obj, fname;
01050 {
01051 #ifndef S_ISBLK
01052 #   ifdef S_IFBLK
01053 #       define S_ISBLK(m) ((m & S_IFMT) == S_IFBLK)
01054 #   else
01055 #       define S_ISBLK(m) (0)  /* anytime false */
01056 #   endif
01057 #endif
01058 
01059 #ifdef S_ISBLK
01060     struct stat st;
01061 
01062     if (rb_stat(fname, &st) < 0) return Qfalse;
01063     if (S_ISBLK(st.st_mode)) return Qtrue;
01064 
01065 #endif
01066     return Qfalse;
01067 }
01068 
01069 /*
01070  * call-seq:
01071  *   File.chardev?(file_name)   =>  true or false
01072  *
01073  * Returns <code>true</code> if the named file is a character device.
01074  */
01075 static VALUE
01076 test_c(obj, fname)
01077     VALUE obj, fname;
01078 {
01079 #ifndef S_ISCHR
01080 #   define S_ISCHR(m) ((m & S_IFMT) == S_IFCHR)
01081 #endif
01082 
01083     struct stat st;
01084 
01085     if (rb_stat(fname, &st) < 0) return Qfalse;
01086     if (S_ISCHR(st.st_mode)) return Qtrue;
01087 
01088     return Qfalse;
01089 }
01090 
01091 
01092 /*
01093  * call-seq:
01094  *    File.exist?(file_name)    =>  true or false
01095  *    File.exists?(file_name)   =>  true or false    (obsolete)
01096  *
01097  * Return <code>true</code> if the named file exists.
01098  */
01099 
01100 static VALUE
01101 test_e(obj, fname)
01102     VALUE obj, fname;
01103 {
01104     struct stat st;
01105 
01106     if (rb_stat(fname, &st) < 0) return Qfalse;
01107     return Qtrue;
01108 }
01109 
01110 /*
01111  * call-seq:
01112  *    File.readable?(file_name)   => true or false
01113  *
01114  * Returns <code>true</code> if the named file is readable by the effective
01115  * user id of this process.
01116  */
01117 
01118 static VALUE
01119 test_r(obj, fname)
01120     VALUE obj, fname;
01121 {
01122     SafeStringValue(fname);
01123     if (eaccess(StringValueCStr(fname), R_OK) < 0) return Qfalse;
01124     return Qtrue;
01125 }
01126 
01127 /*
01128  * call-seq:
01129  *    File.readable_real?(file_name)   => true or false
01130  *
01131  * Returns <code>true</code> if the named file is readable by the real
01132  * user id of this process.
01133  */
01134 
01135 static VALUE
01136 test_R(obj, fname)
01137     VALUE obj, fname;
01138 {
01139     SafeStringValue(fname);
01140     if (access(StringValueCStr(fname), R_OK) < 0) return Qfalse;
01141     return Qtrue;
01142 }
01143 
01144 
01145 /*
01146  * call-seq:
01147  *    File.writable?(file_name)   => true or false
01148  *
01149  * Returns <code>true</code> if the named file is writable by the effective
01150  * user id of this process.
01151  */
01152 
01153 static VALUE
01154 test_w(obj, fname)
01155     VALUE obj, fname;
01156 {
01157     SafeStringValue(fname);
01158     if (eaccess(StringValueCStr(fname), W_OK) < 0) return Qfalse;
01159     return Qtrue;
01160 }
01161 
01162 /*
01163  * call-seq:
01164  *    File.writable_real?(file_name)   => true or false
01165  *
01166  * Returns <code>true</code> if the named file is writable by the real
01167  * user id of this process.
01168  */
01169 
01170 static VALUE
01171 test_W(obj, fname)
01172     VALUE obj, fname;
01173 {
01174     SafeStringValue(fname);
01175     if (access(StringValueCStr(fname), W_OK) < 0) return Qfalse;
01176     return Qtrue;
01177 }
01178 
01179 /*
01180  * call-seq:
01181  *    File.executable?(file_name)   => true or false
01182  *
01183  * Returns <code>true</code> if the named file is executable by the effective
01184  * user id of this process.
01185  */
01186 
01187 static VALUE
01188 test_x(obj, fname)
01189     VALUE obj, fname;
01190 {
01191     SafeStringValue(fname);
01192     if (eaccess(StringValueCStr(fname), X_OK) < 0) return Qfalse;
01193     return Qtrue;
01194 }
01195 
01196 /*
01197  * call-seq:
01198  *    File.executable_real?(file_name)   => true or false
01199  *
01200  * Returns <code>true</code> if the named file is executable by the real
01201  * user id of this process.
01202  */
01203 
01204 static VALUE
01205 test_X(obj, fname)
01206     VALUE obj, fname;
01207 {
01208     SafeStringValue(fname);
01209     if (access(StringValueCStr(fname), X_OK) < 0) return Qfalse;
01210     return Qtrue;
01211 }
01212 
01213 #ifndef S_ISREG
01214 #   define S_ISREG(m) ((m & S_IFMT) == S_IFREG)
01215 #endif
01216 
01217 /*
01218  * call-seq:
01219  *    File.file?(file_name)   => true or false
01220  *
01221  * Returns <code>true</code> if the named file exists and is a
01222  * regular file.
01223  */
01224 
01225 static VALUE
01226 test_f(obj, fname)
01227     VALUE obj, fname;
01228 {
01229     struct stat st;
01230 
01231     if (rb_stat(fname, &st) < 0) return Qfalse;
01232     if (S_ISREG(st.st_mode)) return Qtrue;
01233     return Qfalse;
01234 }
01235 
01236 /*
01237  * call-seq:
01238  *    File.zero?(file_name)   => true or false
01239  *
01240  * Returns <code>true</code> if the named file exists and has
01241  * a zero size.
01242  */
01243 
01244 static VALUE
01245 test_z(obj, fname)
01246     VALUE obj, fname;
01247 {
01248     struct stat st;
01249 
01250     if (rb_stat(fname, &st) < 0) return Qfalse;
01251     if (st.st_size == 0) return Qtrue;
01252     return Qfalse;
01253 }
01254 
01255 /*
01256  * call-seq:
01257  *    File.file?(file_name)   => integer  or  nil
01258  *
01259  * Returns <code>nil</code> if <code>file_name</code> doesn't
01260  * exist or has zero size, the size of the file otherwise.
01261  */
01262 
01263 static VALUE
01264 test_s(obj, fname)
01265     VALUE obj, fname;
01266 {
01267     struct stat st;
01268 
01269     if (rb_stat(fname, &st) < 0) return Qnil;
01270     if (st.st_size == 0) return Qnil;
01271     return OFFT2NUM(st.st_size);
01272 }
01273 
01274 /*
01275  * call-seq:
01276  *    File.owned?(file_name)   => true or false
01277  *
01278  * Returns <code>true</code> if the named file exists and the
01279  * effective used id of the calling process is the owner of
01280  * the file.
01281  */
01282 
01283 static VALUE
01284 test_owned(obj, fname)
01285     VALUE obj, fname;
01286 {
01287     struct stat st;
01288 
01289     if (rb_stat(fname, &st) < 0) return Qfalse;
01290     if (st.st_uid == geteuid()) return Qtrue;
01291     return Qfalse;
01292 }
01293 
01294 static VALUE
01295 test_rowned(obj, fname)
01296     VALUE obj, fname;
01297 {
01298     struct stat st;
01299 
01300     if (rb_stat(fname, &st) < 0) return Qfalse;
01301     if (st.st_uid == getuid()) return Qtrue;
01302     return Qfalse;
01303 }
01304 
01305 /*
01306  * call-seq:
01307  *    File.grpowned?(file_name)   => true or false
01308  *
01309  * Returns <code>true</code> if the named file exists and the
01310  * effective group id of the calling process is the owner of
01311  * the file. Returns <code>false</code> on Windows.
01312  */
01313 
01314 static VALUE
01315 test_grpowned(obj, fname)
01316     VALUE obj, fname;
01317 {
01318 #ifndef _WIN32
01319     struct stat st;
01320 
01321     if (rb_stat(fname, &st) < 0) return Qfalse;
01322     if (st.st_gid == getegid()) return Qtrue;
01323 #endif
01324     return Qfalse;
01325 }
01326 
01327 #if defined(S_ISUID) || defined(S_ISGID) || defined(S_ISVTX)
01328 static VALUE
01329 check3rdbyte(fname, mode)
01330     VALUE fname;
01331     int mode;
01332 {
01333     struct stat st;
01334 
01335     SafeStringValue(fname);
01336     if (stat(StringValueCStr(fname), &st) < 0) return Qfalse;
01337     if (st.st_mode & mode) return Qtrue;
01338     return Qfalse;
01339 }
01340 #endif
01341 
01342 /*
01343  * call-seq:
01344  *   File.setuid?(file_name)   =>  true or false
01345  *
01346  * Returns <code>true</code> if the named file has the setuid bit set.
01347  */
01348 
01349 static VALUE
01350 test_suid(obj, fname)
01351     VALUE obj, fname;
01352 {
01353 #ifdef S_ISUID
01354     return check3rdbyte(fname, S_ISUID);
01355 #else
01356     return Qfalse;
01357 #endif
01358 }
01359 
01360 /*
01361  * call-seq:
01362  *   File.setgid?(file_name)   =>  true or false
01363  *
01364  * Returns <code>true</code> if the named file has the setgid bit set.
01365  */
01366 
01367 static VALUE
01368 test_sgid(obj, fname)
01369     VALUE obj, fname;
01370 {
01371 #ifdef S_ISGID
01372     return check3rdbyte(fname, S_ISGID);
01373 #else
01374     return Qfalse;
01375 #endif
01376 }
01377 
01378 /*
01379  * call-seq:
01380  *   File.sticky?(file_name)   =>  true or false
01381  *
01382  * Returns <code>true</code> if the named file has the sticky bit set.
01383  */
01384 
01385 static VALUE
01386 test_sticky(obj, fname)
01387     VALUE obj, fname;
01388 {
01389 #ifdef S_ISVTX
01390     return check3rdbyte(fname, S_ISVTX);
01391 #else
01392     return Qnil;
01393 #endif
01394 }
01395 
01396 /*
01397  * call-seq:
01398  *   File.identical?(file_1, file_2)   =>  true or false
01399  *
01400  * Returns <code>true</code> if the named files are identical.
01401  *
01402  *     open("a", "w") {}
01403  *     p File.identical?("a", "a")      #=> true
01404  *     p File.identical?("a", "./a")    #=> true
01405  *     File.link("a", "b")
01406  *     p File.identical?("a", "b")      #=> true
01407  *     File.symlink("a", "c")
01408  *     p File.identical?("a", "c")      #=> true
01409  *     open("d", "w") {}
01410  *     p File.identical?("a", "d")      #=> false
01411  */
01412 
01413 static VALUE
01414 test_identical(obj, fname1, fname2)
01415     VALUE obj, fname1, fname2;
01416 {
01417 #ifndef DOSISH
01418     struct stat st1, st2;
01419 
01420     if (rb_stat(fname1, &st1) < 0) return Qfalse;
01421     if (rb_stat(fname2, &st2) < 0) return Qfalse;
01422     if (st1.st_dev != st2.st_dev) return Qfalse;
01423     if (st1.st_ino != st2.st_ino) return Qfalse;
01424 #else
01425 #ifdef _WIN32
01426     BY_HANDLE_FILE_INFORMATION st1, st2;
01427     HANDLE f1 = 0, f2 = 0;
01428 #endif
01429 
01430     rb_secure(2);
01431 #ifdef _WIN32
01432     f1 = w32_io_info(&fname1, &st1);
01433     if (f1 == INVALID_HANDLE_VALUE) return Qfalse;
01434     f2 = w32_io_info(&fname2, &st2);
01435     if (f1) CloseHandle(f1);
01436     if (f2 == INVALID_HANDLE_VALUE) return Qfalse;
01437     if (f2) CloseHandle(f2);
01438 
01439     if (st1.dwVolumeSerialNumber == st2.dwVolumeSerialNumber &&
01440         st1.nFileIndexHigh == st2.nFileIndexHigh &&
01441         st1.nFileIndexLow == st2.nFileIndexLow)
01442         return Qtrue;
01443     if (!f1 || !f2) return Qfalse;
01444     if (rb_w32_iswin95()) return Qfalse;
01445 #else
01446     SafeStringValue(fname1);
01447     fname1 = rb_str_new4(fname1);
01448     SafeStringValue(fname2);
01449     if (access(RSTRING(fname1)->ptr, 0)) return Qfalse;
01450     if (access(RSTRING(fname2)->ptr, 0)) return Qfalse;
01451 #endif
01452     fname1 = rb_file_expand_path(fname1, Qnil);
01453     fname2 = rb_file_expand_path(fname2, Qnil);
01454     if (RSTRING(fname1)->len != RSTRING(fname2)->len) return Qfalse;
01455     if (rb_memcicmp(RSTRING(fname1)->ptr, RSTRING(fname2)->ptr, RSTRING(fname1)->len))
01456         return Qfalse;
01457 #endif
01458     return Qtrue;
01459 }
01460 
01461 /*
01462  * call-seq:
01463  *    File.size(file_name)   => integer
01464  *
01465  * Returns the size of <code>file_name</code>.
01466  */
01467 
01468 static VALUE
01469 rb_file_s_size(klass, fname)
01470     VALUE klass, fname;
01471 {
01472     struct stat st;
01473 
01474     if (rb_stat(fname, &st) < 0)
01475         rb_sys_fail(StringValueCStr(fname));
01476     return OFFT2NUM(st.st_size);
01477 }
01478 
01479 static VALUE
01480 rb_file_ftype(st)
01481     struct stat *st;
01482 {
01483     char *t;
01484 
01485     if (S_ISREG(st->st_mode)) {
01486         t = "file";
01487     }
01488     else if (S_ISDIR(st->st_mode)) {
01489         t = "directory";
01490     }
01491     else if (S_ISCHR(st->st_mode)) {
01492         t = "characterSpecial";
01493     }
01494 #ifdef S_ISBLK
01495     else if (S_ISBLK(st->st_mode)) {
01496         t = "blockSpecial";
01497     }
01498 #endif
01499 #ifdef S_ISFIFO
01500     else if (S_ISFIFO(st->st_mode)) {
01501         t = "fifo";
01502     }
01503 #endif
01504 #ifdef S_ISLNK
01505     else if (S_ISLNK(st->st_mode)) {
01506         t = "link";
01507     }
01508 #endif
01509 #ifdef S_ISSOCK
01510     else if (S_ISSOCK(st->st_mode)) {
01511         t = "socket";
01512     }
01513 #endif
01514     else {
01515         t = "unknown";
01516     }
01517 
01518     return rb_str_new2(t);
01519 }
01520 
01521 /*
01522  *  call-seq:
01523  *     File.ftype(file_name)   => string
01524  *  
01525  *  Identifies the type of the named file; the return string is one of
01526  *  ``<code>file</code>'', ``<code>directory</code>'',
01527  *  ``<code>characterSpecial</code>'', ``<code>blockSpecial</code>'',
01528  *  ``<code>fifo</code>'', ``<code>link</code>'',
01529  *  ``<code>socket</code>'', or ``<code>unknown</code>''.
01530  *     
01531  *     File.ftype("testfile")            #=> "file"
01532  *     File.ftype("/dev/tty")            #=> "characterSpecial"
01533  *     File.ftype("/tmp/.X11-unix/X0")   #=> "socket"
01534  */
01535 
01536 static VALUE
01537 rb_file_s_ftype(klass, fname)
01538     VALUE klass, fname;
01539 {
01540     struct stat st;
01541 
01542     SafeStringValue(fname);
01543     if (lstat(StringValueCStr(fname), &st) == -1) {
01544         rb_sys_fail(RSTRING(fname)->ptr);
01545     }
01546 
01547     return rb_file_ftype(&st);
01548 }
01549 
01550 /*
01551  *  call-seq:
01552  *     File.atime(file_name)  =>  time
01553  *  
01554  *  Returns the last access time for the named file as a Time object).
01555  *     
01556  *     File.atime("testfile")   #=> Wed Apr 09 08:51:48 CDT 2003
01557  *     
01558  */
01559 
01560 static VALUE
01561 rb_file_s_atime(klass, fname)
01562     VALUE klass, fname;
01563 {
01564     struct stat st;
01565 
01566     if (rb_stat(fname, &st) < 0)
01567         rb_sys_fail(StringValueCStr(fname));
01568     return rb_time_new(st.st_atime, 0);
01569 }
01570 
01571 /*
01572  *  call-seq:
01573  *     file.atime    => time
01574  *  
01575  *  Returns the last access time (a <code>Time</code> object)
01576  *   for <i>file</i>, or epoch if <i>file</i> has not been accessed.
01577  *     
01578  *     File.new("testfile").atime   #=> Wed Dec 31 18:00:00 CST 1969
01579  *     
01580  */
01581 
01582 static VALUE
01583 rb_file_atime(obj)
01584     VALUE obj;
01585 {
01586     OpenFile *fptr;
01587     struct stat st;
01588 
01589     GetOpenFile(obj, fptr);
01590     if (fstat(fileno(fptr->f), &st) == -1) {
01591         rb_sys_fail(fptr->path);
01592     }
01593     return rb_time_new(st.st_atime, 0);
01594 }
01595 
01596 /*
01597  *  call-seq:
01598  *     File.mtime(file_name)  =>  time
01599  *  
01600  *  Returns the modification time for the named file as a Time object.
01601  *     
01602  *     File.mtime("testfile")   #=> Tue Apr 08 12:58:04 CDT 2003
01603  *     
01604  */
01605 
01606 static VALUE
01607 rb_file_s_mtime(klass, fname)
01608     VALUE klass, fname;
01609 {
01610     struct stat st;
01611 
01612     if (rb_stat(fname, &st) < 0)
01613         rb_sys_fail(RSTRING(fname)->ptr);
01614     return rb_time_new(st.st_mtime, 0);
01615 }
01616 
01617 /*
01618  *  call-seq:
01619  *     file.mtime -> time
01620  *  
01621  *  Returns the modification time for <i>file</i>.
01622  *     
01623  *     File.new("testfile").mtime   #=> Wed Apr 09 08:53:14 CDT 2003
01624  *     
01625  */
01626 
01627 static VALUE
01628 rb_file_mtime(obj)
01629     VALUE obj;
01630 {
01631     OpenFile *fptr;
01632     struct stat st;
01633 
01634     GetOpenFile(obj, fptr);
01635     if (fstat(fileno(fptr->f), &st) == -1) {
01636         rb_sys_fail(fptr->path);
01637     }
01638     return rb_time_new(st.st_mtime, 0);
01639 }
01640 
01641 /*
01642  *  call-seq:
01643  *     File.ctime(file_name)  => time
01644  *  
01645  *  Returns the change time for the named file (the time at which
01646  *  directory information about the file was changed, not the file
01647  *  itself).
01648  *     
01649  *     File.ctime("testfile")   #=> Wed Apr 09 08:53:13 CDT 2003
01650  *     
01651  */
01652 
01653 static VALUE
01654 rb_file_s_ctime(klass, fname)
01655     VALUE klass, fname;
01656 {
01657     struct stat st;
01658 
01659     if (rb_stat(fname, &st) < 0)
01660         rb_sys_fail(RSTRING(fname)->ptr);
01661     return rb_time_new(st.st_ctime, 0);
01662 }
01663 
01664 /*
01665  *  call-seq:
01666  *     file.ctime -> time
01667  *  
01668  *  Returns the change time for <i>file</i> (that is, the time directory
01669  *  information about the file was changed, not the file itself).
01670  *     
01671  *     File.new("testfile").ctime   #=> Wed Apr 09 08:53:14 CDT 2003
01672  *     
01673  */
01674 
01675 static VALUE
01676 rb_file_ctime(obj)
01677     VALUE obj;
01678 {
01679     OpenFile *fptr;
01680     struct stat st;
01681 
01682     GetOpenFile(obj, fptr);
01683     if (fstat(fileno(fptr->f), &st) == -1) {
01684         rb_sys_fail(fptr->path);
01685     }
01686     return rb_time_new(st.st_ctime, 0);
01687 }
01688 
01689 static void chmod_internal (const char *, void *);
01690 static void
01691 chmod_internal(path, mode)
01692     const char *path;
01693     void *mode;
01694 {
01695     if (chmod(path, (int)mode) < 0)
01696         rb_sys_fail(path);
01697 }
01698 
01699 /*
01700  *  call-seq:
01701  *     File.chmod(mode_int, file_name, ... ) -> integer
01702  *  
01703  *  Changes permission bits on the named file(s) to the bit pattern
01704  *  represented by <i>mode_int</i>. Actual effects are operating system
01705  *  dependent (see the beginning of this section). On Unix systems, see
01706  *  <code>chmod(2)</code> for details. Returns the number of files
01707  *  processed.
01708  *     
01709  *     File.chmod(0644, "testfile", "out")   #=> 2
01710  */
01711 
01712 static VALUE
01713 rb_file_s_chmod(argc, argv)
01714     int argc;
01715     VALUE *argv;
01716 {
01717     VALUE vmode;
01718     VALUE rest;
01719     int mode;
01720     long n;
01721 
01722     rb_secure(2);
01723     rb_scan_args(argc, argv, "1*", &vmode, &rest);
01724     mode = NUM2INT(vmode);
01725 
01726     n = apply2files(chmod_internal, rest, (void *)(long)mode);
01727     return LONG2FIX(n);
01728 }
01729 
01730 /*
01731  *  call-seq:
01732  *     file.chmod(mode_int)   => 0
01733  *  
01734  *  Changes permission bits on <i>file</i> to the bit pattern
01735  *  represented by <i>mode_int</i>. Actual effects are platform
01736  *  dependent; on Unix systems, see <code>chmod(2)</code> for details.
01737  *  Follows symbolic links. Also see <code>File#lchmod</code>.
01738  *     
01739  *     f = File.new("out", "w");
01740  *     f.chmod(0644)   #=> 0
01741  */
01742 
01743 static VALUE
01744 rb_file_chmod(obj, vmode)
01745     VALUE obj, vmode;
01746 {
01747     OpenFile *fptr;
01748     int mode;
01749 
01750     rb_secure(2);
01751     mode = NUM2INT(vmode);
01752 
01753     GetOpenFile(obj, fptr);
01754 #ifdef HAVE_FCHMOD
01755     if (fchmod(fileno(fptr->f), mode) == -1)
01756         rb_sys_fail(fptr->path);
01757 #else
01758     if (!fptr->path) return Qnil;
01759     if (chmod(fptr->path, mode) == -1)
01760         rb_sys_fail(fptr->path);
01761 #endif
01762 
01763     return INT2FIX(0);
01764 }
01765 
01766 #if defined(HAVE_LCHMOD)
01767 static void lchmod_internal (const char *, void *);
01768 static void
01769 lchmod_internal(path, mode)
01770     const char *path;
01771     void *mode;
01772 {
01773     if (lchmod(path, (int)mode) < 0)
01774         rb_sys_fail(path);
0