Ruby  1.9.3p385(2013-02-06revision39114)
file.c
Go to the documentation of this file.
00001 /**********************************************************************
00002 
00003   file.c -
00004 
00005   $Author: usa $
00006   created at: Mon Nov 15 12:24:34 JST 1993
00007 
00008   Copyright (C) 1993-2007 Yukihiro Matsumoto
00009   Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
00010   Copyright (C) 2000  Information-technology Promotion Agency, Japan
00011 
00012 **********************************************************************/
00013 
00014 #ifdef _WIN32
00015 #include "missing/file.h"
00016 #endif
00017 #ifdef __CYGWIN__
00018 #include <windows.h>
00019 #include <sys/cygwin.h>
00020 #include <wchar.h>
00021 #endif
00022 
00023 #include "ruby/ruby.h"
00024 #include "ruby/io.h"
00025 #include "ruby/util.h"
00026 #include "dln.h"
00027 #include "internal.h"
00028 
00029 #ifdef HAVE_UNISTD_H
00030 #include <unistd.h>
00031 #endif
00032 
00033 #ifdef HAVE_SYS_FILE_H
00034 # include <sys/file.h>
00035 #else
00036 int flock(int, int);
00037 #endif
00038 
00039 #ifdef HAVE_SYS_PARAM_H
00040 # include <sys/param.h>
00041 #endif
00042 #ifndef MAXPATHLEN
00043 # define MAXPATHLEN 1024
00044 #endif
00045 
00046 #include <ctype.h>
00047 
00048 #include <time.h>
00049 
00050 #ifdef HAVE_UTIME_H
00051 #include <utime.h>
00052 #elif defined HAVE_SYS_UTIME_H
00053 #include <sys/utime.h>
00054 #endif
00055 
00056 #ifdef HAVE_PWD_H
00057 #include <pwd.h>
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_FCNTL_H)
00068 #include <fcntl.h>
00069 #endif
00070 
00071 #if !defined HAVE_LSTAT && !defined lstat
00072 #define lstat stat
00073 #endif
00074 
00075 /* define system APIs */
00076 #ifdef _WIN32
00077 #define STAT(p, s)      rb_w32_ustati64((p), (s))
00078 #undef lstat
00079 #define lstat(p, s)     rb_w32_ustati64((p), (s))
00080 #undef access
00081 #define access(p, m)    rb_w32_uaccess((p), (m))
00082 #undef chmod
00083 #define chmod(p, m)     rb_w32_uchmod((p), (m))
00084 #undef chown
00085 #define chown(p, o, g)  rb_w32_uchown((p), (o), (g))
00086 #undef utime
00087 #define utime(p, t)     rb_w32_uutime((p), (t))
00088 #undef link
00089 #define link(f, t)      rb_w32_ulink((f), (t))
00090 #undef unlink
00091 #define unlink(p)       rb_w32_uunlink(p)
00092 #undef rename
00093 #define rename(f, t)    rb_w32_urename((f), (t))
00094 #else
00095 #define STAT(p, s)      stat((p), (s))
00096 #endif
00097 
00098 #define rb_sys_fail_path(path) rb_sys_fail_str(path)
00099 
00100 #if defined(__BEOS__) || defined(__HAIKU__) /* should not change ID if -1 */
00101 static int
00102 be_chown(const char *path, uid_t owner, gid_t group)
00103 {
00104     if (owner == (uid_t)-1 || group == (gid_t)-1) {
00105         struct stat st;
00106         if (STAT(path, &st) < 0) return -1;
00107         if (owner == (uid_t)-1) owner = st.st_uid;
00108         if (group == (gid_t)-1) group = st.st_gid;
00109     }
00110     return chown(path, owner, group);
00111 }
00112 #define chown be_chown
00113 static int
00114 be_fchown(int fd, uid_t owner, gid_t group)
00115 {
00116     if (owner == (uid_t)-1 || group == (gid_t)-1) {
00117         struct stat st;
00118         if (fstat(fd, &st) < 0) return -1;
00119         if (owner == (uid_t)-1) owner = st.st_uid;
00120         if (group == (gid_t)-1) group = st.st_gid;
00121     }
00122     return fchown(fd, owner, group);
00123 }
00124 #define fchown be_fchown
00125 #endif /* __BEOS__ || __HAIKU__ */
00126 
00127 VALUE rb_cFile;
00128 VALUE rb_mFileTest;
00129 VALUE rb_cStat;
00130 
00131 #define insecure_obj_p(obj, level) ((level) >= 4 || ((level) > 0 && OBJ_TAINTED(obj)))
00132 
00133 static VALUE
00134 file_path_convert(VALUE name)
00135 {
00136 #ifndef _WIN32 /* non Windows == Unix */
00137     rb_encoding *fname_encoding = rb_enc_from_index(ENCODING_GET(name));
00138     rb_encoding *fs_encoding;
00139     if (rb_default_internal_encoding() != NULL
00140             && rb_usascii_encoding() != fname_encoding
00141             && rb_ascii8bit_encoding() != fname_encoding
00142             && (fs_encoding = rb_filesystem_encoding()) != fname_encoding
00143             && !rb_enc_str_asciionly_p(name)) {
00144         /* Don't call rb_filesystem_encoding() before US-ASCII and ASCII-8BIT */
00145         /* fs_encoding should be ascii compatible */
00146         name = rb_str_conv_enc(name, fname_encoding, fs_encoding);
00147     }
00148 #endif
00149     return name;
00150 }
00151 
00152 static VALUE
00153 rb_get_path_check(VALUE obj, int level)
00154 {
00155     VALUE tmp;
00156     ID to_path;
00157     rb_encoding *enc;
00158 
00159     if (insecure_obj_p(obj, level)) {
00160         rb_insecure_operation();
00161     }
00162 
00163     CONST_ID(to_path, "to_path");
00164     tmp = rb_check_funcall(obj, to_path, 0, 0);
00165     if (tmp == Qundef) {
00166         tmp = obj;
00167     }
00168     StringValue(tmp);
00169 
00170     tmp = file_path_convert(tmp);
00171     if (obj != tmp && insecure_obj_p(tmp, level)) {
00172         rb_insecure_operation();
00173     }
00174     enc = rb_enc_get(tmp);
00175     if (!rb_enc_asciicompat(enc)) {
00176         tmp = rb_str_inspect(tmp);
00177         rb_raise(rb_eEncCompatError, "path name must be ASCII-compatible (%s): %s",
00178                  rb_enc_name(enc), RSTRING_PTR(tmp));
00179     }
00180 
00181     StringValueCStr(tmp);
00182 
00183     return rb_str_new4(tmp);
00184 }
00185 
00186 VALUE
00187 rb_get_path_no_checksafe(VALUE obj)
00188 {
00189     return rb_get_path_check(obj, 0);
00190 }
00191 
00192 VALUE
00193 rb_get_path(VALUE obj)
00194 {
00195     return rb_get_path_check(obj, rb_safe_level());
00196 }
00197 
00198 VALUE
00199 rb_str_encode_ospath(VALUE path)
00200 {
00201 #ifdef _WIN32
00202     rb_encoding *enc = rb_enc_get(path);
00203     if (enc != rb_ascii8bit_encoding()) {
00204         rb_encoding *utf8 = rb_utf8_encoding();
00205         if (enc != utf8)
00206             path = rb_str_encode(path, rb_enc_from_encoding(utf8), 0, Qnil);
00207     }
00208     else if (RSTRING_LEN(path) > 0) {
00209         path = rb_str_dup(path);
00210         rb_enc_associate(path, rb_filesystem_encoding());
00211         path = rb_str_encode(path, rb_enc_from_encoding(rb_utf8_encoding()), 0, Qnil);
00212     }
00213 #endif
00214     return path;
00215 }
00216 
00217 static long
00218 apply2files(void (*func)(const char *, VALUE, void *), VALUE vargs, void *arg)
00219 {
00220     long i;
00221     volatile VALUE path;
00222 
00223     rb_secure(4);
00224     for (i=0; i<RARRAY_LEN(vargs); i++) {
00225         const char *s;
00226         path = rb_get_path(RARRAY_PTR(vargs)[i]);
00227         path = rb_str_encode_ospath(path);
00228         s = RSTRING_PTR(path);
00229         (*func)(s, path, arg);
00230     }
00231 
00232     return RARRAY_LEN(vargs);
00233 }
00234 
00235 /*
00236  *  call-seq:
00237  *     file.path  ->  filename
00238  *
00239  *  Returns the pathname used to create <i>file</i> as a string. Does
00240  *  not normalize the name.
00241  *
00242  *     File.new("testfile").path               #=> "testfile"
00243  *     File.new("/tmp/../tmp/xxx", "w").path   #=> "/tmp/../tmp/xxx"
00244  *
00245  */
00246 
00247 static VALUE
00248 rb_file_path(VALUE obj)
00249 {
00250     rb_io_t *fptr;
00251 
00252     fptr = RFILE(rb_io_taint_check(obj))->fptr;
00253     rb_io_check_initialized(fptr);
00254     if (NIL_P(fptr->pathv)) return Qnil;
00255     return rb_obj_taint(rb_str_dup(fptr->pathv));
00256 }
00257 
00258 static size_t
00259 stat_memsize(const void *p)
00260 {
00261     return p ? sizeof(struct stat) : 0;
00262 }
00263 
00264 static const rb_data_type_t stat_data_type = {
00265     "stat",
00266     {NULL, RUBY_TYPED_DEFAULT_FREE, stat_memsize,},
00267 };
00268 
00269 static VALUE
00270 stat_new_0(VALUE klass, struct stat *st)
00271 {
00272     struct stat *nst = 0;
00273 
00274     if (st) {
00275         nst = ALLOC(struct stat);
00276         *nst = *st;
00277     }
00278     return TypedData_Wrap_Struct(klass, &stat_data_type, nst);
00279 }
00280 
00281 static VALUE
00282 stat_new(struct stat *st)
00283 {
00284     return stat_new_0(rb_cStat, st);
00285 }
00286 
00287 static struct stat*
00288 get_stat(VALUE self)
00289 {
00290     struct stat* st;
00291     TypedData_Get_Struct(self, struct stat, &stat_data_type, st);
00292     if (!st) rb_raise(rb_eTypeError, "uninitialized File::Stat");
00293     return st;
00294 }
00295 
00296 static struct timespec stat_mtimespec(struct stat *st);
00297 
00298 /*
00299  *  call-seq:
00300  *     stat <=> other_stat    -> -1, 0, 1, nil
00301  *
00302  *  Compares <code>File::Stat</code> objects by comparing their
00303  *  respective modification times.
00304  *
00305  *     f1 = File.new("f1", "w")
00306  *     sleep 1
00307  *     f2 = File.new("f2", "w")
00308  *     f1.stat <=> f2.stat   #=> -1
00309  */
00310 
00311 static VALUE
00312 rb_stat_cmp(VALUE self, VALUE other)
00313 {
00314     if (rb_obj_is_kind_of(other, rb_obj_class(self))) {
00315         struct timespec ts1 = stat_mtimespec(get_stat(self));
00316         struct timespec ts2 = stat_mtimespec(get_stat(other));
00317         if (ts1.tv_sec == ts2.tv_sec) {
00318             if (ts1.tv_nsec == ts2.tv_nsec) return INT2FIX(0);
00319             if (ts1.tv_nsec < ts2.tv_nsec) return INT2FIX(-1);
00320             return INT2FIX(1);
00321         }
00322         if (ts1.tv_sec < ts2.tv_sec) return INT2FIX(-1);
00323         return INT2FIX(1);
00324     }
00325     return Qnil;
00326 }
00327 
00328 #define ST2UINT(val) ((val) & ~(~1UL << (sizeof(val) * CHAR_BIT - 1)))
00329 
00330 #ifndef NUM2DEVT
00331 # define NUM2DEVT(v) NUM2UINT(v)
00332 #endif
00333 #ifndef DEVT2NUM
00334 # define DEVT2NUM(v) UINT2NUM(v)
00335 #endif
00336 #ifndef PRI_DEVT_PREFIX
00337 # define PRI_DEVT_PREFIX ""
00338 #endif
00339 
00340 /*
00341  *  call-seq:
00342  *     stat.dev    -> fixnum
00343  *
00344  *  Returns an integer representing the device on which <i>stat</i>
00345  *  resides.
00346  *
00347  *     File.stat("testfile").dev   #=> 774
00348  */
00349 
00350 static VALUE
00351 rb_stat_dev(VALUE self)
00352 {
00353     return DEVT2NUM(get_stat(self)->st_dev);
00354 }
00355 
00356 /*
00357  *  call-seq:
00358  *     stat.dev_major   -> fixnum
00359  *
00360  *  Returns the major part of <code>File_Stat#dev</code> or
00361  *  <code>nil</code>.
00362  *
00363  *     File.stat("/dev/fd1").dev_major   #=> 2
00364  *     File.stat("/dev/tty").dev_major   #=> 5
00365  */
00366 
00367 static VALUE
00368 rb_stat_dev_major(VALUE self)
00369 {
00370 #if defined(major)
00371     return INT2NUM(major(get_stat(self)->st_dev));
00372 #else
00373     return Qnil;
00374 #endif
00375 }
00376 
00377 /*
00378  *  call-seq:
00379  *     stat.dev_minor   -> fixnum
00380  *
00381  *  Returns the minor part of <code>File_Stat#dev</code> or
00382  *  <code>nil</code>.
00383  *
00384  *     File.stat("/dev/fd1").dev_minor   #=> 1
00385  *     File.stat("/dev/tty").dev_minor   #=> 0
00386  */
00387 
00388 static VALUE
00389 rb_stat_dev_minor(VALUE self)
00390 {
00391 #if defined(minor)
00392     return INT2NUM(minor(get_stat(self)->st_dev));
00393 #else
00394     return Qnil;
00395 #endif
00396 }
00397 
00398 /*
00399  *  call-seq:
00400  *     stat.ino   -> fixnum
00401  *
00402  *  Returns the inode number for <i>stat</i>.
00403  *
00404  *     File.stat("testfile").ino   #=> 1083669
00405  *
00406  */
00407 
00408 static VALUE
00409 rb_stat_ino(VALUE self)
00410 {
00411 #if SIZEOF_STRUCT_STAT_ST_INO > SIZEOF_LONG
00412     return ULL2NUM(get_stat(self)->st_ino);
00413 #else
00414     return ULONG2NUM(get_stat(self)->st_ino);
00415 #endif
00416 }
00417 
00418 /*
00419  *  call-seq:
00420  *     stat.mode   -> fixnum
00421  *
00422  *  Returns an integer representing the permission bits of
00423  *  <i>stat</i>. The meaning of the bits is platform dependent; on
00424  *  Unix systems, see <code>stat(2)</code>.
00425  *
00426  *     File.chmod(0644, "testfile")   #=> 1
00427  *     s = File.stat("testfile")
00428  *     sprintf("%o", s.mode)          #=> "100644"
00429  */
00430 
00431 static VALUE
00432 rb_stat_mode(VALUE self)
00433 {
00434     return UINT2NUM(ST2UINT(get_stat(self)->st_mode));
00435 }
00436 
00437 /*
00438  *  call-seq:
00439  *     stat.nlink   -> fixnum
00440  *
00441  *  Returns the number of hard links to <i>stat</i>.
00442  *
00443  *     File.stat("testfile").nlink             #=> 1
00444  *     File.link("testfile", "testfile.bak")   #=> 0
00445  *     File.stat("testfile").nlink             #=> 2
00446  *
00447  */
00448 
00449 static VALUE
00450 rb_stat_nlink(VALUE self)
00451 {
00452     return UINT2NUM(get_stat(self)->st_nlink);
00453 }
00454 
00455 /*
00456  *  call-seq:
00457  *     stat.uid    -> fixnum
00458  *
00459  *  Returns the numeric user id of the owner of <i>stat</i>.
00460  *
00461  *     File.stat("testfile").uid   #=> 501
00462  *
00463  */
00464 
00465 static VALUE
00466 rb_stat_uid(VALUE self)
00467 {
00468     return UIDT2NUM(get_stat(self)->st_uid);
00469 }
00470 
00471 /*
00472  *  call-seq:
00473  *     stat.gid   -> fixnum
00474  *
00475  *  Returns the numeric group id of the owner of <i>stat</i>.
00476  *
00477  *     File.stat("testfile").gid   #=> 500
00478  *
00479  */
00480 
00481 static VALUE
00482 rb_stat_gid(VALUE self)
00483 {
00484     return GIDT2NUM(get_stat(self)->st_gid);
00485 }
00486 
00487 /*
00488  *  call-seq:
00489  *     stat.rdev   ->  fixnum or nil
00490  *
00491  *  Returns an integer representing the device type on which
00492  *  <i>stat</i> resides. Returns <code>nil</code> if the operating
00493  *  system doesn't support this feature.
00494  *
00495  *     File.stat("/dev/fd1").rdev   #=> 513
00496  *     File.stat("/dev/tty").rdev   #=> 1280
00497  */
00498 
00499 static VALUE
00500 rb_stat_rdev(VALUE self)
00501 {
00502 #ifdef HAVE_ST_RDEV
00503     return DEVT2NUM(get_stat(self)->st_rdev);
00504 #else
00505     return Qnil;
00506 #endif
00507 }
00508 
00509 /*
00510  *  call-seq:
00511  *     stat.rdev_major   -> fixnum
00512  *
00513  *  Returns the major part of <code>File_Stat#rdev</code> or
00514  *  <code>nil</code>.
00515  *
00516  *     File.stat("/dev/fd1").rdev_major   #=> 2
00517  *     File.stat("/dev/tty").rdev_major   #=> 5
00518  */
00519 
00520 static VALUE
00521 rb_stat_rdev_major(VALUE self)
00522 {
00523 #if defined(HAVE_ST_RDEV) && defined(major)
00524     return DEVT2NUM(major(get_stat(self)->st_rdev));
00525 #else
00526     return Qnil;
00527 #endif
00528 }
00529 
00530 /*
00531  *  call-seq:
00532  *     stat.rdev_minor   -> fixnum
00533  *
00534  *  Returns the minor part of <code>File_Stat#rdev</code> or
00535  *  <code>nil</code>.
00536  *
00537  *     File.stat("/dev/fd1").rdev_minor   #=> 1
00538  *     File.stat("/dev/tty").rdev_minor   #=> 0
00539  */
00540 
00541 static VALUE
00542 rb_stat_rdev_minor(VALUE self)
00543 {
00544 #if defined(HAVE_ST_RDEV) && defined(minor)
00545     return DEVT2NUM(minor(get_stat(self)->st_rdev));
00546 #else
00547     return Qnil;
00548 #endif
00549 }
00550 
00551 /*
00552  *  call-seq:
00553  *     stat.size    -> fixnum
00554  *
00555  *  Returns the size of <i>stat</i> in bytes.
00556  *
00557  *     File.stat("testfile").size   #=> 66
00558  */
00559 
00560 static VALUE
00561 rb_stat_size(VALUE self)
00562 {
00563     return OFFT2NUM(get_stat(self)->st_size);
00564 }
00565 
00566 /*
00567  *  call-seq:
00568  *     stat.blksize   -> integer or nil
00569  *
00570  *  Returns the native file system's block size. Will return <code>nil</code>
00571  *  on platforms that don't support this information.
00572  *
00573  *     File.stat("testfile").blksize   #=> 4096
00574  *
00575  */
00576 
00577 static VALUE
00578 rb_stat_blksize(VALUE self)
00579 {
00580 #ifdef HAVE_ST_BLKSIZE
00581     return ULONG2NUM(get_stat(self)->st_blksize);
00582 #else
00583     return Qnil;
00584 #endif
00585 }
00586 
00587 /*
00588  *  call-seq:
00589  *     stat.blocks    -> integer or nil
00590  *
00591  *  Returns the number of native file system blocks allocated for this
00592  *  file, or <code>nil</code> if the operating system doesn't
00593  *  support this feature.
00594  *
00595  *     File.stat("testfile").blocks   #=> 2
00596  */
00597 
00598 static VALUE
00599 rb_stat_blocks(VALUE self)
00600 {
00601 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
00602 # if SIZEOF_STRUCT_STAT_ST_BLOCKS > SIZEOF_LONG
00603     return ULL2NUM(get_stat(self)->st_blocks);
00604 # else
00605     return ULONG2NUM(get_stat(self)->st_blocks);
00606 # endif
00607 #else
00608     return Qnil;
00609 #endif
00610 }
00611 
00612 static struct timespec
00613 stat_atimespec(struct stat *st)
00614 {
00615     struct timespec ts;
00616     ts.tv_sec = st->st_atime;
00617 #if defined(HAVE_STRUCT_STAT_ST_ATIM)
00618     ts.tv_nsec = st->st_atim.tv_nsec;
00619 #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
00620     ts.tv_nsec = st->st_atimespec.tv_nsec;
00621 #elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC)
00622     ts.tv_nsec = st->st_atimensec;
00623 #else
00624     ts.tv_nsec = 0;
00625 #endif
00626     return ts;
00627 }
00628 
00629 static VALUE
00630 stat_atime(struct stat *st)
00631 {
00632     struct timespec ts = stat_atimespec(st);
00633     return rb_time_nano_new(ts.tv_sec, ts.tv_nsec);
00634 }
00635 
00636 static struct timespec
00637 stat_mtimespec(struct stat *st)
00638 {
00639     struct timespec ts;
00640     ts.tv_sec = st->st_mtime;
00641 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
00642     ts.tv_nsec = st->st_mtim.tv_nsec;
00643 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
00644     ts.tv_nsec = st->st_mtimespec.tv_nsec;
00645 #elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
00646     ts.tv_nsec = st->st_mtimensec;
00647 #else
00648     ts.tv_nsec = 0;
00649 #endif
00650     return ts;
00651 }
00652 
00653 static VALUE
00654 stat_mtime(struct stat *st)
00655 {
00656     struct timespec ts = stat_mtimespec(st);
00657     return rb_time_nano_new(ts.tv_sec, ts.tv_nsec);
00658 }
00659 
00660 static struct timespec
00661 stat_ctimespec(struct stat *st)
00662 {
00663     struct timespec ts;
00664     ts.tv_sec = st->st_ctime;
00665 #if defined(HAVE_STRUCT_STAT_ST_CTIM)
00666     ts.tv_nsec = st->st_ctim.tv_nsec;
00667 #elif defined(HAVE_STRUCT_STAT_ST_CTIMESPEC)
00668     ts.tv_nsec = st->st_ctimespec.tv_nsec;
00669 #elif defined(HAVE_STRUCT_STAT_ST_CTIMENSEC)
00670     ts.tv_nsec = st->st_ctimensec;
00671 #else
00672     ts.tv_nsec = 0;
00673 #endif
00674     return ts;
00675 }
00676 
00677 static VALUE
00678 stat_ctime(struct stat *st)
00679 {
00680     struct timespec ts = stat_ctimespec(st);
00681     return rb_time_nano_new(ts.tv_sec, ts.tv_nsec);
00682 }
00683 
00684 /*
00685  *  call-seq:
00686  *     stat.atime   -> time
00687  *
00688  *  Returns the last access time for this file as an object of class
00689  *  <code>Time</code>.
00690  *
00691  *     File.stat("testfile").atime   #=> Wed Dec 31 18:00:00 CST 1969
00692  *
00693  */
00694 
00695 static VALUE
00696 rb_stat_atime(VALUE self)
00697 {
00698     return stat_atime(get_stat(self));
00699 }
00700 
00701 /*
00702  *  call-seq:
00703  *     stat.mtime  ->  aTime
00704  *
00705  *  Returns the modification time of <i>stat</i>.
00706  *
00707  *     File.stat("testfile").mtime   #=> Wed Apr 09 08:53:14 CDT 2003
00708  *
00709  */
00710 
00711 static VALUE
00712 rb_stat_mtime(VALUE self)
00713 {
00714     return stat_mtime(get_stat(self));
00715 }
00716 
00717 /*
00718  *  call-seq:
00719  *     stat.ctime  ->  aTime
00720  *
00721  *  Returns the change time for <i>stat</i> (that is, the time
00722  *  directory information about the file was changed, not the file
00723  *  itself).
00724  *
00725  *  Note that on Windows (NTFS), returns creation time (birth time).
00726  *
00727  *     File.stat("testfile").ctime   #=> Wed Apr 09 08:53:14 CDT 2003
00728  *
00729  */
00730 
00731 static VALUE
00732 rb_stat_ctime(VALUE self)
00733 {
00734     return stat_ctime(get_stat(self));
00735 }
00736 
00737 /*
00738  * call-seq:
00739  *   stat.inspect  ->  string
00740  *
00741  * Produce a nicely formatted description of <i>stat</i>.
00742  *
00743  *   File.stat("/etc/passwd").inspect
00744  *      #=> "#<File::Stat dev=0xe000005, ino=1078078, mode=0100644,
00745  *      #    nlink=1, uid=0, gid=0, rdev=0x0, size=1374, blksize=4096,
00746  *      #    blocks=8, atime=Wed Dec 10 10:16:12 CST 2003,
00747  *      #    mtime=Fri Sep 12 15:41:41 CDT 2003,
00748  *      #    ctime=Mon Oct 27 11:20:27 CST 2003>"
00749  */
00750 
00751 static VALUE
00752 rb_stat_inspect(VALUE self)
00753 {
00754     VALUE str;
00755     size_t i;
00756     static const struct {
00757         const char *name;
00758         VALUE (*func)(VALUE);
00759     } member[] = {
00760         {"dev",     rb_stat_dev},
00761         {"ino",     rb_stat_ino},
00762         {"mode",    rb_stat_mode},
00763         {"nlink",   rb_stat_nlink},
00764         {"uid",     rb_stat_uid},
00765         {"gid",     rb_stat_gid},
00766         {"rdev",    rb_stat_rdev},
00767         {"size",    rb_stat_size},
00768         {"blksize", rb_stat_blksize},
00769         {"blocks",  rb_stat_blocks},
00770         {"atime",   rb_stat_atime},
00771         {"mtime",   rb_stat_mtime},
00772         {"ctime",   rb_stat_ctime},
00773     };
00774 
00775     struct stat* st;
00776     TypedData_Get_Struct(self, struct stat, &stat_data_type, st);
00777     if (!st) {
00778         return rb_sprintf("#<%s: uninitialized>", rb_obj_classname(self));
00779     }
00780 
00781     str = rb_str_buf_new2("#<");
00782     rb_str_buf_cat2(str, rb_obj_classname(self));
00783     rb_str_buf_cat2(str, " ");
00784 
00785     for (i = 0; i < sizeof(member)/sizeof(member[0]); i++) {
00786         VALUE v;
00787 
00788         if (i > 0) {
00789             rb_str_buf_cat2(str, ", ");
00790         }
00791         rb_str_buf_cat2(str, member[i].name);
00792         rb_str_buf_cat2(str, "=");
00793         v = (*member[i].func)(self);
00794         if (i == 2) {           /* mode */
00795             rb_str_catf(str, "0%lo", (unsigned long)NUM2ULONG(v));
00796         }
00797         else if (i == 0 || i == 6) { /* dev/rdev */
00798             rb_str_catf(str, "0x%"PRI_DEVT_PREFIX"x", NUM2DEVT(v));
00799         }
00800         else {
00801             rb_str_append(str, rb_inspect(v));
00802         }
00803     }
00804     rb_str_buf_cat2(str, ">");
00805     OBJ_INFECT(str, self);
00806 
00807     return str;
00808 }
00809 
00810 static int
00811 rb_stat(VALUE file, struct stat *st)
00812 {
00813     VALUE tmp;
00814 
00815     rb_secure(2);
00816     tmp = rb_check_convert_type(file, T_FILE, "IO", "to_io");
00817     if (!NIL_P(tmp)) {
00818         rb_io_t *fptr;
00819 
00820         GetOpenFile(tmp, fptr);
00821         return fstat(fptr->fd, st);
00822     }
00823     FilePathValue(file);
00824     file = rb_str_encode_ospath(file);
00825     return STAT(StringValueCStr(file), st);
00826 }
00827 
00828 #ifdef _WIN32
00829 static HANDLE
00830 w32_io_info(VALUE *file, BY_HANDLE_FILE_INFORMATION *st)
00831 {
00832     VALUE tmp;
00833     HANDLE f, ret = 0;
00834 
00835     tmp = rb_check_convert_type(*file, T_FILE, "IO", "to_io");
00836     if (!NIL_P(tmp)) {
00837         rb_io_t *fptr;
00838 
00839         GetOpenFile(tmp, fptr);
00840         f = (HANDLE)rb_w32_get_osfhandle(fptr->fd);
00841         if (f == (HANDLE)-1) return INVALID_HANDLE_VALUE;
00842     }
00843     else {
00844         VALUE tmp;
00845         WCHAR *ptr;
00846         int len;
00847         VALUE v;
00848 
00849         FilePathValue(*file);
00850         tmp = rb_str_encode_ospath(*file);
00851         len = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, NULL, 0);
00852         ptr = ALLOCV_N(WCHAR, v, len);
00853         MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, ptr, len);
00854         f = CreateFileW(ptr, 0,
00855                         FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
00856                         rb_w32_iswin95() ? 0 : FILE_FLAG_BACKUP_SEMANTICS,
00857                         NULL);
00858         ALLOCV_END(v);
00859         if (f == INVALID_HANDLE_VALUE) return f;
00860         ret = f;
00861     }
00862     if (GetFileType(f) == FILE_TYPE_DISK) {
00863         ZeroMemory(st, sizeof(*st));
00864         if (GetFileInformationByHandle(f, st)) return ret;
00865     }
00866     if (ret) CloseHandle(ret);
00867     return INVALID_HANDLE_VALUE;
00868 }
00869 #endif
00870 
00871 /*
00872  *  call-seq:
00873  *     File.stat(file_name)   ->  stat
00874  *
00875  *  Returns a <code>File::Stat</code> object for the named file (see
00876  *  <code>File::Stat</code>).
00877  *
00878  *     File.stat("testfile").mtime   #=> Tue Apr 08 12:58:04 CDT 2003
00879  *
00880  */
00881 
00882 static VALUE
00883 rb_file_s_stat(VALUE klass, VALUE fname)
00884 {
00885     struct stat st;
00886 
00887     rb_secure(4);
00888     FilePathValue(fname);
00889     if (rb_stat(fname, &st) < 0) {
00890         rb_sys_fail_path(fname);
00891     }
00892     return stat_new(&st);
00893 }
00894 
00895 /*
00896  *  call-seq:
00897  *     ios.stat    -> stat
00898  *
00899  *  Returns status information for <em>ios</em> as an object of type
00900  *  <code>File::Stat</code>.
00901  *
00902  *     f = File.new("testfile")
00903  *     s = f.stat
00904  *     "%o" % s.mode   #=> "100644"
00905  *     s.blksize       #=> 4096
00906  *     s.atime         #=> Wed Apr 09 08:53:54 CDT 2003
00907  *
00908  */
00909 
00910 static VALUE
00911 rb_io_stat(VALUE obj)
00912 {
00913     rb_io_t *fptr;
00914     struct stat st;
00915 
00916     GetOpenFile(obj, fptr);
00917     if (fstat(fptr->fd, &st) == -1) {
00918         rb_sys_fail_path(fptr->pathv);
00919     }
00920     return stat_new(&st);
00921 }
00922 
00923 /*
00924  *  call-seq:
00925  *     File.lstat(file_name)   -> stat
00926  *
00927  *  Same as <code>File::stat</code>, but does not follow the last symbolic
00928  *  link. Instead, reports on the link itself.
00929  *
00930  *     File.symlink("testfile", "link2test")   #=> 0
00931  *     File.stat("testfile").size              #=> 66
00932  *     File.lstat("link2test").size            #=> 8
00933  *     File.stat("link2test").size             #=> 66
00934  *
00935  */
00936 
00937 static VALUE
00938 rb_file_s_lstat(VALUE klass, VALUE fname)
00939 {
00940 #ifdef HAVE_LSTAT
00941     struct stat st;
00942 
00943     rb_secure(2);
00944     FilePathValue(fname);
00945     fname = rb_str_encode_ospath(fname);
00946     if (lstat(StringValueCStr(fname), &st) == -1) {
00947         rb_sys_fail_path(fname);
00948     }
00949     return stat_new(&st);
00950 #else
00951     return rb_file_s_stat(klass, fname);
00952 #endif
00953 }
00954 
00955 /*
00956  *  call-seq:
00957  *     file.lstat   ->  stat
00958  *
00959  *  Same as <code>IO#stat</code>, but does not follow the last symbolic
00960  *  link. Instead, reports on the link itself.
00961  *
00962  *     File.symlink("testfile", "link2test")   #=> 0
00963  *     File.stat("testfile").size              #=> 66
00964  *     f = File.new("link2test")
00965  *     f.lstat.size                            #=> 8
00966  *     f.stat.size                             #=> 66
00967  */
00968 
00969 static VALUE
00970 rb_file_lstat(VALUE obj)
00971 {
00972 #ifdef HAVE_LSTAT
00973     rb_io_t *fptr;
00974     struct stat st;
00975     VALUE path;
00976 
00977     rb_secure(2);
00978     GetOpenFile(obj, fptr);
00979     if (NIL_P(fptr->pathv)) return Qnil;
00980     path = rb_str_encode_ospath(fptr->pathv);
00981     if (lstat(RSTRING_PTR(path), &st) == -1) {
00982         rb_sys_fail_path(fptr->pathv);
00983     }
00984     return stat_new(&st);
00985 #else
00986     return rb_io_stat(obj);
00987 #endif
00988 }
00989 
00990 static int
00991 rb_group_member(GETGROUPS_T gid)
00992 {
00993     int rv = FALSE;
00994 #ifndef _WIN32
00995     if (getgid() == gid || getegid() == gid)
00996         return TRUE;
00997 
00998 # ifdef HAVE_GETGROUPS
00999 #  ifndef NGROUPS
01000 #   ifdef NGROUPS_MAX
01001 #    define NGROUPS NGROUPS_MAX
01002 #   else
01003 #    define NGROUPS 32
01004 #   endif
01005 #  endif
01006     {
01007         GETGROUPS_T *gary;
01008         int anum;
01009 
01010         gary = xmalloc(NGROUPS * sizeof(GETGROUPS_T));
01011         anum = getgroups(NGROUPS, gary);
01012         while (--anum >= 0) {
01013             if (gary[anum] == gid) {
01014                 rv = TRUE;
01015                 break;
01016             }
01017         }
01018         xfree(gary);
01019     }
01020 # endif
01021 #endif
01022     return rv;
01023 }
01024 
01025 #ifndef S_IXUGO
01026 #  define S_IXUGO               (S_IXUSR | S_IXGRP | S_IXOTH)
01027 #endif
01028 
01029 #if defined(S_IXGRP) && !defined(_WIN32) && !defined(__CYGWIN__)
01030 #define USE_GETEUID 1
01031 #endif
01032 
01033 #ifndef HAVE_EACCESS
01034 int
01035 eaccess(const char *path, int mode)
01036 {
01037 #ifdef USE_GETEUID
01038     struct stat st;
01039     rb_uid_t euid;
01040 
01041     if (STAT(path, &st) < 0)
01042         return -1;
01043 
01044     euid = geteuid();
01045 
01046     if (euid == 0) {
01047         /* Root can read or write any file. */
01048         if (!(mode & X_OK))
01049             return 0;
01050 
01051         /* Root can execute any file that has any one of the execute
01052            bits set. */
01053         if (st.st_mode & S_IXUGO)
01054             return 0;
01055 
01056         return -1;
01057     }
01058 
01059     if (st.st_uid == euid)        /* owner */
01060         mode <<= 6;
01061     else if (rb_group_member(st.st_gid))
01062         mode <<= 3;
01063 
01064     if ((int)(st.st_mode & mode) == mode) return 0;
01065 
01066     return -1;
01067 #else
01068     return access(path, mode);
01069 #endif
01070 }
01071 #endif
01072 
01073 static inline int
01074 access_internal(const char *path, int mode)
01075 {
01076     return access(path, mode);
01077 }
01078 
01079 
01080 /*
01081  * Document-class: FileTest
01082  *
01083  *  <code>FileTest</code> implements file test operations similar to
01084  *  those used in <code>File::Stat</code>. It exists as a standalone
01085  *  module, and its methods are also insinuated into the <code>File</code>
01086  *  class. (Note that this is not done by inclusion: the interpreter cheats).
01087  *
01088  */
01089 
01090 /*
01091  * Document-method: exist?
01092  *
01093  * call-seq:
01094  *   Dir.exist?(file_name)   ->  true or false
01095  *   Dir.exists?(file_name)   ->  true or false
01096  *
01097  * Returns <code>true</code> if the named file is a directory,
01098  * <code>false</code> otherwise.
01099  *
01100  */
01101 
01102 /*
01103  * Document-method: directory?
01104  *
01105  * call-seq:
01106  *   File.directory?(file_name)   ->  true or false
01107  *
01108  * Returns <code>true</code> if the named file is a directory,
01109  * or a symlink that points at a directory, and <code>false</code>
01110  * otherwise.
01111  *
01112  *    File.directory?(".")
01113  */
01114 
01115 VALUE
01116 rb_file_directory_p(VALUE obj, VALUE fname)
01117 {
01118 #ifndef S_ISDIR
01119 #   define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
01120 #endif
01121 
01122     struct stat st;
01123 
01124     if (rb_stat(fname, &st) < 0) return Qfalse;
01125     if (S_ISDIR(st.st_mode)) return Qtrue;
01126     return Qfalse;
01127 }
01128 
01129 /*
01130  * call-seq:
01131  *   File.pipe?(file_name)   ->  true or false
01132  *
01133  * Returns <code>true</code> if the named file is a pipe.
01134  */
01135 
01136 static VALUE
01137 rb_file_pipe_p(VALUE obj, VALUE fname)
01138 {
01139 #ifdef S_IFIFO
01140 #  ifndef S_ISFIFO
01141 #    define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
01142 #  endif
01143 
01144     struct stat st;
01145 
01146     if (rb_stat(fname, &st) < 0) return Qfalse;
01147     if (S_ISFIFO(st.st_mode)) return Qtrue;
01148 
01149 #endif
01150     return Qfalse;
01151 }
01152 
01153 /*
01154  * call-seq:
01155  *   File.symlink?(file_name)   ->  true or false
01156  *
01157  * Returns <code>true</code> if the named file is a symbolic link.
01158  */
01159 
01160 static VALUE
01161 rb_file_symlink_p(VALUE obj, VALUE fname)
01162 {
01163 #ifndef S_ISLNK
01164 #  ifdef _S_ISLNK
01165 #    define S_ISLNK(m) _S_ISLNK(m)
01166 #  else
01167 #    ifdef _S_IFLNK
01168 #      define S_ISLNK(m) (((m) & S_IFMT) == _S_IFLNK)
01169 #    else
01170 #      ifdef S_IFLNK
01171 #        define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
01172 #      endif
01173 #    endif
01174 #  endif
01175 #endif
01176 
01177 #ifdef S_ISLNK
01178     struct stat st;
01179 
01180     rb_secure(2);
01181     FilePathValue(fname);
01182     fname = rb_str_encode_ospath(fname);
01183     if (lstat(StringValueCStr(fname), &st) < 0) return Qfalse;
01184     if (S_ISLNK(st.st_mode)) return Qtrue;
01185 #endif
01186 
01187     return Qfalse;
01188 }
01189 
01190 /*
01191  * call-seq:
01192  *   File.socket?(file_name)   ->  true or false
01193  *
01194  * Returns <code>true</code> if the named file is a socket.
01195  */
01196 
01197 static VALUE
01198 rb_file_socket_p(VALUE obj, VALUE fname)
01199 {
01200 #ifndef S_ISSOCK
01201 #  ifdef _S_ISSOCK
01202 #    define S_ISSOCK(m) _S_ISSOCK(m)
01203 #  else
01204 #    ifdef _S_IFSOCK
01205 #      define S_ISSOCK(m) (((m) & S_IFMT) == _S_IFSOCK)
01206 #    else
01207 #      ifdef S_IFSOCK
01208 #        define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
01209 #      endif
01210 #    endif
01211 #  endif
01212 #endif
01213 
01214 #ifdef S_ISSOCK
01215     struct stat st;
01216 
01217     if (rb_stat(fname, &st) < 0) return Qfalse;
01218     if (S_ISSOCK(st.st_mode)) return Qtrue;
01219 
01220 #endif
01221     return Qfalse;
01222 }
01223 
01224 /*
01225  * call-seq:
01226  *   File.blockdev?(file_name)   ->  true or false
01227  *
01228  * Returns <code>true</code> if the named file is a block device.
01229  */
01230 
01231 static VALUE
01232 rb_file_blockdev_p(VALUE obj, VALUE fname)
01233 {
01234 #ifndef S_ISBLK
01235 #   ifdef S_IFBLK
01236 #       define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
01237 #   else
01238 #       define S_ISBLK(m) (0)  /* anytime false */
01239 #   endif
01240 #endif
01241 
01242 #ifdef S_ISBLK
01243     struct stat st;
01244 
01245     if (rb_stat(fname, &st) < 0) return Qfalse;
01246     if (S_ISBLK(st.st_mode)) return Qtrue;
01247 
01248 #endif
01249     return Qfalse;
01250 }
01251 
01252 /*
01253  * call-seq:
01254  *   File.chardev?(file_name)   ->  true or false
01255  *
01256  * Returns <code>true</code> if the named file is a character device.
01257  */
01258 static VALUE
01259 rb_file_chardev_p(VALUE obj, VALUE fname)
01260 {
01261 #ifndef S_ISCHR
01262 #   define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
01263 #endif
01264 
01265     struct stat st;
01266 
01267     if (rb_stat(fname, &st) < 0) return Qfalse;
01268     if (S_ISCHR(st.st_mode)) return Qtrue;
01269 
01270     return Qfalse;
01271 }
01272 
01273 /*
01274  * call-seq:
01275  *    File.exist?(file_name)    ->  true or false
01276  *    File.exists?(file_name)   ->  true or false
01277  *
01278  * Return <code>true</code> if the named file exists.
01279  */
01280 
01281 static VALUE
01282 rb_file_exist_p(VALUE obj, VALUE fname)
01283 {
01284     struct stat st;
01285 
01286     if (rb_stat(fname, &st) < 0) return Qfalse;
01287     return Qtrue;
01288 }
01289 
01290 /*
01291  * call-seq:
01292  *    File.readable?(file_name)   -> true or false
01293  *
01294  * Returns <code>true</code> if the named file is readable by the effective
01295  * user id of this process.
01296  */
01297 
01298 static VALUE
01299 rb_file_readable_p(VALUE obj, VALUE fname)
01300 {
01301     rb_secure(2);
01302     FilePathValue(fname);
01303     fname = rb_str_encode_ospath(fname);
01304     if (eaccess(StringValueCStr(fname), R_OK) < 0) return Qfalse;
01305     return Qtrue;
01306 }
01307 
01308 /*
01309  * call-seq:
01310  *    File.readable_real?(file_name)   -> true or false
01311  *
01312  * Returns <code>true</code> if the named file is readable by the real
01313  * user id of this process.
01314  */
01315 
01316 static VALUE
01317 rb_file_readable_real_p(VALUE obj, VALUE fname)
01318 {
01319     rb_secure(2);
01320     FilePathValue(fname);
01321     fname = rb_str_encode_ospath(fname);
01322     if (access_internal(StringValueCStr(fname), R_OK) < 0) return Qfalse;
01323     return Qtrue;
01324 }
01325 
01326 #ifndef S_IRUGO
01327 #  define S_IRUGO               (S_IRUSR | S_IRGRP | S_IROTH)
01328 #endif
01329 
01330 #ifndef S_IWUGO
01331 #  define S_IWUGO               (S_IWUSR | S_IWGRP | S_IWOTH)
01332 #endif
01333 
01334 /*
01335  * call-seq:
01336  *    File.world_readable?(file_name)   -> fixnum or nil
01337  *
01338  * If <i>file_name</i> is readable by others, returns an integer
01339  * representing the file permission bits of <i>file_name</i>. Returns
01340  * <code>nil</code> otherwise. The meaning of the bits is platform
01341  * dependent; on Unix systems, see <code>stat(2)</code>.
01342  *
01343  *    File.world_readable?("/etc/passwd")           #=> 420
01344  *    m = File.world_readable?("/etc/passwd")
01345  *    sprintf("%o", m)                              #=> "644"
01346  */
01347 
01348 static VALUE
01349 rb_file_world_readable_p(VALUE obj, VALUE fname)
01350 {
01351 #ifdef S_IROTH
01352     struct stat st;
01353 
01354     if (rb_stat(fname, &st) < 0) return Qnil;
01355     if ((st.st_mode & (S_IROTH)) == S_IROTH) {
01356         return UINT2NUM(st.st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
01357     }
01358 #endif
01359     return Qnil;
01360 }
01361 
01362 /*
01363  * call-seq:
01364  *    File.writable?(file_name)   -> true or false
01365  *
01366  * Returns <code>true</code> if the named file is writable by the effective
01367  * user id of this process.
01368  */
01369 
01370 static VALUE
01371 rb_file_writable_p(VALUE obj, VALUE fname)
01372 {
01373     rb_secure(2);
01374     FilePathValue(fname);
01375     fname = rb_str_encode_ospath(fname);
01376     if (eaccess(StringValueCStr(fname), W_OK) < 0) return Qfalse;
01377     return Qtrue;
01378 }
01379 
01380 /*
01381  * call-seq:
01382  *    File.writable_real?(file_name)   -> true or false
01383  *
01384  * Returns <code>true</code> if the named file is writable by the real
01385  * user id of this process.
01386  */
01387 
01388 static VALUE
01389 rb_file_writable_real_p(VALUE obj, VALUE fname)
01390 {
01391     rb_secure(2);
01392     FilePathValue(fname);
01393     fname = rb_str_encode_ospath(fname);
01394     if (access_internal(StringValueCStr(fname), W_OK) < 0) return Qfalse;
01395     return Qtrue;
01396 }
01397 
01398 /*
01399  * call-seq:
01400  *    File.world_writable?(file_name)   -> fixnum or nil
01401  *
01402  * If <i>file_name</i> is writable by others, returns an integer
01403  * representing the file permission bits of <i>file_name</i>. Returns
01404  * <code>nil</code> otherwise. The meaning of the bits is platform
01405  * dependent; on Unix systems, see <code>stat(2)</code>.
01406  *
01407  *    File.world_writable?("/tmp")                  #=> 511
01408  *    m = File.world_writable?("/tmp")
01409  *    sprintf("%o", m)                              #=> "777"
01410  */
01411 
01412 static VALUE
01413 rb_file_world_writable_p(VALUE obj, VALUE fname)
01414 {
01415 #ifdef S_IWOTH
01416     struct stat st;
01417 
01418     if (rb_stat(fname, &st) < 0) return Qnil;
01419     if ((st.st_mode & (S_IWOTH)) == S_IWOTH) {
01420         return UINT2NUM(st.st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
01421     }
01422 #endif
01423     return Qnil;
01424 }
01425 
01426 /*
01427  * call-seq:
01428  *    File.executable?(file_name)   -> true or false
01429  *
01430  * Returns <code>true</code> if the named file is executable by the effective
01431  * user id of this process.
01432  */
01433 
01434 static VALUE
01435 rb_file_executable_p(VALUE obj, VALUE fname)
01436 {
01437     rb_secure(2);
01438     FilePathValue(fname);
01439     fname = rb_str_encode_ospath(fname);
01440     if (eaccess(StringValueCStr(fname), X_OK) < 0) return Qfalse;
01441     return Qtrue;
01442 }
01443 
01444 /*
01445  * call-seq:
01446  *    File.executable_real?(file_name)   -> true or false
01447  *
01448  * Returns <code>true</code> if the named file is executable by the real
01449  * user id of this process.
01450  */
01451 
01452 static VALUE
01453 rb_file_executable_real_p(VALUE obj, VALUE fname)
01454 {
01455     rb_secure(2);
01456     FilePathValue(fname);
01457     fname = rb_str_encode_ospath(fname);
01458     if (access_internal(StringValueCStr(fname), X_OK) < 0) return Qfalse;
01459     return Qtrue;
01460 }
01461 
01462 #ifndef S_ISREG
01463 #   define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
01464 #endif
01465 
01466 /*
01467  * call-seq:
01468  *    File.file?(file_name)   -> true or false
01469  *
01470  * Returns <code>true</code> if the named file exists and is a
01471  * regular file.
01472  */
01473 
01474 static VALUE
01475 rb_file_file_p(VALUE obj, VALUE fname)
01476 {
01477     struct stat st;
01478 
01479     if (rb_stat(fname, &st) < 0) return Qfalse;
01480     if (S_ISREG(st.st_mode)) return Qtrue;
01481     return Qfalse;
01482 }
01483 
01484 /*
01485  * call-seq:
01486  *    File.zero?(file_name)   -> true or false
01487  *
01488  * Returns <code>true</code> if the named file exists and has
01489  * a zero size.
01490  */
01491 
01492 static VALUE
01493 rb_file_zero_p(VALUE obj, VALUE fname)
01494 {
01495     struct stat st;
01496 
01497     if (rb_stat(fname, &st) < 0) return Qfalse;
01498     if (st.st_size == 0) return Qtrue;
01499     return Qfalse;
01500 }
01501 
01502 /*
01503  * call-seq:
01504  *    File.size?(file_name)   -> Integer or nil
01505  *
01506  * Returns +nil+ if +file_name+ doesn't exist or has zero size, the size of the
01507  * file otherwise.
01508  */
01509 
01510 static VALUE
01511 rb_file_size_p(VALUE obj, VALUE fname)
01512 {
01513     struct stat st;
01514 
01515     if (rb_stat(fname, &st) < 0) return Qnil;
01516     if (st.st_size == 0) return Qnil;
01517     return OFFT2NUM(st.st_size);
01518 }
01519 
01520 /*
01521  * call-seq:
01522  *    File.owned?(file_name)   -> true or false
01523  *
01524  * Returns <code>true</code> if the named file exists and the
01525  * effective used id of the calling process is the owner of
01526  * the file.
01527  */
01528 
01529 static VALUE
01530 rb_file_owned_p(VALUE obj, VALUE fname)
01531 {
01532     struct stat st;
01533 
01534     if (rb_stat(fname, &st) < 0) return Qfalse;
01535     if (st.st_uid == geteuid()) return Qtrue;
01536     return Qfalse;
01537 }
01538 
01539 static VALUE
01540 rb_file_rowned_p(VALUE obj, VALUE fname)
01541 {
01542     struct stat st;
01543 
01544     if (rb_stat(fname, &st) < 0) return Qfalse;
01545     if (st.st_uid == getuid()) return Qtrue;
01546     return Qfalse;
01547 }
01548 
01549 /*
01550  * call-seq:
01551  *    File.grpowned?(file_name)   -> true or false
01552  *
01553  * Returns <code>true</code> if the named file exists and the
01554  * effective group id of the calling process is the owner of
01555  * the file. Returns <code>false</code> on Windows.
01556  */
01557 
01558 static VALUE
01559 rb_file_grpowned_p(VALUE obj, VALUE fname)
01560 {
01561 #ifndef _WIN32
01562     struct stat st;
01563 
01564     if (rb_stat(fname, &st) < 0) return Qfalse;
01565     if (rb_group_member(st.st_gid)) return Qtrue;
01566 #endif
01567     return Qfalse;
01568 }
01569 
01570 #if defined(S_ISUID) || defined(S_ISGID) || defined(S_ISVTX)
01571 static VALUE
01572 check3rdbyte(VALUE fname, int mode)
01573 {
01574     struct stat st;
01575 
01576     rb_secure(2);
01577     FilePathValue(fname);
01578     fname = rb_str_encode_ospath(fname);
01579     if (STAT(StringValueCStr(fname), &st) < 0) return Qfalse;
01580     if (st.st_mode & mode) return Qtrue;
01581     return Qfalse;
01582 }
01583 #endif
01584 
01585 /*
01586  * call-seq:
01587  *   File.setuid?(file_name)   ->  true or false
01588  *
01589  * Returns <code>true</code> if the named file has the setuid bit set.
01590  */
01591 
01592 static VALUE
01593 rb_file_suid_p(VALUE obj, VALUE fname)
01594 {
01595 #ifdef S_ISUID
01596     return check3rdbyte(fname, S_ISUID);
01597 #else
01598     return Qfalse;
01599 #endif
01600 }
01601 
01602 /*
01603  * call-seq:
01604  *   File.setgid?(file_name)   ->  true or false
01605  *
01606  * Returns <code>true</code> if the named file has the setgid bit set.
01607  */
01608 
01609 static VALUE
01610 rb_file_sgid_p(VALUE obj, VALUE fname)
01611 {
01612 #ifdef S_ISGID
01613     return check3rdbyte(fname, S_ISGID);
01614 #else
01615     return Qfalse;
01616 #endif
01617 }
01618 
01619 /*
01620  * call-seq:
01621  *   File.sticky?(file_name)   ->  true or false
01622  *
01623  * Returns <code>true</code> if the named file has the sticky bit set.
01624  */
01625 
01626 static VALUE
01627 rb_file_sticky_p(VALUE obj, VALUE fname)
01628 {
01629 #ifdef S_ISVTX
01630     return check3rdbyte(fname, S_ISVTX);
01631 #else
01632     return Qnil;
01633 #endif
01634 }
01635 
01636 /*
01637  * call-seq:
01638  *   File.identical?(file_1, file_2)   ->  true or false
01639  *
01640  * Returns <code>true</code> if the named files are identical.
01641  *
01642  *     open("a", "w") {}
01643  *     p File.identical?("a", "a")      #=> true
01644  *     p File.identical?("a", "./a")    #=> true
01645  *     File.link("a", "b")
01646  *     p File.identical?("a", "b")      #=> true
01647  *     File.symlink("a", "c")
01648  *     p File.identical?("a", "c")      #=> true
01649  *     open("d", "w") {}
01650  *     p File.identical?("a", "d")      #=> false
01651  */
01652 
01653 static VALUE
01654 rb_file_identical_p(VALUE obj, VALUE fname1, VALUE fname2)
01655 {
01656 #ifndef DOSISH
01657     struct stat st1, st2;
01658 
01659     if (rb_stat(fname1, &st1) < 0) return Qfalse;
01660     if (rb_stat(fname2, &st2) < 0) return Qfalse;
01661     if (st1.st_dev != st2.st_dev) return Qfalse;
01662     if (st1.st_ino != st2.st_ino) return Qfalse;
01663 #else
01664 # ifdef _WIN32
01665     BY_HANDLE_FILE_INFORMATION st1, st2;
01666     HANDLE f1 = 0, f2 = 0;
01667 # endif
01668 
01669     rb_secure(2);
01670 # ifdef _WIN32
01671     f1 = w32_io_info(&fname1, &st1);
01672     if (f1 == INVALID_HANDLE_VALUE) return Qfalse;
01673     f2 = w32_io_info(&fname2, &st2);
01674     if (f1) CloseHandle(f1);
01675     if (f2 == INVALID_HANDLE_VALUE) return Qfalse;
01676     if (f2) CloseHandle(f2);
01677 
01678     if (st1.dwVolumeSerialNumber == st2.dwVolumeSerialNumber &&
01679         st1.nFileIndexHigh == st2.nFileIndexHigh &&
01680         st1.nFileIndexLow == st2.nFileIndexLow)
01681         return Qtrue;
01682     if (!f1 || !f2) return Qfalse;
01683     if (rb_w32_iswin95()) return Qfalse;
01684 # else
01685     FilePathValue(fname1);
01686     fname1 = rb_str_new4(fname1);
01687     fname1 = rb_str_encode_ospath(fname1);
01688     FilePathValue(fname2);
01689     fname2 = rb_str_encode_ospath(fname2);
01690     if (access(RSTRING_PTR(fname1), 0)) return Qfalse;
01691     if (access(RSTRING_PTR(fname2), 0)) return Qfalse;
01692 # endif
01693     fname1 = rb_file_expand_path(fname1, Qnil);
01694     fname2 = rb_file_expand_path(fname2, Qnil);
01695     if (RSTRING_LEN(fname1) != RSTRING_LEN(fname2)) return Qfalse;
01696     if (rb_memcicmp(RSTRING_PTR(fname1), RSTRING_PTR(fname2), RSTRING_LEN(fname1)))
01697         return Qfalse;
01698 #endif
01699     return Qtrue;
01700 }
01701 
01702 /*
01703  * call-seq:
01704  *    File.size(file_name)   -> integer
01705  *
01706  * Returns the size of <code>file_name</code>.
01707  */
01708 
01709 static VALUE
01710 rb_file_s_size(VALUE klass, VALUE fname)
01711 {
01712     struct stat st;
01713 
01714     if (rb_stat(fname, &st) < 0) {
01715         FilePathValue(fname);
01716         rb_sys_fail_path(fname);
01717     }
01718     return OFFT2NUM(st.st_size);
01719 }
01720 
01721 static VALUE
01722 rb_file_ftype(const struct stat *st)
01723 {
01724     const char *t;
01725 
01726     if (S_ISREG(st->st_mode)) {
01727         t = "file";
01728     }
01729     else if (S_ISDIR(st->st_mode)) {
01730         t = "directory";
01731     }
01732     else if (S_ISCHR(st->st_mode)) {
01733         t = "characterSpecial";
01734     }
01735 #ifdef S_ISBLK
01736     else if (S_ISBLK(st->st_mode)) {
01737         t = "blockSpecial";
01738     }
01739 #endif
01740 #ifdef S_ISFIFO
01741     else if (S_ISFIFO(st->st_mode)) {
01742         t = "fifo";
01743     }
01744 #endif
01745 #ifdef S_ISLNK
01746     else if (S_ISLNK(st->st_mode)) {
01747         t = "link";
01748     }
01749 #endif
01750 #ifdef S_ISSOCK
01751     else if (S_ISSOCK(st->st_mode)) {
01752         t = "socket";
01753     }
01754 #endif
01755     else {
01756         t = "unknown";
01757     }
01758 
01759     return rb_usascii_str_new2(t);
01760 }
01761 
01762 /*
01763  *  call-seq:
01764  *     File.ftype(file_name)   -> string
01765  *
01766  *  Identifies the type of the named file; the return string is one of
01767  *  ``<code>file</code>'', ``<code>directory</code>'',
01768  *  ``<code>characterSpecial</code>'', ``<code>blockSpecial</code>'',
01769  *  ``<code>fifo</code>'', ``<code>link</code>'',
01770  *  ``<code>socket</code>'', or ``<code>unknown</code>''.
01771  *
01772  *     File.ftype("testfile")            #=> "file"
01773  *     File.ftype("/dev/tty")            #=> "characterSpecial"
01774  *     File.ftype("/tmp/.X11-unix/X0")   #=> "socket"
01775  */
01776 
01777 static VALUE
01778 rb_file_s_ftype(VALUE klass, VALUE fname)
01779 {
01780     struct stat st;
01781 
01782     rb_secure(2);
01783     FilePathValue(fname);
01784     fname = rb_str_encode_ospath(fname);
01785     if (lstat(StringValueCStr(fname), &st) == -1) {
01786         rb_sys_fail_path(fname);
01787     }
01788 
01789     return rb_file_ftype(&st);
01790 }
01791 
01792 /*
01793  *  call-seq:
01794  *     File.atime(file_name)  ->  time
01795  *
01796  *  Returns the last access time for the named file as a Time object).
01797  *
01798  *     File.atime("testfile")   #=> Wed Apr 09 08:51:48 CDT 2003
01799  *
01800  */
01801 
01802 static VALUE
01803 rb_file_s_atime(VALUE klass, VALUE fname)
01804 {
01805     struct stat st;
01806 
01807     if (rb_stat(fname, &st) < 0) {
01808         FilePathValue(fname);
01809         rb_sys_fail_path(fname);
01810     }
01811     return stat_atime(&st);
01812 }
01813 
01814 /*
01815  *  call-seq:
01816  *     file.atime    -> time
01817  *
01818  *  Returns the last access time (a <code>Time</code> object)
01819  *   for <i>file</i>, or epoch if <i>file</i> has not been accessed.
01820  *
01821  *     File.new("testfile").atime   #=> Wed Dec 31 18:00:00 CST 1969
01822  *
01823  */
01824 
01825 static VALUE
01826 rb_file_atime(VALUE obj)
01827 {
01828     rb_io_t *fptr;
01829     struct stat st;
01830 
01831     GetOpenFile(obj, fptr);
01832     if (fstat(fptr->fd, &st) == -1) {
01833         rb_sys_fail_path(fptr->pathv);
01834     }
01835     return stat_atime(&st);
01836 }
01837 
01838 /*
01839  *  call-seq:
01840  *     File.mtime(file_name)  ->  time
01841  *
01842  *  Returns the modification time for the named file as a Time object.
01843  *
01844  *     File.mtime("testfile")   #=> Tue Apr 08 12:58:04 CDT 2003
01845  *
01846  */
01847 
01848 static VALUE
01849 rb_file_s_mtime(VALUE klass, VALUE fname)
01850 {
01851     struct stat st;
01852 
01853     if (rb_stat(fname, &st) < 0) {
01854         FilePathValue(fname);
01855         rb_sys_fail_path(fname);
01856     }
01857     return stat_mtime(&st);
01858 }
01859 
01860 /*
01861  *  call-seq:
01862  *     file.mtime  ->  time
01863  *
01864  *  Returns the modification time for <i>file</i>.
01865  *
01866  *     File.new("testfile").mtime   #=> Wed Apr 09 08:53:14 CDT 2003
01867  *
01868  */
01869 
01870 static VALUE
01871 rb_file_mtime(VALUE obj)
01872 {
01873     rb_io_t *fptr;
01874     struct stat st;
01875 
01876     GetOpenFile(obj, fptr);
01877     if (fstat(fptr->fd, &st) == -1) {
01878         rb_sys_fail_path(fptr->pathv);
01879     }
01880     return stat_mtime(&st);
01881 }
01882 
01883 /*
01884  *  call-seq:
01885  *     File.ctime(file_name)  -> time
01886  *
01887  *  Returns the change time for the named file (the time at which
01888  *  directory information about the file was changed, not the file
01889  *  itself).
01890  *
01891  *  Note that on Windows (NTFS), returns creation time (birth time).
01892  *
01893  *     File.ctime("testfile")   #=> Wed Apr 09 08:53:13 CDT 2003
01894  *
01895  */
01896 
01897 static VALUE
01898 rb_file_s_ctime(VALUE klass, VALUE fname)
01899 {
01900     struct stat st;
01901 
01902     if (rb_stat(fname, &st) < 0) {
01903         FilePathValue(fname);
01904         rb_sys_fail_path(fname);
01905     }
01906     return stat_ctime(&st);
01907 }
01908 
01909 /*
01910  *  call-seq:
01911  *     file.ctime  ->  time
01912  *
01913  *  Returns the change time for <i>file</i> (that is, the time directory
01914  *  information about the file was changed, not the file itself).
01915  *
01916  *  Note that on Windows (NTFS), returns creation time (birth time).
01917  *
01918  *     File.new("testfile").ctime   #=> Wed Apr 09 08:53:14 CDT 2003
01919  *
01920  */
01921 
01922 static VALUE
01923 rb_file_ctime(VALUE obj)
01924 {
01925     rb_io_t *fptr;
01926     struct stat st;
01927 
01928     GetOpenFile(obj, fptr);
01929     if (fstat(fptr->fd, &st) == -1) {
01930         rb_sys_fail_path(fptr->pathv);
01931     }
01932     return stat_ctime(&st);
01933 }
01934 
01935 /*
01936  *  call-seq:
01937  *     file.size    -> integer
01938  *
01939  *  Returns the size of <i>file</i> in bytes.
01940  *
01941  *     File.new("testfile").size   #=> 66
01942  *
01943  */
01944 
01945 static VALUE
01946 rb_file_size(VALUE obj)
01947 {
01948     rb_io_t *fptr;
01949     struct stat st;
01950 
01951     GetOpenFile(obj, fptr);
01952     if (fptr->mode & FMODE_WRITABLE) {
01953         rb_io_flush(obj);
01954     }
01955     if (fstat(fptr->fd, &st) == -1) {
01956         rb_sys_fail_path(fptr->pathv);
01957     }
01958     return OFFT2NUM(st.st_size);
01959 }
01960 
01961 static void
01962 chmod_internal(const char *path, VALUE pathv, void *mode)
01963 {
01964     if (chmod(path, *(int *)mode) < 0)
01965         rb_sys_fail_path(pathv);
01966 }
01967 
01968 /*
01969  *  call-seq:
01970  *     File.chmod(mode_int, file_name, ... )  ->  integer
01971  *
01972  *  Changes permission bits on the named file(s) to the bit pattern
01973  *  represented by <i>mode_int</i>. Actual effects are operating system
01974  *  dependent (see the beginning of this section). On Unix systems, see
01975  *  <code>chmod(2)</code> for details. Returns the number of files
01976  *  processed.
01977  *
01978  *     File.chmod(0644, "testfile", "out")   #=> 2
01979  */
01980 
01981 static VALUE
01982 rb_file_s_chmod(int argc, VALUE *argv)
01983 {
01984     VALUE vmode;
01985     VALUE rest;
01986     int mode;
01987     long n;
01988 
01989     rb_secure(2);
01990     rb_scan_args(argc, argv, "1*", &vmode, &rest);
01991     mode = NUM2INT(vmode);
01992 
01993     n = apply2files(chmod_internal, rest, &mode);
01994     return LONG2FIX(n);
01995 }
01996 
01997 /*
01998  *  call-seq:
01999  *     file.chmod(mode_int)   -> 0
02000  *
02001  *  Changes permission bits on <i>file</i> to the bit pattern
02002  *  represented by <i>mode_int</i>. Actual effects are platform
02003  *  dependent; on Unix systems, see <code>chmod(2)</code> for details.
02004  *  Follows symbolic links. Also see <code>File#lchmod</code>.
02005  *
02006  *     f = File.new("out", "w");
02007  *     f.chmod(0644)   #=> 0
02008  */
02009 
02010 static VALUE
02011 rb_file_chmod(VALUE obj, VALUE vmode)
02012 {
02013     rb_io_t *fptr;
02014     int mode;
02015 #ifndef HAVE_FCHMOD
02016     VALUE path;
02017 #endif
02018 
02019     rb_secure(2);
02020     mode = NUM2INT(vmode);
02021 
02022     GetOpenFile(obj, fptr);
02023 #ifdef HAVE_FCHMOD
02024     if (fchmod(fptr->fd, mode) == -1)
02025         rb_sys_fail_path(fptr->pathv);
02026 #else
02027     if (NIL_P(fptr->pathv)) return Qnil;
02028     path = rb_str_encode_ospath(fptr->pathv);
02029     if (chmod(RSTRING_PTR(path), mode) == -1)
02030         rb_sys_fail_path(fptr->pathv);
02031 #endif
02032 
02033     return INT2FIX(0);
02034 }
02035 
02036 #if defined(HAVE_LCHMOD)
02037 static void
02038 lchmod_internal(const char *path, VALUE pathv, void *mode)
02039 {
02040     if (lchmod(path, (int)(VALUE)mode) < 0)
02041         rb_sys_fail_path(pathv);
02042 }
02043 
02044 /*
02045  *  call-seq:
02046  *     File.lchmod(mode_int, file_name, ...)  -> integer
02047  *
02048  *  Equivalent to <code>File::chmod</code>, but does not follow symbolic
02049  *  links (so it will change the permissions associated with the link,
02050  *  not the file referenced by the link). Often not available.
02051  *
02052  */
02053 
02054 static VALUE
02055 rb_file_s_lchmod(int argc, VALUE *argv)
02056 {
02057     VALUE vmode;
02058     VALUE rest;
02059     long mode, n;
02060 
02061     rb_secure(2);
02062     rb_scan_args(argc, argv, "1*", &vmode, &rest);
02063     mode = NUM2INT(vmode);
02064 
02065     n = apply2files(lchmod_internal, rest, (void *)(long)mode);
02066     return LONG2FIX(n);
02067 }
02068 #else
02069 #define rb_file_s_lchmod rb_f_notimplement
02070 #endif
02071 
02072 struct chown_args {
02073     rb_uid_t owner;
02074     rb_gid_t group;
02075 };
02076 
02077 static void
02078 chown_internal(const char *path, VALUE pathv, void *arg)
02079 {
02080     struct chown_args *args = arg;
02081     if (chown(path, args->owner, args->group) < 0)
02082         rb_sys_fail_path(pathv);
02083 }
02084 
02085 /*
02086  *  call-seq:
02087  *     File.chown(owner_int, group_int, file_name,... )  ->  integer
02088  *
02089  *  Changes the owner and group of the named file(s) to the given
02090  *  numeric owner and group id's. Only a process with superuser
02091  *  privileges may change the owner of a file. The current owner of a
02092  *  file may change the file's group to any group to which the owner
02093  *  belongs. A <code>nil</code> or -1 owner or group id is ignored.
02094  *  Returns the number of files processed.
02095  *
02096  *     File.chown(nil, 100, "testfile")
02097  *
02098  */
02099 
02100 static VALUE
02101 rb_file_s_chown(int argc, VALUE *argv)
02102 {
02103     VALUE o, g, rest;
02104     struct chown_args arg;
02105     long n;
02106 
02107     rb_secure(2);
02108     rb_scan_args(argc, argv, "2*", &o, &g, &rest);
02109     if (NIL_P(o)) {
02110         arg.owner = -1;
02111     }
02112     else {
02113         arg.owner = NUM2UIDT(o);
02114     }
02115     if (NIL_P(g)) {
02116         arg.group = -1;
02117     }
02118     else {
02119         arg.group = NUM2GIDT(g);
02120     }
02121 
02122     n = apply2files(chown_internal, rest, &arg);
02123     return LONG2FIX(n);
02124 }
02125 
02126 /*
02127  *  call-seq:
02128  *     file.chown(owner_int, group_int )   -> 0
02129  *
02130  *  Changes the owner and group of <i>file</i> to the given numeric
02131  *  owner and group id's. Only a process with superuser privileges may
02132  *  change the owner of a file. The current owner of a file may change
02133  *  the file's group to any group to which the owner belongs. A
02134  *  <code>nil</code> or -1 owner or group id is ignored. Follows
02135  *  symbolic links. See also <code>File#lchown</code>.
02136  *
02137  *     File.new("testfile").chown(502, 1000)
02138  *
02139  */
02140 
02141 static VALUE
02142 rb_file_chown(VALUE obj, VALUE owner, VALUE group)
02143 {
02144     rb_io_t *fptr;
02145     int o, g;
02146 #ifndef HAVE_FCHOWN
02147     VALUE path;
02148 #endif
02149 
02150     rb_secure(2);
02151     o = NIL_P(owner) ? -1 : NUM2INT(owner);
02152     g = NIL_P(group) ? -1 : NUM2INT(group);
02153     GetOpenFile(obj, fptr);
02154 #ifndef HAVE_FCHOWN
02155     if (NIL_P(fptr->pathv)) return Qnil;
02156     path = rb_str_encode_ospath(fptr->pathv);
02157     if (chown(RSTRING_PTR(path), o, g) == -1)
02158         rb_sys_fail_path(fptr->pathv);
02159 #else
02160     if (fchown(fptr->fd, o, g) == -1)
02161         rb_sys_fail_path(fptr->pathv);
02162 #endif
02163 
02164     return INT2FIX(0);
02165 }
02166 
02167 #if defined(HAVE_LCHOWN)
02168 static void
02169 lchown_internal(const char *path, VALUE pathv, void *arg)
02170 {
02171     struct chown_args *args = arg;
02172     if (lchown(path, args->owner, args->group) < 0)
02173         rb_sys_fail_path(pathv);
02174 }
02175 
02176 /*
02177  *  call-seq:
02178  *     file.lchown(owner_int, group_int, file_name,..) -> integer
02179  *
02180  *  Equivalent to <code>File::chown</code>, but does not follow symbolic
02181  *  links (so it will change the owner associated with the link, not the
02182  *  file referenced by the link). Often not available. Returns number
02183  *  of files in the argument list.
02184  *
02185  */
02186 
02187 static VALUE
02188 rb_file_s_lchown(int argc, VALUE *argv)
02189 {
02190     VALUE o, g, rest;
02191     struct chown_args arg;
02192     long n;
02193 
02194     rb_secure(2);
02195     rb_scan_args(argc, argv, "2*", &o, &g, &rest);
02196     if (NIL_P(o)) {
02197         arg.owner = -1;
02198     }
02199     else {
02200         arg.owner = NUM2UIDT(o);
02201     }
02202     if (NIL_P(g)) {
02203         arg.group = -1;
02204     }
02205     else {
02206         arg.group = NUM2GIDT(g);
02207     }
02208 
02209     n = apply2files(lchown_internal, rest, &arg);
02210     return LONG2FIX(n);
02211 }
02212 #else
02213 #define rb_file_s_lchown rb_f_notimplement
02214 #endif
02215 
02216 struct utime_args {
02217     const struct timespec* tsp;
02218     VALUE atime, mtime;
02219 };
02220 
02221 #if defined DOSISH || defined __CYGWIN__
02222 NORETURN(static void utime_failed(VALUE, const struct timespec *, VALUE, VALUE));
02223 
02224 static void
02225 utime_failed(VALUE path, const struct timespec *tsp, VALUE atime, VALUE mtime)
02226 {
02227     if (tsp && errno == EINVAL) {
02228         VALUE e[2], a = Qnil, m = Qnil;
02229         int d = 0;
02230         if (!NIL_P(atime)) {
02231             a = rb_inspect(atime);
02232         }
02233         if (!NIL_P(mtime) && mtime != atime && !rb_equal(atime, mtime)) {
02234             m = rb_inspect(mtime);
02235         }
02236         if (NIL_P(a)) e[0] = m;
02237         else if (NIL_P(m) || rb_str_cmp(a, m) == 0) e[0] = a;
02238         else {
02239             e[0] = rb_str_plus(a, rb_str_new_cstr(" or "));
02240             rb_str_append(e[0], m);
02241             d = 1;
02242         }
02243         if (!NIL_P(e[0])) {
02244             if (path) {
02245                 if (!d) e[0] = rb_str_dup(e[0]);
02246                 rb_str_append(rb_str_cat2(e[0], " for "), path);
02247             }
02248             e[1] = INT2FIX(EINVAL);
02249             rb_exc_raise(rb_class_new_instance(2, e, rb_eSystemCallError));
02250         }
02251         errno = EINVAL;
02252     }
02253     rb_sys_fail_path(path);
02254 }
02255 #else
02256 #define utime_failed(path, tsp, atime, mtime) rb_sys_fail_path(path)
02257 #endif
02258 
02259 #if defined(HAVE_UTIMES)
02260 
02261 static void
02262 utime_internal(const char *path, VALUE pathv, void *arg)
02263 {
02264     struct utime_args *v = arg;
02265     const struct timespec *tsp = v->tsp;
02266     struct timeval tvbuf[2], *tvp = NULL;
02267 
02268 #ifdef HAVE_UTIMENSAT
02269     static int try_utimensat = 1;
02270 
02271     if (try_utimensat) {
02272         if (utimensat(AT_FDCWD, path, tsp, 0) < 0) {
02273             if (errno == ENOSYS) {
02274                 try_utimensat = 0;
02275                 goto no_utimensat;
02276             }
02277             utime_failed(pathv, tsp, v->atime, v->mtime);
02278         }
02279         return;
02280     }
02281 no_utimensat:
02282 #endif
02283 
02284     if (tsp) {
02285         tvbuf[0].tv_sec = tsp[0].tv_sec;
02286         tvbuf[0].tv_usec = (int)(tsp[0].tv_nsec / 1000);
02287         tvbuf[1].tv_sec = tsp[1].tv_sec;
02288         tvbuf[1].tv_usec = (int)(tsp[1].tv_nsec / 1000);
02289         tvp = tvbuf;
02290     }
02291     if (utimes(path, tvp) < 0)
02292         utime_failed(pathv, tsp, v->atime, v->mtime);
02293 }
02294 
02295 #else
02296 
02297 #if !defined HAVE_UTIME_H && !defined HAVE_SYS_UTIME_H
02298 struct utimbuf {
02299     long actime;
02300     long modtime;
02301 };
02302 #endif
02303 
02304 static void
02305 utime_internal(const char *path, VALUE pathv, void *arg)
02306 {
02307     struct utime_args *v = arg;
02308     const struct timespec *tsp = v->tsp;
02309     struct utimbuf utbuf, *utp = NULL;
02310     if (tsp) {
02311         utbuf.actime = tsp[0].tv_sec;
02312         utbuf.modtime = tsp[1].tv_sec;
02313         utp = &utbuf;
02314     }
02315     if (utime(path, utp) < 0)
02316         utime_failed(pathv, tsp, v->atime, v->mtime);
02317 }
02318 
02319 #endif
02320 
02321 /*
02322  * call-seq:
02323  *  File.utime(atime, mtime, file_name,...)   ->  integer
02324  *
02325  * Sets the access and modification times of each
02326  * named file to the first two arguments. Returns
02327  * the number of file names in the argument list.
02328  */
02329 
02330 static VALUE
02331 rb_file_s_utime(int argc, VALUE *argv)
02332 {
02333     VALUE rest;
02334     struct utime_args args;
02335     struct timespec tss[2], *tsp = NULL;
02336     long n;
02337 
02338     rb_secure(2);
02339     rb_scan_args(argc, argv, "2*", &args.atime, &args.mtime, &rest);
02340 
02341     if (!NIL_P(args.atime) || !NIL_P(args.mtime)) {
02342         tsp = tss;
02343         tsp[0] = rb_time_timespec(args.atime);
02344         tsp[1] = rb_time_timespec(args.mtime);
02345     }
02346     args.tsp = tsp;
02347 
02348     n = apply2files(utime_internal, rest, &args);
02349     return LONG2FIX(n);
02350 }
02351 
02352 NORETURN(static void sys_fail2(VALUE,VALUE));
02353 static void
02354 sys_fail2(VALUE s1, VALUE s2)
02355 {
02356     VALUE str;
02357 #ifdef MAX_PATH
02358     const int max_pathlen = MAX_PATH;
02359 #else
02360     const int max_pathlen = MAXPATHLEN;
02361 #endif
02362 
02363     str = rb_str_new_cstr("(");
02364     rb_str_append(str, rb_str_ellipsize(s1, max_pathlen));
02365     rb_str_cat2(str, ", ");
02366     rb_str_append(str, rb_str_ellipsize(s2, max_pathlen));
02367     rb_str_cat2(str, ")");
02368     rb_sys_fail_path(str);
02369 }
02370 
02371 #ifdef HAVE_LINK
02372 /*
02373  *  call-seq:
02374  *     File.link(old_name, new_name)    -> 0
02375  *
02376  *  Creates a new name for an existing file using a hard link. Will not
02377  *  overwrite <i>new_name</i> if it already exists (raising a subclass
02378  *  of <code>SystemCallError</code>). Not available on all platforms.
02379  *
02380  *     File.link("testfile", ".testfile")   #=> 0
02381  *     IO.readlines(".testfile")[0]         #=> "This is line one\n"
02382  */
02383 
02384 static VALUE
02385 rb_file_s_link(VALUE klass, VALUE from, VALUE to)
02386 {
02387     rb_secure(2);
02388     FilePathValue(from);
02389     FilePathValue(to);
02390     from = rb_str_encode_ospath(from);
02391     to = rb_str_encode_ospath(to);
02392 
02393     if (link(StringValueCStr(from), StringValueCStr(to)) < 0) {
02394         sys_fail2(from, to);
02395     }
02396     return INT2FIX(0);
02397 }
02398 #else
02399 #define rb_file_s_link rb_f_notimplement
02400 #endif
02401 
02402 #ifdef HAVE_SYMLINK
02403 /*
02404  *  call-seq:
02405  *     File.symlink(old_name, new_name)   -> 0
02406  *
02407  *  Creates a symbolic link called <i>new_name</i> for the existing file
02408  *  <i>old_name</i>. Raises a <code>NotImplemented</code> exception on
02409  *  platforms that do not support symbolic links.
02410  *
02411  *     File.symlink("testfile", "link2test")   #=> 0
02412  *
02413  */
02414 
02415 static VALUE
02416 rb_file_s_symlink(VALUE klass, VALUE from, VALUE to)
02417 {
02418     rb_secure(2);
02419     FilePathValue(from);
02420     FilePathValue(to);
02421     from = rb_str_encode_ospath(from);
02422     to = rb_str_encode_ospath(to);
02423 
02424     if (symlink(StringValueCStr(from), StringValueCStr(to)) < 0) {
02425         sys_fail2(from, to);
02426     }
02427     return INT2FIX(0);
02428 }
02429 #else
02430 #define rb_file_s_symlink rb_f_notimplement
02431 #endif
02432 
02433 #ifdef HAVE_READLINK
02434 static VALUE rb_readlink(VALUE path);
02435 
02436 /*
02437  *  call-seq:
02438  *     File.readlink(link_name)  ->  file_name
02439  *
02440  *  Returns the name of the file referenced by the given link.
02441  *  Not available on all platforms.
02442  *
02443  *     File.symlink("testfile", "link2test")   #=> 0
02444  *     File.readlink("link2test")              #=> "testfile"
02445  */
02446 
02447 static VALUE
02448 rb_file_s_readlink(VALUE klass, VALUE path)
02449 {
02450     return rb_readlink(path);
02451 }
02452 
02453 static VALUE
02454 rb_readlink(VALUE path)
02455 {
02456     char *buf;
02457     int size = 100;
02458     ssize_t rv;
02459     VALUE v;
02460 
02461     rb_secure(2);
02462     FilePathValue(path);
02463     path = rb_str_encode_ospath(path);
02464     buf = xmalloc(size);
02465     while ((rv = readlink(RSTRING_PTR(path), buf, size)) == size
02466 #ifdef _AIX
02467             || (rv < 0 && errno == ERANGE) /* quirky behavior of GPFS */
02468 #endif
02469         ) {
02470         size *= 2;
02471         buf = xrealloc(buf, size);
02472     }
02473     if (rv < 0) {
02474         xfree(buf);
02475         rb_sys_fail_path(path);
02476     }
02477     v = rb_filesystem_str_new(buf, rv);
02478     xfree(buf);
02479 
02480     return v;
02481 }
02482 #else
02483 #define rb_file_s_readlink rb_f_notimplement
02484 #endif
02485 
02486 static void
02487 unlink_internal(const char *path, VALUE pathv, void *arg)
02488 {
02489     if (unlink(path) < 0)
02490         rb_sys_fail_path(pathv);
02491 }
02492 
02493 /*
02494  *  call-seq:
02495  *     File.delete(file_name, ...)  -> integer
02496  *     File.unlink(file_name, ...)  -> integer
02497  *
02498  *  Deletes the named files, returning the number of names
02499  *  passed as arguments. Raises an exception on any error.
02500  *  See also <code>Dir::rmdir</code>.
02501  */
02502 
02503 static VALUE
02504 rb_file_s_unlink(VALUE klass, VALUE args)
02505 {
02506     long n;
02507 
02508     rb_secure(2);
02509     n = apply2files(unlink_internal, args, 0);
02510     return LONG2FIX(n);
02511 }
02512 
02513 /*
02514  *  call-seq:
02515  *     File.rename(old_name, new_name)   -> 0
02516  *
02517  *  Renames the given file to the new name. Raises a
02518  *  <code>SystemCallError</code> if the file cannot be renamed.
02519  *
02520  *     File.rename("afile", "afile.bak")   #=> 0
02521  */
02522 
02523 static VALUE
02524 rb_file_s_rename(VALUE klass, VALUE from, VALUE to)
02525 {
02526     const char *src, *dst;
02527     VALUE f, t;
02528 
02529     rb_secure(2);
02530     FilePathValue(from);
02531     FilePathValue(to);
02532     f = rb_str_encode_ospath(from);
02533     t = rb_str_encode_ospath(to);
02534     src = StringValueCStr(f);
02535     dst = StringValueCStr(t);
02536 #if defined __CYGWIN__
02537     errno = 0;
02538 #endif
02539     if (rename(src, dst) < 0) {
02540 #if defined DOSISH
02541         switch (errno) {
02542           case EEXIST:
02543 #if defined (__EMX__)
02544           case EACCES:
02545 #endif
02546             if (chmod(dst, 0666) == 0 &&
02547                 unlink(dst) == 0 &&
02548                 rename(src, dst) == 0)
02549                 return INT2FIX(0);
02550         }
02551 #endif
02552         sys_fail2(from, to);
02553     }
02554 
02555     return INT2FIX(0);
02556 }
02557 
02558 /*
02559  *  call-seq:
02560  *     File.umask()          -> integer
02561  *     File.umask(integer)   -> integer
02562  *
02563  *  Returns the current umask value for this process. If the optional
02564  *  argument is given, set the umask to that value and return the
02565  *  previous value. Umask values are <em>subtracted</em> from the
02566  *  default permissions, so a umask of <code>0222</code> would make a
02567  *  file read-only for everyone.
02568  *
02569  *     File.umask(0006)   #=> 18
02570  *     File.umask         #=> 6
02571  */
02572 
02573 static VALUE
02574 rb_file_s_umask(int argc, VALUE *argv)
02575 {
02576     int omask = 0;
02577 
02578     rb_secure(2);
02579     if (argc == 0) {
02580         omask = umask(0);
02581         umask(omask);
02582     }
02583     else if (argc == 1) {
02584         omask = umask(NUM2INT(argv[0]));
02585     }
02586     else {
02587         rb_raise(rb_eArgError, "wrong number of arguments (%d for 0..1)", argc);
02588     }
02589     return INT2FIX(omask);
02590 }
02591 
02592 #ifdef __CYGWIN__
02593 #undef DOSISH
02594 #endif
02595 #if defined __CYGWIN__ || defined DOSISH
02596 #define DOSISH_UNC
02597 #define DOSISH_DRIVE_LETTER
02598 #define FILE_ALT_SEPARATOR '\\'
02599 #endif
02600 #ifdef FILE_ALT_SEPARATOR
02601 #define isdirsep(x) ((x) == '/' || (x) == FILE_ALT_SEPARATOR)
02602 static const char file_alt_separator[] = {FILE_ALT_SEPARATOR, '\0'};
02603 #else
02604 #define isdirsep(x) ((x) == '/')
02605 #endif
02606 
02607 #ifndef USE_NTFS
02608 #if defined _WIN32 || defined __CYGWIN__
02609 #define USE_NTFS 1
02610 #else
02611 #define USE_NTFS 0
02612 #endif
02613 #endif
02614 
02615 #if USE_NTFS
02616 #define istrailinggarbage(x) ((x) == '.' || (x) == ' ')
02617 #else
02618 #define istrailinggarbage(x) 0
02619 #endif
02620 
02621 #define Next(p, e, enc) ((p) + rb_enc_mbclen((p), (e), (enc)))
02622 #define Inc(p, e, enc) ((p) = Next((p), (e), (enc)))
02623 
02624 #if defined(DOSISH_UNC)
02625 #define has_unc(buf) (isdirsep((buf)[0]) && isdirsep((buf)[1]))
02626 #else
02627 #define has_unc(buf) 0
02628 #endif
02629 
02630 #ifdef DOSISH_DRIVE_LETTER
02631 static inline int
02632 has_drive_letter(const char *buf)
02633 {
02634     if (ISALPHA(buf[0]) && buf[1] == ':') {
02635         return 1;
02636     }
02637     else {
02638         return 0;
02639     }
02640 }
02641 
02642 static char*
02643 getcwdofdrv(int drv)
02644 {
02645     char drive[4];
02646     char *drvcwd, *oldcwd;
02647 
02648     drive[0] = drv;
02649     drive[1] = ':';
02650     drive[2] = '\0';
02651 
02652     /* the only way that I know to get the current directory
02653        of a particular drive is to change chdir() to that drive,
02654        so save the old cwd before chdir()
02655     */
02656     oldcwd = my_getcwd();
02657     if (chdir(drive) == 0) {
02658         drvcwd = my_getcwd();
02659         chdir(oldcwd);
02660         xfree(oldcwd);
02661     }
02662     else {
02663         /* perhaps the drive is not exist. we return only drive letter */
02664         drvcwd = strdup(drive);
02665     }
02666     return drvcwd;
02667 }
02668 
02669 static inline int
02670 not_same_drive(VALUE path, int drive)
02671 {
02672     const char *p = RSTRING_PTR(path);
02673     if (RSTRING_LEN(path) < 2) return 0;
02674     if (has_drive_letter(p)) {
02675         return TOLOWER(p[0]) != TOLOWER(drive);
02676     }
02677     else {
02678         return has_unc(p);
02679     }
02680 }
02681 #endif
02682 
02683 static inline char *
02684 skiproot(const char *path, const char *end, rb_encoding *enc)
02685 {
02686 #ifdef DOSISH_DRIVE_LETTER
02687     if (path + 2 <= end && has_drive_letter(path)) path += 2;
02688 #endif
02689     while (path < end && isdirsep(*path)) path++;
02690     return (char *)path;
02691 }
02692 
02693 #define nextdirsep rb_enc_path_next
02694 char *
02695 rb_enc_path_next(const char *s, const char *e, rb_encoding *enc)
02696 {
02697     while (s < e && !isdirsep(*s)) {
02698         Inc(s, e, enc);
02699     }
02700     return (char *)s;
02701 }
02702 
02703 #if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
02704 #define skipprefix rb_enc_path_skip_prefix
02705 #else
02706 #define skipprefix(path, end, enc) (path)
02707 #endif
02708 char *
02709 rb_enc_path_skip_prefix(const char *path, const char *end, rb_encoding *enc)
02710 {
02711 #if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
02712 #ifdef DOSISH_UNC
02713     if (path + 2 <= end && isdirsep(path[0]) && isdirsep(path[1])) {
02714         path += 2;
02715         while (path < end && isdirsep(*path)) path++;
02716         if ((path = rb_enc_path_next(path, end, enc)) < end && path[0] && path[1] && !isdirsep(path[1]))
02717             path = rb_enc_path_next(path + 1, end, enc);
02718         return (char *)path;
02719     }
02720 #endif
02721 #ifdef DOSISH_DRIVE_LETTER
02722     if (has_drive_letter(path))
02723         return (char *)(path + 2);
02724 #endif
02725 #endif
02726     return (char *)path;
02727 }
02728 
02729 static inline char *
02730 skipprefixroot(const char *path, const char *end, rb_encoding *enc)
02731 {
02732 #if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
02733     char *p = skipprefix(path, end, enc);
02734     while (isdirsep(*p)) p++;
02735     return p;
02736 #else
02737     return skiproot(path, end, enc);
02738 #endif
02739 }
02740 
02741 #define strrdirsep rb_enc_path_last_separator
02742 char *
02743 rb_enc_path_last_separator(const char *path, const char *end, rb_encoding *enc)
02744 {
02745     char *last = NULL;
02746     while (path < end) {
02747         if (isdirsep(*path)) {
02748             const char *tmp = path++;
02749             while (path < end && isdirsep(*path)) path++;
02750             if (path >= end) break;
02751             last = (char *)tmp;
02752         }
02753         else {
02754             Inc(path, end, enc);
02755         }
02756     }
02757     return last;
02758 }
02759 
02760 static char *
02761 chompdirsep(const char *path, const char *end, rb_encoding *enc)
02762 {
02763     while (path < end) {
02764         if (isdirsep(*path)) {
02765             const char *last = path++;
02766             while (path < end && isdirsep(*path)) path++;
02767             if (path >= end) return (char *)last;
02768         }
02769         else {
02770             Inc(path, end, enc);
02771         }
02772     }
02773     return (char *)path;
02774 }
02775 
02776 char *
02777 rb_enc_path_end(const char *path, const char *end, rb_encoding *enc)
02778 {
02779     if (path < end && isdirsep(*path)) path++;
02780     return chompdirsep(path, end, enc);
02781 }
02782 
02783 #if USE_NTFS
02784 static char *
02785 ntfs_tail(const char *path, const char *end, rb_encoding *enc)
02786 {
02787     while (path < end && *path == '.') path++;
02788     while (path < end && *path != ':') {
02789         if (istrailinggarbage(*path)) {
02790             const char *last = path++;
02791             while (path < end && istrailinggarbage(*path)) path++;
02792             if (path >= end || *path == ':') return (char *)last;
02793         }
02794         else if (isdirsep(*path)) {
02795             const char *last = path++;
02796             while (path < end && isdirsep(*path)) path++;
02797             if (path >= end) return (char *)last;
02798             if (*path == ':') path++;
02799         }
02800         else {
02801             Inc(path, end, enc);
02802         }
02803     }
02804     return (char *)path;
02805 }
02806 #endif
02807 
02808 #define BUFCHECK(cond) do {\
02809     bdiff = p - buf;\
02810     if (cond) {\
02811         do {buflen *= 2;} while (cond);\
02812         rb_str_resize(result, buflen);\
02813         buf = RSTRING_PTR(result);\
02814         p = buf + bdiff;\
02815         pend = buf + buflen;\
02816     }\
02817 } while (0)
02818 
02819 #define BUFINIT() (\
02820     p = buf = RSTRING_PTR(result),\
02821     buflen = RSTRING_LEN(result),\
02822     pend = p + buflen)
02823 
02824 VALUE
02825 rb_home_dir(const char *user, VALUE result)
02826 {
02827     const char *dir;
02828     char *buf;
02829 #if defined DOSISH || defined __CYGWIN__
02830     char *p, *bend;
02831 #endif
02832     long dirlen;
02833     rb_encoding *enc;
02834 
02835     if (!user || !*user) {
02836         if (!(dir = getenv("HOME"))) {
02837             rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'");
02838         }
02839         dirlen = strlen(dir);
02840         rb_str_resize(result, dirlen);
02841         memcpy(buf = RSTRING_PTR(result), dir, dirlen);
02842     }
02843     else {
02844 #ifdef HAVE_PWD_H
02845         struct passwd *pwPtr = getpwnam(user);
02846         if (!pwPtr) {
02847             endpwent();
02848             rb_raise(rb_eArgError, "user %s doesn't exist", user);
02849         }
02850         dirlen = strlen(pwPtr->pw_dir);
02851         rb_str_resize(result, dirlen);
02852         memcpy(buf = RSTRING_PTR(result), pwPtr->pw_dir, dirlen + 1);
02853         endpwent();
02854 #else
02855         return Qnil;
02856 #endif
02857     }
02858     enc = rb_filesystem_encoding();
02859     rb_enc_associate(result, enc);
02860 #if defined DOSISH || defined __CYGWIN__
02861     for (bend = (p = buf) + dirlen; p < bend; Inc(p, bend, enc)) {
02862         if (*p == '\\') {
02863             *p = '/';
02864         }
02865     }
02866 #endif
02867     return result;
02868 }
02869 
02870 #ifndef _WIN32
02871 static char *
02872 append_fspath(VALUE result, VALUE fname, char *dir, rb_encoding **enc, rb_encoding *fsenc)
02873 {
02874     char *buf, *cwdp = dir;
02875     VALUE dirname = Qnil;
02876     size_t dirlen = strlen(dir), buflen = rb_str_capacity(result);
02877 
02878     *enc = fsenc;
02879     do {buflen *= 2;} while (dirlen > buflen);
02880     rb_str_resize(result, buflen);
02881     buf = RSTRING_PTR(result);
02882     memcpy(buf, cwdp, dirlen);
02883     xfree(dir);
02884     if (!NIL_P(dirname)) rb_str_resize(dirname, 0);
02885     rb_enc_associate(result, *enc);
02886     return buf + dirlen;
02887 }
02888 
02889 VALUE
02890 rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_name, VALUE result)
02891 {
02892     const char *s, *b, *fend;
02893     char *buf, *p, *pend, *root;
02894     size_t buflen, bdiff;
02895     int tainted;
02896     rb_encoding *enc, *fsenc = rb_filesystem_encoding();
02897 
02898     s = StringValuePtr(fname);
02899     fend = s + RSTRING_LEN(fname);
02900     enc = rb_enc_get(fname);
02901     BUFINIT();
02902     tainted = OBJ_TAINTED(fname);
02903 
02904     if (s[0] == '~' && abs_mode == 0) {      /* execute only if NOT absolute_path() */
02905         long userlen = 0;
02906         tainted = 1;
02907         if (isdirsep(s[1]) || s[1] == '\0') {
02908             buf = 0;
02909             b = 0;
02910             rb_str_set_len(result, 0);
02911             if (*++s) ++s;
02912         }
02913         else {
02914             s = nextdirsep(b = s, fend, enc);
02915             userlen = s - b;
02916             BUFCHECK(bdiff + userlen >= buflen);
02917             memcpy(p, b, userlen);
02918             rb_str_set_len(result, userlen);
02919             buf = p + 1;
02920             p += userlen;
02921         }
02922         if (NIL_P(rb_home_dir(buf, result))) {
02923             rb_raise(rb_eArgError, "can't find user %s", buf);
02924         }
02925         if (!rb_is_absolute_path(RSTRING_PTR(result))) {
02926             if (userlen) {
02927                 rb_raise(rb_eArgError, "non-absolute home of %.*s", (int)userlen, b);
02928             }
02929             else {
02930                 rb_raise(rb_eArgError, "non-absolute home");
02931             }
02932         }
02933         BUFINIT();
02934         p = pend;
02935     }
02936 #ifdef DOSISH_DRIVE_LETTER
02937     /* skip drive letter */
02938     else if (has_drive_letter(s)) {
02939         if (isdirsep(s[2])) {
02940             /* specified drive letter, and full path */
02941             /* skip drive letter */
02942             BUFCHECK(bdiff + 2 >= buflen);
02943             memcpy(p, s, 2);
02944             p += 2;
02945             s += 2;
02946             rb_enc_copy(result, fname);
02947         }
02948         else {
02949             /* specified drive, but not full path */
02950             int same = 0;
02951             if (!NIL_P(dname) && !not_same_drive(dname, s[0])) {
02952                 rb_file_expand_path_internal(dname, Qnil, abs_mode, long_name, result);
02953                 BUFINIT();
02954                 if (has_drive_letter(p) && TOLOWER(p[0]) == TOLOWER(s[0])) {
02955                     /* ok, same drive */
02956                     same = 1;
02957                 }
02958             }
02959             if (!same) {
02960                 char *e = append_fspath(result, fname, getcwdofdrv(*s), &enc, fsenc);
02961                 tainted = 1;
02962                 BUFINIT();
02963                 p = e;
02964             }
02965             else {
02966                 rb_enc_associate(result, enc = rb_enc_check(result, fname));
02967                 p = pend;
02968             }
02969             p = chompdirsep(skiproot(buf, p, enc), p, enc);
02970             s += 2;
02971         }
02972     }
02973 #endif
02974     else if (!rb_is_absolute_path(s)) {
02975         if (!NIL_P(dname)) {
02976             rb_file_expand_path_internal(dname, Qnil, abs_mode, long_name, result);
02977             rb_enc_associate(result, rb_enc_check(result, fname));
02978             BUFINIT();
02979             p = pend;
02980         }
02981         else {
02982             char *e = append_fspath(result, fname, my_getcwd(), &enc, fsenc);
02983             tainted = 1;
02984             BUFINIT();
02985             p = e;
02986         }
02987 #if defined DOSISH || defined __CYGWIN__
02988         if (isdirsep(*s)) {
02989             /* specified full path, but not drive letter nor UNC */
02990             /* we need to get the drive letter or UNC share name */
02991             p = skipprefix(buf, p, enc);
02992         }
02993         else
02994 #endif
02995             p = chompdirsep(skiproot(buf, p, enc), p, enc);
02996     }
02997     else {
02998         size_t len;
02999         b = s;
03000         do s++; while (isdirsep(*s));
03001         len = s - b;
03002         p = buf + len;
03003         BUFCHECK(bdiff >= buflen);
03004         memset(buf, '/', len);
03005         rb_str_set_len(result, len);
03006         rb_enc_associate(result, rb_enc_check(result, fname));
03007     }
03008     if (p > buf && p[-1] == '/')
03009         --p;
03010     else {
03011         rb_str_set_len(result, p-buf);
03012         BUFCHECK(bdiff + 1 >= buflen);
03013         *p = '/';
03014     }
03015 
03016     rb_str_set_len(result, p-buf+1);
03017     BUFCHECK(bdiff + 1 >= buflen);
03018     p[1] = 0;
03019     root = skipprefix(buf, p+1, enc);
03020 
03021     b = s;
03022     while (*s) {
03023         switch (*s) {
03024           case '.':
03025             if (b == s++) {     /* beginning of path element */
03026                 switch (*s) {
03027                   case '\0':
03028                     b = s;
03029                     break;
03030                   case '.':
03031                     if (*(s+1) == '\0' || isdirsep(*(s+1))) {
03032                         /* We must go back to the parent */
03033                         char *n;
03034                         *p = '\0';
03035                         if (!(n = strrdirsep(root, p, enc))) {
03036                             *p = '/';
03037                         }
03038                         else {
03039                             p = n;
03040                         }
03041                         b = ++s;
03042                     }
03043 #if USE_NTFS
03044                     else {
03045                         do ++s; while (istrailinggarbage(*s));
03046                     }
03047 #endif
03048                     break;
03049                   case '/':
03050 #if defined DOSISH || defined __CYGWIN__
03051                   case '\\':
03052 #endif
03053                     b = ++s;
03054                     break;
03055                   default:
03056                     /* ordinary path element, beginning don't move */
03057                     break;
03058                 }
03059             }
03060 #if USE_NTFS
03061             else {
03062                 --s;
03063               case ' ': {
03064                 const char *e = s;
03065                 while (s < fend && istrailinggarbage(*s)) s++;
03066                 if (!*s) {
03067                     s = e;
03068                     goto endpath;
03069                 }
03070               }
03071             }
03072 #endif
03073             break;
03074           case '/':
03075 #if defined DOSISH || defined __CYGWIN__
03076           case '\\':
03077 #endif
03078             if (s > b) {
03079                 long rootdiff = root - buf;
03080                 rb_str_set_len(result, p-buf+1);
03081                 BUFCHECK(bdiff + (s-b+1) >= buflen);
03082                 root = buf + rootdiff;
03083                 memcpy(++p, b, s-b);
03084                 p += s-b;
03085                 *p = '/';
03086             }
03087             b = ++s;
03088             break;
03089           default:
03090             Inc(s, fend, enc);
03091             break;
03092         }
03093     }
03094 
03095     if (s > b) {
03096 #if USE_NTFS
03097         static const char prime[] = ":$DATA";
03098         enum {prime_len = sizeof(prime) -1};
03099       endpath:
03100         if (s > b + prime_len && strncasecmp(s - prime_len, prime, prime_len) == 0) {
03101             /* alias of stream */
03102             /* get rid of a bug of x64 VC++ */
03103             if (*(s - (prime_len+1)) == ':') {
03104                 s -= prime_len + 1; /* prime */
03105             }
03106             else if (memchr(b, ':', s - prime_len - b)) {
03107                 s -= prime_len; /* alternative */
03108             }
03109         }
03110 #endif
03111         rb_str_set_len(result, p-buf+1);
03112         BUFCHECK(bdiff + (s-b) >= buflen);
03113         memcpy(++p, b, s-b);
03114         p += s-b;
03115         rb_str_set_len(result, p-buf);
03116     }
03117     if (p == skiproot(buf, p + !!*p, enc) - 1) p++;
03118 
03119 #if USE_NTFS
03120     *p = '\0';
03121     if ((s = strrdirsep(b = buf, p, enc)) != 0 && !strpbrk(s, "*?")) {
03122         VALUE tmp, v;
03123         size_t len;
03124         rb_encoding *enc;
03125         WCHAR *wstr;
03126         WIN32_FIND_DATAW wfd;
03127         HANDLE h;
03128 #ifdef __CYGWIN__
03129 #ifdef HAVE_CYGWIN_CONV_PATH
03130         char *w32buf = NULL;
03131         const int flags = CCP_POSIX_TO_WIN_A | CCP_RELATIVE;
03132 #else
03133         char w32buf[MAXPATHLEN];
03134 #endif
03135         const char *path;
03136         ssize_t bufsize;
03137         int lnk_added = 0, is_symlink = 0;
03138         struct stat st;
03139         p = (char *)s;
03140         len = strlen(p);
03141         if (lstat(buf, &st) == 0 && S_ISLNK(st.st_mode)) {
03142             is_symlink = 1;
03143             if (len > 4 && STRCASECMP(p + len - 4, ".lnk") != 0) {
03144                 lnk_added = 1;
03145             }
03146         }
03147         path = *buf ? buf : "/";
03148 #ifdef HAVE_CYGWIN_CONV_PATH
03149         bufsize = cygwin_conv_path(flags, path, NULL, 0);
03150         if (bufsize > 0) {
03151             bufsize += len;
03152             if (lnk_added) bufsize += 4;
03153             w32buf = ALLOCA_N(char, bufsize);
03154             if (cygwin_conv_path(flags, path, w32buf, bufsize) == 0) {
03155                 b = w32buf;
03156             }
03157         }
03158 #else
03159         bufsize = MAXPATHLEN;
03160         if (cygwin_conv_to_win32_path(path, w32buf) == 0) {
03161             b = w32buf;
03162         }
03163 #endif
03164         if (is_symlink && b == w32buf) {
03165             *p = '\\';
03166             strlcat(w32buf, p, bufsize);
03167             if (lnk_added) {
03168                 strlcat(w32buf, ".lnk", bufsize);
03169             }
03170         }
03171         else {
03172             lnk_added = 0;
03173         }
03174         *p = '/';
03175 #endif
03176         rb_str_set_len(result, p - buf + strlen(p));
03177         enc = rb_enc_get(result);
03178         tmp = result;
03179         if (enc != rb_utf8_encoding() && rb_enc_str_coderange(result) != ENC_CODERANGE_7BIT) {
03180             tmp = rb_str_encode_ospath(result);
03181         }
03182         len = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, NULL, 0);
03183         wstr = ALLOCV_N(WCHAR, v, len);
03184         MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, wstr, len);
03185         if (tmp != result) rb_str_resize(tmp, 0);
03186         h = FindFirstFileW(wstr, &wfd);
03187         ALLOCV_END(v);
03188         if (h != INVALID_HANDLE_VALUE) {
03189             size_t wlen;
03190             FindClose(h);
03191             len = lstrlenW(wfd.cFileName);
03192 #ifdef __CYGWIN__
03193             if (lnk_added && len > 4 &&
03194                 wcscasecmp(wfd.cFileName + len - 4, L".lnk") == 0) {
03195                 wfd.cFileName[len -= 4] = L'\0';
03196             }
03197 #else
03198             p = (char *)s;
03199 #endif
03200             ++p;
03201             wlen = (int)len;
03202             len = WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, NULL, 0, NULL, NULL);
03203             BUFCHECK(bdiff + len >= buflen);
03204             WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, p, len + 1, NULL, NULL);
03205             if (tmp != result) {
03206                 rb_str_buf_cat(tmp, p, len);
03207                 tmp = rb_str_encode(tmp, rb_enc_from_encoding(enc), 0, Qnil);
03208                 len = RSTRING_LEN(tmp);
03209                 BUFCHECK(bdiff + len >= buflen);
03210                 memcpy(p, RSTRING_PTR(tmp), len);
03211                 rb_str_resize(tmp, 0);
03212             }
03213             p += len;
03214         }
03215 #ifdef __CYGWIN__
03216         else {
03217             p += strlen(p);
03218         }
03219 #endif
03220     }
03221 #endif
03222 
03223     if (tainted) OBJ_TAINT(result);
03224     rb_str_set_len(result, p - buf);
03225     rb_enc_check(fname, result);
03226     ENC_CODERANGE_CLEAR(result);
03227     return result;
03228 }
03229 #endif /* _WIN32 */
03230 
03231 #define EXPAND_PATH_BUFFER() rb_enc_str_new(0, MAXPATHLEN + 2, rb_filesystem_encoding())
03232 
03233 #define check_expand_path_args(fname, dname) \
03234     (((fname) = rb_get_path(fname)), \
03235      (void)(NIL_P(dname) ? (dname) : ((dname) = rb_get_path(dname))))
03236 
03237 static VALUE
03238 file_expand_path_1(VALUE fname)
03239 {
03240     return rb_file_expand_path_internal(fname, Qnil, 0, 0, EXPAND_PATH_BUFFER());
03241 }
03242 
03243 VALUE
03244 rb_file_expand_path(VALUE fname, VALUE dname)
03245 {
03246     check_expand_path_args(fname, dname);
03247     return rb_file_expand_path_internal(fname, dname, 0, 1, EXPAND_PATH_BUFFER());
03248 }
03249 
03250 VALUE
03251 rb_file_expand_path_fast(VALUE fname, VALUE dname)
03252 {
03253     check_expand_path_args(fname, dname);
03254     return rb_file_expand_path_internal(fname, dname, 0, 0, EXPAND_PATH_BUFFER());
03255 }
03256 
03257 /*
03258  *  call-seq:
03259  *     File.expand_path(file_name [, dir_string] )  ->  abs_file_name
03260  *
03261  *  Converts a pathname to an absolute pathname. Relative paths are
03262  *  referenced from the current working directory of the process unless
03263  *  <i>dir_string</i> is given, in which case it will be used as the
03264  *  starting point. The given pathname may start with a
03265  *  ``<code>~</code>'', which expands to the process owner's home
03266  *  directory (the environment variable <code>HOME</code> must be set
03267  *  correctly). ``<code>~</code><i>user</i>'' expands to the named
03268  *  user's home directory.
03269  *
03270  *     File.expand_path("~oracle/bin")           #=> "/home/oracle/bin"
03271  *     File.expand_path("../../bin", "/tmp/x")   #=> "/bin"
03272  */
03273 
03274 VALUE
03275 rb_file_s_expand_path(int argc, VALUE *argv)
03276 {
03277     VALUE fname, dname;
03278 
03279     if (argc == 1) {
03280         return rb_file_expand_path(argv[0], Qnil);
03281     }
03282     rb_scan_args(argc, argv, "11", &fname, &dname);
03283 
03284     return rb_file_expand_path(fname, dname);
03285 }
03286 
03287 VALUE
03288 rb_file_absolute_path(VALUE fname, VALUE dname)
03289 {
03290     check_expand_path_args(fname, dname);
03291     return rb_file_expand_path_internal(fname, dname, 1, 1, EXPAND_PATH_BUFFER());
03292 }
03293 
03294 /*
03295  *  call-seq:
03296  *     File.absolute_path(file_name [, dir_string] )  ->  abs_file_name
03297  *
03298  *  Converts a pathname to an absolute pathname. Relative paths are
03299  *  referenced from the current working directory of the process unless
03300  *  <i>dir_string</i> is given, in which case it will be used as the
03301  *  starting point. If the given pathname starts with a ``<code>~</code>''
03302  *  it is NOT expanded, it is treated as a normal directory name.
03303  *
03304  *     File.absolute_path("~oracle/bin")       #=> "<relative_path>/~oracle/bin"
03305  */
03306 
03307 VALUE
03308 rb_file_s_absolute_path(int argc, VALUE *argv)
03309 {
03310     VALUE fname, dname;
03311 
03312     if (argc == 1) {
03313         return rb_file_absolute_path(argv[0], Qnil);
03314     }
03315     rb_scan_args(argc, argv, "11", &fname, &dname);
03316 
03317     return rb_file_absolute_path(fname, dname);
03318 }
03319 
03320 static void
03321 realpath_rec(long *prefixlenp, VALUE *resolvedp, const char *unresolved, VALUE loopcheck, int strict, int last)
03322 {
03323     const char *pend = unresolved + strlen(unresolved);
03324     rb_encoding *enc = rb_enc_get(*resolvedp);
03325     ID resolving;
03326     CONST_ID(resolving, "resolving");
03327     while (unresolved < pend) {
03328         const char *testname = unresolved;
03329         const char *unresolved_firstsep = rb_enc_path_next(unresolved, pend, enc);
03330         long testnamelen = unresolved_firstsep - unresolved;
03331         const char *unresolved_nextname = unresolved_firstsep;
03332         while (unresolved_nextname < pend && isdirsep(*unresolved_nextname))
03333             unresolved_nextname++;
03334         unresolved = unresolved_nextname;
03335         if (testnamelen == 1 && testname[0] == '.') {
03336         }
03337         else if (testnamelen == 2 && testname[0] == '.' && testname[1] == '.') {
03338             if (*prefixlenp < RSTRING_LEN(*resolvedp)) {
03339                 const char *resolved_str = RSTRING_PTR(*resolvedp);
03340                 const char *resolved_names = resolved_str + *prefixlenp;
03341                 const char *lastsep = strrdirsep(resolved_names, resolved_str + RSTRING_LEN(*resolvedp), enc);
03342                 long len = lastsep ? lastsep - resolved_names : 0;
03343                 rb_str_resize(*resolvedp, *prefixlenp + len);
03344             }
03345         }
03346         else {
03347             VALUE checkval;
03348             VALUE testpath = rb_str_dup(*resolvedp);
03349             if (*prefixlenp < RSTRING_LEN(testpath))
03350                 rb_str_cat2(testpath, "/");
03351             rb_str_cat(testpath, testname, testnamelen);
03352             checkval = rb_hash_aref(loopcheck, testpath);
03353             if (!NIL_P(checkval)) {
03354                 if (checkval == ID2SYM(resolving)) {
03355                     errno = ELOOP;
03356                     rb_sys_fail_path(testpath);
03357                 }
03358                 else {
03359                     *resolvedp = rb_str_dup(checkval);
03360                 }
03361             }
03362             else {
03363                 struct stat sbuf;
03364                 int ret;
03365                 VALUE testpath2 = rb_str_encode_ospath(testpath);
03366                 ret = lstat(RSTRING_PTR(testpath2), &sbuf);
03367                 if (ret == -1) {
03368                     if (errno == ENOENT) {
03369                         if (strict || !last || *unresolved_firstsep)
03370                             rb_sys_fail_path(testpath);
03371                         *resolvedp = testpath;
03372                         break;
03373                     }
03374                     else {
03375                         rb_sys_fail_path(testpath);
03376                     }
03377                 }
03378 #ifdef HAVE_READLINK
03379                 if (S_ISLNK(sbuf.st_mode)) {
03380                     VALUE link;
03381                     volatile VALUE link_orig = Qnil;
03382                     const char *link_prefix, *link_names;
03383                     long link_prefixlen;
03384                     rb_hash_aset(loopcheck, testpath, ID2SYM(resolving));
03385                     link = rb_readlink(testpath);
03386                     link_prefix = RSTRING_PTR(link);
03387                     link_names = skipprefixroot(link_prefix, link_prefix + RSTRING_LEN(link), rb_enc_get(link));
03388                     link_prefixlen = link_names - link_prefix;
03389                     if (link_prefixlen > 0) {
03390                         rb_encoding *enc, *linkenc = rb_enc_get(link);
03391                         link_orig = link;
03392                         link = rb_str_subseq(link, 0, link_prefixlen);
03393                         enc = rb_enc_check(*resolvedp, link);
03394                         if (enc != linkenc) link = rb_str_conv_enc(link, linkenc, enc);
03395                         *resolvedp = link;
03396                         *prefixlenp = link_prefixlen;
03397                     }
03398                     realpath_rec(prefixlenp, resolvedp, link_names, loopcheck, strict, *unresolved_firstsep == '\0');
03399                     RB_GC_GUARD(link_orig);
03400                     rb_hash_aset(loopcheck, testpath, rb_str_dup_frozen(*resolvedp));
03401                 }
03402                 else
03403 #endif
03404                 {
03405                     VALUE s = rb_str_dup_frozen(testpath);
03406                     rb_hash_aset(loopcheck, s, s);
03407                     *resolvedp = testpath;
03408                 }
03409             }
03410         }
03411     }
03412 }
03413 
03414 VALUE
03415 rb_realpath_internal(VALUE basedir, VALUE path, int strict)
03416 {
03417     long prefixlen;
03418     VALUE resolved;
03419     volatile VALUE unresolved_path;
03420     VALUE loopcheck;
03421     volatile VALUE curdir = Qnil;
03422 
03423     rb_encoding *enc;
03424     char *path_names = NULL, *basedir_names = NULL, *curdir_names = NULL;
03425     char *ptr, *prefixptr = NULL, *pend;
03426     long len;
03427 
03428     rb_secure(2);
03429 
03430     FilePathValue(path);
03431     unresolved_path = rb_str_dup_frozen(path);
03432 
03433     if (!NIL_P(basedir)) {
03434         FilePathValue(basedir);
03435         basedir = rb_str_dup_frozen(basedir);
03436     }
03437 
03438     RSTRING_GETMEM(unresolved_path, ptr, len);
03439     path_names = skipprefixroot(ptr, ptr + len, rb_enc_get(unresolved_path));
03440     if (ptr != path_names) {
03441         resolved = rb_str_subseq(unresolved_path, 0, path_names - ptr);
03442         goto root_found;
03443     }
03444 
03445     if (!NIL_P(basedir)) {
03446         RSTRING_GETMEM(basedir, ptr, len);
03447         basedir_names = skipprefixroot(ptr, ptr + len, rb_enc_get(basedir));
03448         if (ptr != basedir_names) {
03449             resolved = rb_str_subseq(basedir, 0, basedir_names - ptr);
03450             goto root_found;
03451         }
03452     }
03453 
03454     curdir = rb_dir_getwd();
03455     RSTRING_GETMEM(curdir, ptr, len);
03456     curdir_names = skipprefixroot(ptr, ptr + len, rb_enc_get(curdir));
03457     resolved = rb_str_subseq(curdir, 0, curdir_names - ptr);
03458 
03459   root_found:
03460     RSTRING_GETMEM(resolved, prefixptr, prefixlen);
03461     pend = prefixptr + prefixlen;
03462     enc = rb_enc_get(resolved);
03463     ptr = chompdirsep(prefixptr, pend, enc);
03464     if (ptr < pend) {
03465         prefixlen = ++ptr - prefixptr;
03466         rb_str_set_len(resolved, prefixlen);
03467     }
03468 #ifdef FILE_ALT_SEPARATOR
03469     while (prefixptr < ptr) {
03470         if (*prefixptr == FILE_ALT_SEPARATOR) {
03471             *prefixptr = '/';
03472         }
03473         Inc(prefixptr, pend, enc);
03474     }
03475 #endif
03476 
03477     loopcheck = rb_hash_new();
03478     if (curdir_names)
03479         realpath_rec(&prefixlen, &resolved, curdir_names, loopcheck, 1, 0);
03480     if (basedir_names)
03481         realpath_rec(&prefixlen, &resolved, basedir_names, loopcheck, 1, 0);
03482     realpath_rec(&prefixlen, &resolved, path_names, loopcheck, strict, 1);
03483 
03484     OBJ_TAINT(resolved);
03485     return resolved;
03486 }
03487 
03488 /*
03489  * call-seq:
03490  *     File.realpath(pathname [, dir_string])  ->  real_pathname
03491  *
03492  *  Returns the real (absolute) pathname of _pathname_ in the actual
03493  *  filesystem not containing symlinks or useless dots.
03494  *
03495  *  If _dir_string_ is given, it is used as a base directory
03496  *  for interpreting relative pathname instead of the current directory.
03497  *
03498  *  All components of the pathname must exist when this method is
03499  *  called.
03500  */
03501 static VALUE
03502 rb_file_s_realpath(int argc, VALUE *argv, VALUE klass)
03503 {
03504     VALUE path, basedir;
03505     rb_scan_args(argc, argv, "11", &path, &basedir);
03506     return rb_realpath_internal(basedir, path, 1);
03507 }
03508 
03509 /*
03510  * call-seq:
03511  *     File.realdirpath(pathname [, dir_string])  ->  real_pathname
03512  *
03513  *  Returns the real (absolute) pathname of _pathname_ in the actual filesystem.
03514  *  The real pathname doesn't contain symlinks or useless dots.
03515  *
03516  *  If _dir_string_ is given, it is used as a base directory
03517  *  for interpreting relative pathname instead of the current directory.
03518  *
03519  *  The last component of the real pathname can be nonexistent.
03520  */
03521 static VALUE
03522 rb_file_s_realdirpath(int argc, VALUE *argv, VALUE klass)
03523 {
03524     VALUE path, basedir;
03525     rb_scan_args(argc, argv, "11", &path, &basedir);
03526     return rb_realpath_internal(basedir, path, 0);
03527 }
03528 
03529 static size_t
03530 rmext(const char *p, long l0, long l1, const char *e, long l2, rb_encoding *enc)
03531 {
03532     int len1, len2;
03533     unsigned int c;
03534     const char *s, *last;
03535 
03536     if (!e || !l2) return 0;
03537 
03538     c = rb_enc_codepoint_len(e, e + l2, &len1, enc);
03539     if (rb_enc_ascget(e + len1, e + l2, &len2, enc) == '*' && len1 + len2 == l2) {
03540         if (c == '.') return l0;
03541         s = p;
03542         e = p + l1;
03543         last = e;
03544         while (s < e) {
03545             if (rb_enc_codepoint_len(s, e, &len1, enc) == c) last = s;
03546             s += len1;
03547         }
03548         return last - p;
03549     }
03550     if (l1 < l2) return l1;
03551 
03552     s = p+l1-l2;
03553     if (rb_enc_left_char_head(p, s, p+l1, enc) != s) return 0;
03554 #if CASEFOLD_FILESYSTEM
03555 #define fncomp strncasecmp
03556 #else
03557 #define fncomp strncmp
03558 #endif
03559     if (fncomp(s, e, l2) == 0) {
03560         return l1-l2;
03561     }
03562     return 0;
03563 }
03564 
03565 const char *
03566 ruby_enc_find_basename(const char *name, long *baselen, long *alllen, rb_encoding *enc)
03567 {
03568     const char *p, *q, *e, *end;
03569 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
03570     const char *root;
03571 #endif
03572     long f = 0, n = -1;
03573 
03574     end = name + *alllen;
03575     name = skipprefix(name, end, enc);
03576 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
03577     root = name;
03578 #endif
03579     while (isdirsep(*name))
03580         name++;
03581     if (!*name) {
03582         p = name - 1;
03583         f = 1;
03584 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
03585         if (name != root) {
03586             /* has slashes */
03587         }
03588 #ifdef DOSISH_DRIVE_LETTER
03589         else if (*p == ':') {
03590             p++;
03591             f = 0;
03592         }
03593 #endif
03594 #ifdef DOSISH_UNC
03595         else {
03596             p = "/";
03597         }
03598 #endif
03599 #endif
03600     }
03601     else {
03602         if (!(p = strrdirsep(name, end, enc))) {
03603             p = name;
03604         }
03605         else {
03606             while (isdirsep(*p)) p++; /* skip last / */
03607         }
03608 #if USE_NTFS
03609         n = ntfs_tail(p, end, enc) - p;
03610 #else
03611         n = chompdirsep(p, end, enc) - p;
03612 #endif
03613         for (q = p; q - p < n && *q == '.'; q++);
03614         for (e = 0; q - p < n; Inc(q, end, enc)) {
03615             if (*q == '.') e = q;
03616         }
03617         if (e) f = e - p;
03618         else f = n;
03619     }
03620 
03621     if (baselen)
03622         *baselen = f;
03623     if (alllen)
03624         *alllen = n;
03625     return p;
03626 }
03627 
03628 /*
03629  *  call-seq:
03630  *     File.basename(file_name [, suffix] )  ->  base_name
03631  *
03632  *  Returns the last component of the filename given in <i>file_name</i>,
03633  *  which must be formed using forward slashes (``<code>/</code>'')
03634  *  regardless of the separator used on the local file system. If
03635  *  <i>suffix</i> is given and present at the end of <i>file_name</i>,
03636  *  it is removed.
03637  *
03638  *     File.basename("/home/gumby/work/ruby.rb")          #=> "ruby.rb"
03639  *     File.basename("/home/gumby/work/ruby.rb", ".rb")   #=> "ruby"
03640  */
03641 
03642 static VALUE
03643 rb_file_s_basename(int argc, VALUE *argv)
03644 {
03645     VALUE fname, fext, basename;
03646     const char *name, *p;
03647     long f, n;
03648     rb_encoding *enc;
03649 
03650     if (rb_scan_args(argc, argv, "11", &fname, &fext) == 2) {
03651         rb_encoding *enc;
03652         StringValue(fext);
03653         if (!rb_enc_asciicompat(enc = rb_enc_get(fext))) {
03654             rb_raise(rb_eEncCompatError, "ascii incompatible character encodings: %s",
03655                      rb_enc_name(enc));
03656         }
03657     }
03658     FilePathStringValue(fname);
03659     if (!NIL_P(fext)) enc = rb_enc_check(fname, fext);
03660     else enc = rb_enc_get(fname);
03661     if ((n = RSTRING_LEN(fname)) == 0 || !*(name = RSTRING_PTR(fname)))
03662         return rb_str_new_shared(fname);
03663 
03664     p = ruby_enc_find_basename(name, &f, &n, enc);
03665     if (n >= 0) {
03666         if (NIL_P(fext)) {
03667             f = n;
03668         }
03669         else {
03670             rb_encoding *fenc = rb_enc_get(fext);
03671             const char *fp;
03672             if (enc != fenc &&
03673                 rb_enc_str_coderange(fext) != ENC_CODERANGE_7BIT) {
03674                 fext = rb_str_conv_enc(fext, fenc, enc);
03675             }
03676             fp = StringValueCStr(fext);
03677             if (!(f = rmext(p, f, n, fp, RSTRING_LEN(fext), enc))) {
03678                 f = n;
03679             }
03680             RB_GC_GUARD(fext);
03681         }
03682         if (f == RSTRING_LEN(fname)) return rb_str_new_shared(fname);
03683     }
03684 
03685     basename = rb_str_new(p, f);
03686     rb_enc_copy(basename, fname);
03687     OBJ_INFECT(basename, fname);
03688     return basename;
03689 }
03690 
03691 /*
03692  *  call-seq:
03693  *     File.dirname(file_name )  ->  dir_name
03694  *
03695  *  Returns all components of the filename given in <i>file_name</i>
03696  *  except the last one. The filename must be formed using forward
03697  *  slashes (``<code>/</code>'') regardless of the separator used on the
03698  *  local file system.
03699  *
03700  *     File.dirname("/home/gumby/work/ruby.rb")   #=> "/home/gumby/work"
03701  */
03702 
03703 static VALUE
03704 rb_file_s_dirname(VALUE klass, VALUE fname)
03705 {
03706     return rb_file_dirname(fname);
03707 }
03708 
03709 VALUE
03710 rb_file_dirname(VALUE fname)
03711 {
03712     const char *name, *root, *p, *end;
03713     VALUE dirname;
03714     rb_encoding *enc;
03715 
03716     FilePathStringValue(fname);
03717     name = StringValueCStr(fname);
03718     end = name + RSTRING_LEN(fname);
03719     enc = rb_enc_get(fname);
03720     root = skiproot(name, end, enc);
03721 #ifdef DOSISH_UNC
03722     if (root > name + 1 && isdirsep(*name))
03723         root = skipprefix(name = root - 2, end, enc);
03724 #else
03725     if (root > name + 1)
03726         name = root - 1;
03727 #endif
03728     p = strrdirsep(root, end, enc);
03729     if (!p) {
03730         p = root;
03731     }
03732     if (p == name)
03733         return rb_usascii_str_new2(".");
03734 #ifdef DOSISH_DRIVE_LETTER
03735     if (has_drive_letter(name) && isdirsep(*(name + 2))) {
03736         const char *top = skiproot(name + 2, end, enc);
03737         dirname = rb_str_new(name, 3);
03738         rb_str_cat(dirname, top, p - top);
03739     }
03740     else
03741 #endif
03742     dirname = rb_str_new(name, p - name);
03743 #ifdef DOSISH_DRIVE_LETTER
03744     if (has_drive_letter(name) && root == name + 2 && p - name == 2)
03745         rb_str_cat(dirname, ".", 1);
03746 #endif
03747     rb_enc_copy(dirname, fname);
03748     OBJ_INFECT(dirname, fname);
03749     return dirname;
03750 }
03751 
03752 /*
03753  * accept a String, and return the pointer of the extension.
03754  * if len is passed, set the length of extension to it.
03755  * returned pointer is in ``name'' or NULL.
03756  *                 returns   *len
03757  *   no dot        NULL      0
03758  *   dotfile       top       0
03759  *   end with dot  dot       1
03760  *   .ext          dot       len of .ext
03761  *   .ext:stream   dot       len of .ext without :stream (NT only)
03762  *
03763  */
03764 const char *
03765 ruby_enc_find_extname(const char *name, long *len, rb_encoding *enc)
03766 {
03767     const char *p, *e, *end = name + (len ? *len : (long)strlen(name));
03768 
03769     p = strrdirsep(name, end, enc);     /* get the last path component */
03770     if (!p)
03771         p = name;
03772     else
03773         do name = ++p; while (isdirsep(*p));
03774 
03775     e = 0;
03776     while (*p && *p == '.') p++;
03777     while (*p) {
03778         if (*p == '.' || istrailinggarbage(*p)) {
03779 #if USE_NTFS
03780             const char *last = p++, *dot = last;
03781             while (istrailinggarbage(*p)) {
03782                 if (*p == '.') dot = p;
03783                 p++;
03784             }
03785             if (!*p || *p == ':') {
03786                 p = last;
03787                 break;
03788             }
03789             if (*last == '.' || dot > last) e = dot;
03790             continue;
03791 #else
03792             e = p;        /* get the last dot of the last component */
03793 #endif
03794         }
03795 #if USE_NTFS
03796         else if (*p == ':') {
03797             break;
03798         }
03799 #endif
03800         else if (isdirsep(*p))
03801             break;
03802         Inc(p, end, enc);
03803     }
03804 
03805     if (len) {
03806         /* no dot, or the only dot is first or end? */
03807         if (!e || e == name)
03808             *len = 0;
03809         else if (e+1 == p)
03810             *len = 1;
03811         else
03812             *len = p - e;
03813     }
03814     return e;
03815 }
03816 
03817 /*
03818  *  call-seq:
03819  *     File.extname(path)  ->  string
03820  *
03821  *  Returns the extension (the portion of file name in <i>path</i>
03822  *  after the period).
03823  *
03824  *     File.extname("test.rb")         #=> ".rb"
03825  *     File.extname("a/b/d/test.rb")   #=> ".rb"
03826  *     File.extname("test")            #=> ""
03827  *     File.extname(".profile")        #=> ""
03828  *
03829  */
03830 
03831 static VALUE
03832 rb_file_s_extname(VALUE klass, VALUE fname)
03833 {
03834     const char *name, *e;
03835     long len;
03836     VALUE extname;
03837 
03838     FilePathStringValue(fname);
03839     name = StringValueCStr(fname);
03840     len = RSTRING_LEN(fname);
03841     e = ruby_enc_find_extname(name, &len, rb_enc_get(fname));
03842     if (len <= 1)
03843         return rb_str_new(0, 0);
03844     extname = rb_str_subseq(fname, e - name, len); /* keep the dot, too! */
03845     OBJ_INFECT(extname, fname);
03846     return extname;
03847 }
03848 
03849 /*
03850  *  call-seq:
03851  *     File.path(path)  ->  string
03852  *
03853  *  Returns the string representation of the path
03854  *
03855  *     File.path("/dev/null")          #=> "/dev/null"
03856  *     File.path(Pathname.new("/tmp")) #=> "/tmp"
03857  *
03858  */
03859 
03860 static VALUE
03861 rb_file_s_path(VALUE klass, VALUE fname)
03862 {
03863     return rb_get_path(fname);
03864 }
03865 
03866 /*
03867  *  call-seq:
03868  *     File.split(file_name)   -> array
03869  *
03870  *  Splits the given string into a directory and a file component and
03871  *  returns them in a two-element array. See also
03872  *  <code>File::dirname</code> and <code>File::basename</code>.
03873  *
03874  *     File.split("/home/gumby/.profile")   #=> ["/home/gumby", ".profile"]
03875  */
03876 
03877 static VALUE
03878 rb_file_s_split(VALUE klass, VALUE path)
03879 {
03880     FilePathStringValue(path);          /* get rid of converting twice */
03881     return rb_assoc_new(rb_file_s_dirname(Qnil, path), rb_file_s_basename(1,&path));
03882 }
03883 
03884 static VALUE separator;
03885 
03886 static VALUE rb_file_join(VALUE ary, VALUE sep);
03887 
03888 static VALUE
03889 file_inspect_join(VALUE ary, VALUE argp, int recur)
03890 {
03891     VALUE *arg = (VALUE *)argp;
03892     if (recur || ary == arg[0]) rb_raise(rb_eArgError, "recursive array");
03893     return rb_file_join(arg[0], arg[1]);
03894 }
03895 
03896 static VALUE
03897 rb_file_join(VALUE ary, VALUE sep)
03898 {
03899     long len, i;
03900     VALUE result, tmp;
03901     const char *name, *tail;
03902 
03903     if (RARRAY_LEN(ary) == 0) return rb_str_new(0, 0);
03904 
03905     len = 1;
03906     for (i=0; i<RARRAY_LEN(ary); i++) {
03907         tmp = RARRAY_PTR(ary)[i];
03908         if (RB_TYPE_P(tmp, T_STRING)) {
03909             len += RSTRING_LEN(tmp);
03910         }
03911         else {
03912             len += 10;
03913         }
03914     }
03915     if (!NIL_P(sep)) {
03916         StringValue(sep);
03917         len += RSTRING_LEN(sep) * RARRAY_LEN(ary) - 1;
03918     }
03919     result = rb_str_buf_new(len);
03920     OBJ_INFECT(result, ary);
03921     for (i=0; i<RARRAY_LEN(ary); i++) {
03922         tmp = RARRAY_PTR(ary)[i];
03923         switch (TYPE(tmp)) {
03924           case T_STRING:
03925             break;
03926           case T_ARRAY:
03927             if (ary == tmp) {
03928                 rb_raise(rb_eArgError, "recursive array");
03929             }
03930             else {
03931                 VALUE args[2];
03932 
03933                 args[0] = tmp;
03934                 args[1] = sep;
03935                 tmp = rb_exec_recursive(file_inspect_join, ary, (VALUE)args);
03936             }
03937             break;
03938           default:
03939             FilePathStringValue(tmp);
03940         }
03941         name = StringValueCStr(result);
03942         len = RSTRING_LEN(result);
03943         if (i == 0) {
03944             rb_enc_copy(result, tmp);
03945         }
03946         else if (!NIL_P(sep)) {
03947             tail = chompdirsep(name, name + len, rb_enc_get(result));
03948             if (RSTRING_PTR(tmp) && isdirsep(RSTRING_PTR(tmp)[0])) {
03949                 rb_str_set_len(result, tail - name);
03950             }
03951             else if (!*tail) {
03952                 rb_str_buf_append(result, sep);
03953             }
03954         }
03955         rb_str_buf_append(result, tmp);
03956     }
03957 
03958     return result;
03959 }
03960 
03961 /*
03962  *  call-seq:
03963  *     File.join(string, ...)  ->  path
03964  *
03965  *  Returns a new string formed by joining the strings using
03966  *  <code>File::SEPARATOR</code>.
03967  *
03968  *     File.join("usr", "mail", "gumby")   #=> "usr/mail/gumby"
03969  *
03970  */
03971 
03972 static VALUE
03973 rb_file_s_join(VALUE klass, VALUE args)
03974 {
03975     return rb_file_join(args, separator);
03976 }
03977 
03978 #if defined(HAVE_TRUNCATE) || defined(HAVE_CHSIZE)
03979 /*
03980  *  call-seq:
03981  *     File.truncate(file_name, integer)  -> 0
03982  *
03983  *  Truncates the file <i>file_name</i> to be at most <i>integer</i>
03984  *  bytes long. Not available on all platforms.
03985  *
03986  *     f = File.new("out", "w")
03987  *     f.write("1234567890")     #=> 10
03988  *     f.close                   #=> nil
03989  *     File.truncate("out", 5)   #=> 0
03990  *     File.size("out")          #=> 5
03991  *
03992  */
03993 
03994 static VALUE
03995 rb_file_s_truncate(VALUE klass, VALUE path, VALUE len)
03996 {
03997     off_t pos;
03998 
03999     rb_secure(2);
04000     pos = NUM2OFFT(len);
04001     FilePathValue(path);
04002     path = rb_str_encode_ospath(path);
04003 #ifdef HAVE_TRUNCATE
04004     if (truncate(StringValueCStr(path), pos) < 0)
04005         rb_sys_fail_path(path);
04006 #else /* defined(HAVE_CHSIZE) */
04007     {
04008         int tmpfd;
04009 
04010         if ((tmpfd = open(StringValueCStr(path), 0)) < 0) {
04011             rb_sys_fail_path(path);
04012         }
04013         rb_update_max_fd(tmpfd);
04014         if (chsize(tmpfd, pos) < 0) {
04015             close(tmpfd);
04016             rb_sys_fail_path(path);
04017         }
04018         close(tmpfd);
04019     }
04020 #endif
04021     return INT2FIX(0);
04022 }
04023 #else
04024 #define rb_file_s_truncate rb_f_notimplement
04025 #endif
04026 
04027 #if defined(HAVE_FTRUNCATE) || defined(HAVE_CHSIZE)
04028 /*
04029  *  call-seq:
04030  *     file.truncate(integer)    -> 0
04031  *
04032  *  Truncates <i>file</i> to at most <i>integer</i> bytes. The file
04033  *  must be opened for writing. Not available on all platforms.
04034  *
04035  *     f = File.new("out", "w")
04036  *     f.syswrite("1234567890")   #=> 10
04037  *     f.truncate(5)              #=> 0
04038  *     f.close()                  #=> nil
04039  *     File.size("out")           #=> 5
04040  */
04041 
04042 static VALUE
04043 rb_file_truncate(VALUE obj, VALUE len)
04044 {
04045     rb_io_t *fptr;
04046     off_t pos;
04047 
04048     rb_secure(2);
04049     pos = NUM2OFFT(len);
04050     GetOpenFile(obj, fptr);
04051     if (!(fptr->mode & FMODE_WRITABLE)) {
04052         rb_raise(rb_eIOError, "not opened for writing");
04053     }
04054     rb_io_flush(obj);
04055 #ifdef HAVE_FTRUNCATE
04056     if (ftruncate(fptr->fd, pos) < 0)
04057         rb_sys_fail_path(fptr->pathv);
04058 #else /* defined(HAVE_CHSIZE) */
04059     if (chsize(fptr->fd, pos) < 0)
04060         rb_sys_fail_path(fptr->pathv);
04061 #endif
04062     return INT2FIX(0);
04063 }
04064 #else
04065 #define rb_file_truncate rb_f_notimplement
04066 #endif
04067 
04068 # ifndef LOCK_SH
04069 #  define LOCK_SH 1
04070 # endif
04071 # ifndef LOCK_EX
04072 #  define LOCK_EX 2
04073 # endif
04074 # ifndef LOCK_NB
04075 #  define LOCK_NB 4
04076 # endif
04077 # ifndef LOCK_UN
04078 #  define LOCK_UN 8
04079 # endif
04080 
04081 #ifdef __CYGWIN__
04082 #include <winerror.h>
04083 extern unsigned long __attribute__((stdcall)) GetLastError(void);
04084 #endif
04085 
04086 static VALUE
04087 rb_thread_flock(void *data)
04088 {
04089 #ifdef __CYGWIN__
04090     int old_errno = errno;
04091 #endif
04092     int *op = data, ret = flock(op[0], op[1]);
04093 
04094 #ifdef __CYGWIN__
04095     if (GetLastError() == ERROR_NOT_LOCKED) {
04096         ret = 0;
04097         errno = old_errno;
04098     }
04099 #endif
04100     return (VALUE)ret;
04101 }
04102 
04103 /*
04104  *  call-seq:
04105  *     file.flock (locking_constant )-> 0 or false
04106  *
04107  *  Locks or unlocks a file according to <i>locking_constant</i> (a
04108  *  logical <em>or</em> of the values in the table below).
04109  *  Returns <code>false</code> if <code>File::LOCK_NB</code> is
04110  *  specified and the operation would otherwise have blocked. Not
04111  *  available on all platforms.
04112  *
04113  *  Locking constants (in class File):
04114  *
04115  *     LOCK_EX   | Exclusive lock. Only one process may hold an
04116  *               | exclusive lock for a given file at a time.
04117  *     ----------+------------------------------------------------
04118  *     LOCK_NB   | Don't block when locking. May be combined
04119  *               | with other lock options using logical or.
04120  *     ----------+------------------------------------------------
04121  *     LOCK_SH   | Shared lock. Multiple processes may each hold a
04122  *               | shared lock for a given file at the same time.
04123  *     ----------+------------------------------------------------
04124  *     LOCK_UN   | Unlock.
04125  *
04126  *  Example:
04127  *
04128  *     # update a counter using write lock
04129  *     # don't use "w" because it truncates the file before lock.
04130  *     File.open("counter", File::RDWR|File::CREAT, 0644) {|f|
04131  *       f.flock(File::LOCK_EX)
04132  *       value = f.read.to_i + 1
04133  *       f.rewind
04134  *       f.write("#{value}\n")
04135  *       f.flush
04136  *       f.truncate(f.pos)
04137  *     }
04138  *
04139  *     # read the counter using read lock
04140  *     File.open("counter", "r") {|f|
04141  *       f.flock(File::LOCK_SH)
04142  *       p f.read
04143  *     }
04144  *
04145  */
04146 
04147 static VALUE
04148 rb_file_flock(VALUE obj, VALUE operation)
04149 {
04150     rb_io_t *fptr;
04151     int op[2], op1;
04152 
04153     rb_secure(2);
04154     op[1] = op1 = NUM2INT(operation);
04155     GetOpenFile(obj, fptr);
04156     op[0] = fptr->fd;
04157 
04158     if (fptr->mode & FMODE_WRITABLE) {
04159         rb_io_flush(obj);
04160     }
04161     while ((int)rb_thread_io_blocking_region(rb_thread_flock, op, fptr->fd) < 0) {
04162         switch (errno) {
04163           case EAGAIN:
04164           case EACCES:
04165 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
04166           case EWOULDBLOCK:
04167 #endif
04168             if (op1 & LOCK_NB) return Qfalse;
04169             rb_thread_polling();
04170             rb_io_check_closed(fptr);
04171             continue;
04172 
04173           case EINTR:
04174 #if defined(ERESTART)
04175           case ERESTART:
04176 #endif
04177             break;
04178 
04179           default:
04180             rb_sys_fail_path(fptr->pathv);
04181         }
04182     }
04183     return INT2FIX(0);
04184 }
04185 #undef flock
04186 
04187 static void
04188 test_check(int n, int argc, VALUE *argv)
04189 {
04190     int i;
04191 
04192     rb_secure(2);
04193     n+=1;
04194     if (n != argc) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, n);
04195     for (i=1; i<n; i++) {
04196         switch (TYPE(argv[i])) {
04197           case T_STRING:
04198           default:
04199             FilePathValue(argv[i]);
04200             break;
04201           case T_FILE:
04202             break;
04203         }
04204     }
04205 }
04206 
04207 #define CHECK(n) test_check((n), argc, argv)
04208 
04209 /*
04210  *  call-seq:
04211  *     test(int_cmd, file1 [, file2] ) -> obj
04212  *
04213  *  Uses the integer <i>aCmd</i> to perform various tests on
04214  *  <i>file1</i> (first table below) or on <i>file1</i> and
04215  *  <i>file2</i> (second table).
04216  *
04217  *  File tests on a single file:
04218  *
04219  *    Test   Returns   Meaning
04220  *    "A"  | Time    | Last access time for file1
04221  *    "b"  | boolean | True if file1 is a block device
04222  *    "c"  | boolean | True if file1 is a character device
04223  *    "C"  | Time    | Last change time for file1
04224  *    "d"  | boolean | True if file1 exists and is a directory
04225  *    "e"  | boolean | True if file1 exists
04226  *    "f"  | boolean | True if file1 exists and is a regular file
04227  *    "g"  | boolean | True if file1 has the \CF{setgid} bit
04228  *         |         | set (false under NT)
04229  *    "G"  | boolean | True if file1 exists and has a group
04230  *         |         | ownership equal to the caller's group
04231  *    "k"  | boolean | True if file1 exists and has the sticky bit set
04232  *    "l"  | boolean | True if file1 exists and is a symbolic link
04233  *    "M"  | Time    | Last modification time for file1
04234  *    "o"  | boolean | True if file1 exists and is owned by
04235  *         |         | the caller's effective uid
04236  *    "O"  | boolean | True if file1 exists and is owned by
04237  *         |         | the caller's real uid
04238  *    "p"  | boolean | True if file1 exists and is a fifo
04239  *    "r"  | boolean | True if file1 is readable by the effective
04240  *         |         | uid/gid of the caller
04241  *    "R"  | boolean | True if file is readable by the real
04242  *         |         | uid/gid of the caller
04243  *    "s"  | int/nil | If file1 has nonzero size, return the size,
04244  *         |         | otherwise return nil
04245  *    "S"  | boolean | True if file1 exists and is a socket
04246  *    "u"  | boolean | True if file1 has the setuid bit set
04247  *    "w"  | boolean | True if file1 exists and is writable by
04248  *         |         | the effective uid/gid
04249  *    "W"  | boolean | True if file1 exists and is writable by
04250  *         |         | the real uid/gid
04251  *    "x"  | boolean | True if file1 exists and is executable by
04252  *         |         | the effective uid/gid
04253  *    "X"  | boolean | True if file1 exists and is executable by
04254  *         |         | the real uid/gid
04255  *    "z"  | boolean | True if file1 exists and has a zero length
04256  *
04257  * Tests that take two files:
04258  *
04259  *    "-"  | boolean | True if file1 and file2 are identical
04260  *    "="  | boolean | True if the modification times of file1
04261  *         |         | and file2 are equal
04262  *    "<"  | boolean | True if the modification time of file1
04263  *         |         | is prior to that of file2
04264  *    ">"  | boolean | True if the modification time of file1
04265  *         |         | is after that of file2
04266  */
04267 
04268 static VALUE
04269 rb_f_test(int argc, VALUE *argv)
04270 {
04271     int cmd;
04272 
04273     if (argc == 0) rb_raise(rb_eArgError, "wrong number of arguments (0 for 2..3)");
04274     cmd = NUM2CHR(argv[0]);
04275     if (cmd == 0) goto unknown;
04276     if (strchr("bcdefgGkloOprRsSuwWxXz", cmd)) {
04277         CHECK(1);
04278         switch (cmd) {
04279           case 'b':
04280             return rb_file_blockdev_p(0, argv[1]);
04281 
04282           case 'c':
04283             return rb_file_chardev_p(0, argv[1]);
04284 
04285           case 'd':
04286             return rb_file_directory_p(0, argv[1]);
04287 
04288           case 'a':
04289           case 'e':
04290             return rb_file_exist_p(0, argv[1]);
04291 
04292           case 'f':
04293             return rb_file_file_p(0, argv[1]);
04294 
04295           case 'g':
04296             return rb_file_sgid_p(0, argv[1]);
04297 
04298           case 'G':
04299             return rb_file_grpowned_p(0, argv[1]);
04300 
04301           case 'k':
04302             return rb_file_sticky_p(0, argv[1]);
04303 
04304           case 'l':
04305             return rb_file_symlink_p(0, argv[1]);
04306 
04307           case 'o':
04308             return rb_file_owned_p(0, argv[1]);
04309 
04310           case 'O':
04311             return rb_file_rowned_p(0, argv[1]);
04312 
04313           case 'p':
04314             return rb_file_pipe_p(0, argv[1]);
04315 
04316           case 'r':
04317             return rb_file_readable_p(0, argv[1]);
04318 
04319           case 'R':
04320             return rb_file_readable_real_p(0, argv[1]);
04321 
04322           case 's':
04323             return rb_file_size_p(0, argv[1]);
04324 
04325           case 'S':
04326             return rb_file_socket_p(0, argv[1]);
04327 
04328           case 'u':
04329             return rb_file_suid_p(0, argv[1]);
04330 
04331           case 'w':
04332             return rb_file_writable_p(0, argv[1]);
04333 
04334           case 'W':
04335             return rb_file_writable_real_p(0, argv[1]);
04336 
04337           case 'x':
04338             return rb_file_executable_p(0, argv[1]);
04339 
04340           case 'X':
04341             return rb_file_executable_real_p(0, argv[1]);
04342 
04343           case 'z':
04344             return rb_file_zero_p(0, argv[1]);
04345         }
04346     }
04347 
04348     if (strchr("MAC", cmd)) {
04349         struct stat st;
04350         VALUE fname = argv[1];
04351 
04352         CHECK(1);
04353         if (rb_stat(fname, &st) == -1) {
04354             FilePathValue(fname);
04355             rb_sys_fail_path(fname);
04356         }
04357 
04358         switch (cmd) {
04359           case 'A':
04360             return stat_atime(&st);
04361           case 'M':
04362             return stat_mtime(&st);
04363           case 'C':
04364             return stat_ctime(&st);
04365         }
04366     }
04367 
04368     if (cmd == '-') {
04369         CHECK(2);
04370         return rb_file_identical_p(0, argv[1], argv[2]);
04371     }
04372 
04373     if (strchr("=<>", cmd)) {
04374         struct stat st1, st2;
04375 
04376         CHECK(2);
04377         if (rb_stat(argv[1], &st1) < 0) return Qfalse;
04378         if (rb_stat(argv[2], &st2) < 0) return Qfalse;
04379 
04380         switch (cmd) {
04381           case '=':
04382             if (st1.st_mtime == st2.st_mtime) return Qtrue;
04383             return Qfalse;
04384 
04385           case '>':
04386             if (st1.st_mtime > st2.st_mtime) return Qtrue;
04387             return Qfalse;
04388 
04389           case '<':
04390             if (st1.st_mtime < st2.st_mtime) return Qtrue;
04391             return Qfalse;
04392         }
04393     }
04394   unknown:
04395     /* unknown command */
04396     if (ISPRINT(cmd)) {
04397         rb_raise(rb_eArgError, "unknown command '%s%c'", cmd == '\'' || cmd == '\\' ? "\\" : "", cmd);
04398     }
04399     else {
04400         rb_raise(rb_eArgError, "unknown command \"\\x%02X\"", cmd);
04401     }
04402     return Qnil;                /* not reached */
04403 }
04404 
04405 
04406 /*
04407  *  Document-class: File::Stat
04408  *
04409  *  Objects of class <code>File::Stat</code> encapsulate common status
04410  *  information for <code>File</code> objects. The information is
04411  *  recorded at the moment the <code>File::Stat</code> object is
04412  *  created; changes made to the file after that point will not be
04413  *  reflected. <code>File::Stat</code> objects are returned by
04414  *  <code>IO#stat</code>, <code>File::stat</code>,
04415  *  <code>File#lstat</code>, and <code>File::lstat</code>. Many of these
04416  *  methods return platform-specific values, and not all values are
04417  *  meaningful on all systems. See also <code>Kernel#test</code>.
04418  */
04419 
04420 static VALUE
04421 rb_stat_s_alloc(VALUE klass)
04422 {
04423     return stat_new_0(klass, 0);
04424 }
04425 
04426 /*
04427  * call-seq:
04428  *
04429  *   File::Stat.new(file_name)  -> stat
04430  *
04431  * Create a File::Stat object for the given file name (raising an
04432  * exception if the file doesn't exist).
04433  */
04434 
04435 static VALUE
04436 rb_stat_init(VALUE obj, VALUE fname)
04437 {
04438     struct stat st, *nst;
04439 
04440     rb_secure(2);
04441     FilePathValue(fname);
04442     fname = rb_str_encode_ospath(fname);
04443     if (STAT(StringValueCStr(fname), &st) == -1) {
04444         rb_sys_fail_path(fname);
04445     }
04446     if (DATA_PTR(obj)) {
04447         xfree(DATA_PTR(obj));
04448         DATA_PTR(obj) = NULL;
04449     }
04450     nst = ALLOC(struct stat);
04451     *nst = st;
04452     DATA_PTR(obj) = nst;
04453 
04454     return Qnil;
04455 }
04456 
04457 /* :nodoc: */
04458 static VALUE
04459 rb_stat_init_copy(VALUE copy, VALUE orig)
04460 {
04461     struct stat *nst;
04462 
04463     if (copy == orig) return orig;
04464     rb_check_frozen(copy);
04465     /* need better argument type check */
04466     if (!rb_obj_is_instance_of(orig, rb_obj_class(copy))) {
04467         rb_raise(rb_eTypeError, "wrong argument class");
04468     }
04469     if (DATA_PTR(copy)) {
04470         xfree(DATA_PTR(copy));
04471         DATA_PTR(copy) = 0;
04472     }
04473     if (DATA_PTR(orig)) {
04474         nst = ALLOC(struct stat);
04475         *nst = *(struct stat*)DATA_PTR(orig);
04476         DATA_PTR(copy) = nst;
04477     }
04478 
04479     return copy;
04480 }
04481 
04482 /*
04483  *  call-seq:
04484  *     stat.ftype   -> string
04485  *
04486  *  Identifies the type of <i>stat</i>. The return string is one of:
04487  *  ``<code>file</code>'', ``<code>directory</code>'',
04488  *  ``<code>characterSpecial</code>'', ``<code>blockSpecial</code>'',
04489  *  ``<code>fifo</code>'', ``<code>link</code>'',
04490  *  ``<code>socket</code>'', or ``<code>unknown</code>''.
04491  *
04492  *     File.stat("/dev/tty").ftype   #=> "characterSpecial"
04493  *
04494  */
04495 
04496 static VALUE
04497 rb_stat_ftype(VALUE obj)
04498 {
04499     return rb_file_ftype(get_stat(obj));
04500 }
04501 
04502 /*
04503  *  call-seq:
04504  *     stat.directory?   -> true or false
04505  *
04506  *  Returns <code>true</code> if <i>stat</i> is a directory,
04507  *  <code>false</code> otherwise.
04508  *
04509  *     File.stat("testfile").directory?   #=> false
04510  *     File.stat(".").directory?          #=> true
04511  */
04512 
04513 static VALUE
04514 rb_stat_d(VALUE obj)
04515 {
04516     if (S_ISDIR(get_stat(obj)->st_mode)) return Qtrue;
04517     return Qfalse;
04518 }
04519 
04520 /*
04521  *  call-seq:
04522  *     stat.pipe?    -> true or false
04523  *
04524  *  Returns <code>true</code> if the operating system supports pipes and
04525  *  <i>stat</i> is a pipe; <code>false</code> otherwise.
04526  */
04527 
04528 static VALUE
04529 rb_stat_p(VALUE obj)
04530 {
04531 #ifdef S_IFIFO
04532     if (S_ISFIFO(get_stat(obj)->st_mode)) return Qtrue;
04533 
04534 #endif
04535     return Qfalse;
04536 }
04537 
04538 /*
04539  *  call-seq:
04540  *     stat.symlink?    -> true or false
04541  *
04542  *  Returns <code>true</code> if <i>stat</i> is a symbolic link,
04543  *  <code>false</code> if it isn't or if the operating system doesn't
04544  *  support this feature. As <code>File::stat</code> automatically
04545  *  follows symbolic links, <code>symlink?</code> will always be
04546  *  <code>false</code> for an object returned by
04547  *  <code>File::stat</code>.
04548  *
04549  *     File.symlink("testfile", "alink")   #=> 0
04550  *     File.stat("alink").symlink?         #=> false
04551  *     File.lstat("alink").symlink?        #=> true
04552  *
04553  */
04554 
04555 static VALUE
04556 rb_stat_l(VALUE obj)
04557 {
04558 #ifdef S_ISLNK
04559     if (S_ISLNK(get_stat(obj)->st_mode)) return Qtrue;
04560 #endif
04561     return Qfalse;
04562 }
04563 
04564 /*
04565  *  call-seq:
04566  *     stat.socket?    -> true or false
04567  *
04568  *  Returns <code>true</code> if <i>stat</i> is a socket,
04569  *  <code>false</code> if it isn't or if the operating system doesn't
04570  *  support this feature.
04571  *
04572  *     File.stat("testfile").socket?   #=> false
04573  *
04574  */
04575 
04576 static VALUE
04577 rb_stat_S(VALUE obj)
04578 {
04579 #ifdef S_ISSOCK
04580     if (S_ISSOCK(get_stat(obj)->st_mode)) return Qtrue;
04581 
04582 #endif
04583     return Qfalse;
04584 }
04585 
04586 /*
04587  *  call-seq:
04588  *     stat.blockdev?   -> true or false
04589  *
04590  *  Returns <code>true</code> if the file is a block device,
04591  *  <code>false</code> if it isn't or if the operating system doesn't
04592  *  support this feature.
04593  *
04594  *     File.stat("testfile").blockdev?    #=> false
04595  *     File.stat("/dev/hda1").blockdev?   #=> true
04596  *
04597  */
04598 
04599 static VALUE
04600 rb_stat_b(VALUE obj)
04601 {
04602 #ifdef S_ISBLK
04603     if (S_ISBLK(get_stat(obj)->st_mode)) return Qtrue;
04604 
04605 #endif
04606     return Qfalse;
04607 }
04608 
04609 /*
04610  *  call-seq:
04611  *     stat.chardev?    -> true or false
04612  *
04613  *  Returns <code>true</code> if the file is a character device,
04614  *  <code>false</code> if it isn't or if the operating system doesn't
04615  *  support this feature.
04616  *
04617  *     File.stat("/dev/tty").chardev?   #=> true
04618  *
04619  */
04620 
04621 static VALUE
04622 rb_stat_c(VALUE obj)
04623 {
04624     if (S_ISCHR(get_stat(obj)->st_mode)) return Qtrue;
04625 
04626     return Qfalse;
04627 }
04628 
04629 /*
04630  *  call-seq:
04631  *     stat.owned?    -> true or false
04632  *
04633  *  Returns <code>true</code> if the effective user id of the process is
04634  *  the same as the owner of <i>stat</i>.
04635  *
04636  *     File.stat("testfile").owned?      #=> true
04637  *     File.stat("/etc/passwd").owned?   #=> false
04638  *
04639  */
04640 
04641 static VALUE
04642 rb_stat_owned(VALUE obj)
04643 {
04644     if (get_stat(obj)->st_uid == geteuid()) return Qtrue;
04645     return Qfalse;
04646 }
04647 
04648 static VALUE
04649 rb_stat_rowned(VALUE obj)
04650 {
04651     if (get_stat(obj)->st_uid == getuid()) return Qtrue;
04652     return Qfalse;
04653 }
04654 
04655 /*
04656  *  call-seq:
04657  *     stat.grpowned?   -> true or false
04658  *
04659  *  Returns true if the effective group id of the process is the same as
04660  *  the group id of <i>stat</i>. On Windows NT, returns <code>false</code>.
04661  *
04662  *     File.stat("testfile").grpowned?      #=> true
04663  *     File.stat("/etc/passwd").grpowned?   #=> false
04664  *
04665  */
04666 
04667 static VALUE
04668 rb_stat_grpowned(VALUE obj)
04669 {
04670 #ifndef _WIN32
04671     if (rb_group_member(get_stat(obj)->st_gid)) return Qtrue;
04672 #endif
04673     return Qfalse;
04674 }
04675 
04676 /*
04677  *  call-seq:
04678  *     stat.readable?    -> true or false
04679  *
04680  *  Returns <code>true</code> if <i>stat</i> is readable by the
04681  *  effective user id of this process.
04682  *
04683  *     File.stat("testfile").readable?   #=> true
04684  *
04685  */
04686 
04687 static VALUE
04688 rb_stat_r(VALUE obj)
04689 {
04690     struct stat *st = get_stat(obj);
04691 
04692 #ifdef USE_GETEUID
04693     if (geteuid() == 0) return Qtrue;
04694 #endif
04695 #ifdef S_IRUSR
04696     if (rb_stat_owned(obj))
04697         return st->st_mode & S_IRUSR ? Qtrue : Qfalse;
04698 #endif
04699 #ifdef S_IRGRP
04700     if (rb_stat_grpowned(obj))
04701         return st->st_mode & S_IRGRP ? Qtrue : Qfalse;
04702 #endif
04703 #ifdef S_IROTH
04704     if (!(st->st_mode & S_IROTH)) return Qfalse;
04705 #endif
04706     return Qtrue;
04707 }
04708 
04709 /*
04710  *  call-seq:
04711  *     stat.readable_real?  ->  true or false
04712  *
04713  *  Returns <code>true</code> if <i>stat</i> is readable by the real
04714  *  user id of this process.
04715  *
04716  *     File.stat("testfile").readable_real?   #=> true
04717  *
04718  */
04719 
04720 static VALUE
04721 rb_stat_R(VALUE obj)
04722 {
04723     struct stat *st = get_stat(obj);
04724 
04725 #ifdef USE_GETEUID
04726     if (getuid() == 0) return Qtrue;
04727 #endif
04728 #ifdef S_IRUSR
04729     if (rb_stat_rowned(obj))
04730         return st->st_mode & S_IRUSR ? Qtrue : Qfalse;
04731 #endif
04732 #ifdef S_IRGRP
04733     if (rb_group_member(get_stat(obj)->st_gid))
04734         return st->st_mode & S_IRGRP ? Qtrue : Qfalse;
04735 #endif
04736 #ifdef S_IROTH
04737     if (!(st->st_mode & S_IROTH)) return Qfalse;
04738 #endif
04739     return Qtrue;
04740 }
04741 
04742 /*
04743  * call-seq:
04744  *    stat.world_readable? -> fixnum or nil
04745  *
04746  * If <i>stat</i> is readable by others, returns an integer
04747  * representing the file permission bits of <i>stat</i>. Returns
04748  * <code>nil</code> otherwise. The meaning of the bits is platform
04749  * dependent; on Unix systems, see <code>stat(2)</code>.
04750  *
04751  *    m = File.stat("/etc/passwd").world_readable?  #=> 420
04752  *    sprintf("%o", m)                              #=> "644"
04753  */
04754 
04755 static VALUE
04756 rb_stat_wr(VALUE obj)
04757 {
04758 #ifdef S_IROTH
04759     if ((get_stat(obj)->st_mode & (S_IROTH)) == S_IROTH) {
04760         return UINT2NUM(get_stat(obj)->st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
04761     }
04762     else {
04763         return Qnil;
04764     }
04765 #endif
04766 }
04767 
04768 /*
04769  *  call-seq:
04770  *     stat.writable?  ->  true or false
04771  *
04772  *  Returns <code>true</code> if <i>stat</i> is writable by the
04773  *  effective user id of this process.
04774  *
04775  *     File.stat("testfile").writable?   #=> true
04776  *
04777  */
04778 
04779 static VALUE
04780 rb_stat_w(VALUE obj)
04781 {
04782     struct stat *st = get_stat(obj);
04783 
04784 #ifdef USE_GETEUID
04785     if (geteuid() == 0) return Qtrue;
04786 #endif
04787 #ifdef S_IWUSR
04788     if (rb_stat_owned(obj))
04789         return st->st_mode & S_IWUSR ? Qtrue : Qfalse;
04790 #endif
04791 #ifdef S_IWGRP
04792     if (rb_stat_grpowned(obj))
04793         return st->st_mode & S_IWGRP ? Qtrue : Qfalse;
04794 #endif
04795 #ifdef S_IWOTH
04796     if (!(st->st_mode & S_IWOTH)) return Qfalse;
04797 #endif
04798     return Qtrue;
04799 }
04800 
04801 /*
04802  *  call-seq:
04803  *     stat.writable_real?  ->  true or false
04804  *
04805  *  Returns <code>true</code> if <i>stat</i> is writable by the real
04806  *  user id of this process.
04807  *
04808  *     File.stat("testfile").writable_real?   #=> true
04809  *
04810  */
04811 
04812 static VALUE
04813 rb_stat_W(VALUE obj)
04814 {
04815     struct stat *st = get_stat(obj);
04816 
04817 #ifdef USE_GETEUID
04818     if (getuid() == 0) return Qtrue;
04819 #endif
04820 #ifdef S_IWUSR
04821     if (rb_stat_rowned(obj))
04822         return st->st_mode & S_IWUSR ? Qtrue : Qfalse;
04823 #endif
04824 #ifdef S_IWGRP
04825     if (rb_group_member(get_stat(obj)->st_gid))
04826         return st->st_mode & S_IWGRP ? Qtrue : Qfalse;
04827 #endif
04828 #ifdef S_IWOTH
04829     if (!(st->st_mode & S_IWOTH)) return Qfalse;
04830 #endif
04831     return Qtrue;
04832 }
04833 
04834 /*
04835  * call-seq:
04836  *    stat.world_writable?  ->  fixnum or nil
04837  *
04838  * If <i>stat</i> is writable by others, returns an integer
04839  * representing the file permission bits of <i>stat</i>. Returns
04840  * <code>nil</code> otherwise. The meaning of the bits is platform
04841  * dependent; on Unix systems, see <code>stat(2)</code>.
04842  *
04843  *    m = File.stat("/tmp").world_writable?         #=> 511
04844  *    sprintf("%o", m)                              #=> "777"
04845  */
04846 
04847 static VALUE
04848 rb_stat_ww(VALUE obj)
04849 {
04850 #ifdef S_IROTH
04851     if ((get_stat(obj)->st_mode & (S_IWOTH)) == S_IWOTH) {
04852         return UINT2NUM(get_stat(obj)->st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
04853     }
04854     else {
04855         return Qnil;
04856     }
04857 #endif
04858 }
04859 
04860 /*
04861  *  call-seq:
04862  *     stat.executable?    -> true or false
04863  *
04864  *  Returns <code>true</code> if <i>stat</i> is executable or if the
04865  *  operating system doesn't distinguish executable files from
04866  *  nonexecutable files. The tests are made using the effective owner of
04867  *  the process.
04868  *
04869  *     File.stat("testfile").executable?   #=> false
04870  *
04871  */
04872 
04873 static VALUE
04874 rb_stat_x(VALUE obj)
04875 {
04876     struct stat *st = get_stat(obj);
04877 
04878 #ifdef USE_GETEUID
04879     if (geteuid() == 0) {
04880         return st->st_mode & S_IXUGO ? Qtrue : Qfalse;
04881     }
04882 #endif
04883 #ifdef S_IXUSR
04884     if (rb_stat_owned(obj))
04885         return st->st_mode & S_IXUSR ? Qtrue : Qfalse;
04886 #endif
04887 #ifdef S_IXGRP
04888     if (rb_stat_grpowned(obj))
04889         return st->st_mode & S_IXGRP ? Qtrue : Qfalse;
04890 #endif
04891 #ifdef S_IXOTH
04892     if (!(st->st_mode & S_IXOTH)) return Qfalse;
04893 #endif
04894     return Qtrue;
04895 }
04896 
04897 /*
04898  *  call-seq:
04899  *     stat.executable_real?    -> true or false
04900  *
04901  *  Same as <code>executable?</code>, but tests using the real owner of
04902  *  the process.
04903  */
04904 
04905 static VALUE
04906 rb_stat_X(VALUE obj)
04907 {
04908     struct stat *st = get_stat(obj);
04909 
04910 #ifdef USE_GETEUID
04911     if (getuid() == 0) {
04912         return st->st_mode & S_IXUGO ? Qtrue : Qfalse;
04913     }
04914 #endif
04915 #ifdef S_IXUSR
04916     if (rb_stat_rowned(obj))
04917         return st->st_mode & S_IXUSR ? Qtrue : Qfalse;
04918 #endif
04919 #ifdef S_IXGRP
04920     if (rb_group_member(get_stat(obj)->st_gid))
04921         return st->st_mode & S_IXGRP ? Qtrue : Qfalse;
04922 #endif
04923 #ifdef S_IXOTH
04924     if (!(st->st_mode & S_IXOTH)) return Qfalse;
04925 #endif
04926     return Qtrue;
04927 }
04928 
04929 /*
04930  *  call-seq:
04931  *     stat.file?    -> true or false
04932  *
04933  *  Returns <code>true</code> if <i>stat</i> is a regular file (not
04934  *  a device file, pipe, socket, etc.).
04935  *
04936  *     File.stat("testfile").file?   #=> true
04937  *
04938  */
04939 
04940 static VALUE
04941 rb_stat_f(VALUE obj)
04942 {
04943     if (S_ISREG(get_stat(obj)->st_mode)) return Qtrue;
04944     return Qfalse;
04945 }
04946 
04947 /*
04948  *  call-seq:
04949  *     stat.zero?    -> true or false
04950  *
04951  *  Returns <code>true</code> if <i>stat</i> is a zero-length file;
04952  *  <code>false</code> otherwise.
04953  *
04954  *     File.stat("testfile").zero?   #=> false
04955  *
04956  */
04957 
04958 static VALUE
04959 rb_stat_z(VALUE obj)
04960 {
04961     if (get_stat(obj)->st_size == 0) return Qtrue;
04962     return Qfalse;
04963 }
04964 
04965 /*
04966  *  call-seq:
04967  *     state.size    -> integer
04968  *
04969  *  Returns the size of <i>stat</i> in bytes.
04970  *
04971  *     File.stat("testfile").size   #=> 66
04972  *
04973  */
04974 
04975 static VALUE
04976 rb_stat_s(VALUE obj)
04977 {
04978     off_t size = get_stat(obj)->st_size;
04979 
04980     if (size == 0) return Qnil;
04981     return OFFT2NUM(size);
04982 }
04983 
04984 /*
04985  *  call-seq:
04986  *     stat.setuid?    -> true or false
04987  *
04988  *  Returns <code>true</code> if <i>stat</i> has the set-user-id
04989  *  permission bit set, <code>false</code> if it doesn't or if the
04990  *  operating system doesn't support this feature.
04991  *
04992  *     File.stat("/bin/su").setuid?   #=> true
04993  */
04994 
04995 static VALUE
04996 rb_stat_suid(VALUE obj)
04997 {
04998 #ifdef S_ISUID
04999     if (get_stat(obj)->st_mode & S_ISUID) return Qtrue;
05000 #endif
05001     return Qfalse;
05002 }
05003 
05004 /*
05005  *  call-seq:
05006  *     stat.setgid?   -> true or false
05007  *
05008  *  Returns <code>true</code> if <i>stat</i> has the set-group-id
05009  *  permission bit set, <code>false</code> if it doesn't or if the
05010  *  operating system doesn't support this feature.
05011  *
05012  *     File.stat("/usr/sbin/lpc").setgid?   #=> true
05013  *
05014  */
05015 
05016 static VALUE
05017 rb_stat_sgid(VALUE obj)
05018 {
05019 #ifdef S_ISGID
05020     if (get_stat(obj)->st_mode & S_ISGID) return Qtrue;
05021 #endif
05022     return Qfalse;
05023 }
05024 
05025 /*
05026  *  call-seq:
05027  *     stat.sticky?    -> true or false
05028  *
05029  *  Returns <code>true</code> if <i>stat</i> has its sticky bit set,
05030  *  <code>false</code> if it doesn't or if the operating system doesn't
05031  *  support this feature.
05032  *
05033  *     File.stat("testfile").sticky?   #=> false
05034  *
05035  */
05036 
05037 static VALUE
05038 rb_stat_sticky(VALUE obj)
05039 {
05040 #ifdef S_ISVTX
05041     if (get_stat(obj)->st_mode & S_ISVTX) return Qtrue;
05042 #endif
05043     return Qfalse;
05044 }
05045 
05046 VALUE rb_mFConst;
05047 
05048 void
05049 rb_file_const(const char *name, VALUE value)
05050 {
05051     rb_define_const(rb_mFConst, name, value);
05052 }
05053 
05054 int
05055 rb_is_absolute_path(const char *path)
05056 {
05057 #ifdef DOSISH_DRIVE_LETTER
05058     if (has_drive_letter(path) && isdirsep(path[2])) return 1;
05059 #endif
05060 #ifdef DOSISH_UNC
05061     if (isdirsep(path[0]) && isdirsep(path[1])) return 1;
05062 #endif
05063 #ifndef DOSISH
05064     if (path[0] == '/') return 1;
05065 #endif
05066     return 0;
05067 }
05068 
05069 #ifndef ENABLE_PATH_CHECK
05070 # if defined DOSISH || defined __CYGWIN__
05071 #   define ENABLE_PATH_CHECK 0
05072 # else
05073 #   define ENABLE_PATH_CHECK 1
05074 # endif
05075 #endif
05076 
05077 #if ENABLE_PATH_CHECK
05078 static int
05079 path_check_0(VALUE path, int execpath)
05080 {
05081     struct stat st;
05082     const char *p0 = StringValueCStr(path);
05083     const char *e0;
05084     rb_encoding *enc;
05085     char *p = 0, *s;
05086 
05087     if (!rb_is_absolute_path(p0)) {
05088         char *buf = my_getcwd();
05089         VALUE newpath;
05090 
05091         newpath = rb_str_new2(buf);
05092         xfree(buf);
05093 
05094         rb_str_cat2(newpath, "/");
05095         rb_str_cat2(newpath, p0);
05096         path = newpath;
05097         p0 = RSTRING_PTR(path);
05098     }
05099     e0 = p0 + RSTRING_LEN(path);
05100     enc = rb_enc_get(path);
05101     for (;;) {
05102 #ifndef S_IWOTH
05103 # define S_IWOTH 002
05104 #endif
05105         if (STAT(p0, &st) == 0 && S_ISDIR(st.st_mode) && (st.st_mode & S_IWOTH)
05106 #ifdef S_ISVTX
05107             && !(p && execpath && (st.st_mode & S_ISVTX))
05108 #endif
05109             && !access(p0, W_OK)) {
05110             rb_warn("Insecure world writable dir %s in %sPATH, mode 0%"
05111                     PRI_MODET_PREFIX"o",
05112                     p0, (execpath ? "" : "LOAD_"), st.st_mode);
05113             if (p) *p = '/';
05114             RB_GC_GUARD(path);
05115             return 0;
05116         }
05117         s = strrdirsep(p0, e0, enc);
05118         if (p) *p = '/';
05119         if (!s || s == p0) return 1;
05120         p = s;
05121         e0 = p;
05122         *p = '\0';
05123     }
05124 }
05125 #endif
05126 
05127 #if ENABLE_PATH_CHECK
05128 #define fpath_check(path) path_check_0((path), FALSE)
05129 #else
05130 #define fpath_check(path) 1
05131 #endif
05132 
05133 int
05134 rb_path_check(const char *path)
05135 {
05136 #if ENABLE_PATH_CHECK
05137     const char *p0, *p, *pend;
05138     const char sep = PATH_SEP_CHAR;
05139 
05140     if (!path) return 1;
05141 
05142     pend = path + strlen(path);
05143     p0 = path;
05144     p = strchr(path, sep);
05145     if (!p) p = pend;
05146 
05147     for (;;) {
05148         if (!path_check_0(rb_str_new(p0, p - p0), TRUE)) {
05149             return 0;           /* not safe */
05150         }
05151         p0 = p + 1;
05152         if (p0 > pend) break;
05153         p = strchr(p0, sep);
05154         if (!p) p = pend;
05155     }
05156 #endif
05157     return 1;
05158 }
05159 
05160 #ifndef _WIN32
05161 int
05162 rb_file_load_ok(const char *path)
05163 {
05164     int ret = 1;
05165     int fd = open(path, O_RDONLY);
05166     if (fd == -1) return 0;
05167     rb_update_max_fd(fd);
05168 #if !defined DOSISH
05169     {
05170         struct stat st;
05171         if (fstat(fd, &st) || !S_ISREG(st.st_mode)) {
05172             ret = 0;
05173         }
05174     }
05175 #endif
05176     (void)close(fd);
05177     return ret;
05178 }
05179 #endif
05180 
05181 static int
05182 is_explicit_relative(const char *path)
05183 {
05184     if (*path++ != '.') return 0;
05185     if (*path == '.') path++;
05186     return isdirsep(*path);
05187 }
05188 
05189 static VALUE
05190 copy_path_class(VALUE path, VALUE orig)
05191 {
05192     RBASIC(path)->klass = rb_obj_class(orig);
05193     OBJ_FREEZE(path);
05194     return path;
05195 }
05196 
05197 int
05198 rb_find_file_ext(VALUE *filep, const char *const *ext)
05199 {
05200     return rb_find_file_ext_safe(filep, ext, rb_safe_level());
05201 }
05202 
05203 int
05204 rb_find_file_ext_safe(VALUE *filep, const char *const *ext, int safe_level)
05205 {
05206     const char *f = StringValueCStr(*filep);
05207     VALUE fname = *filep, load_path, tmp;
05208     long i, j, fnlen;
05209     int expanded = 0;
05210 
05211     if (!ext[0]) return 0;
05212 
05213     if (f[0] == '~') {
05214         fname = file_expand_path_1(fname);
05215         if (safe_level >= 1 && OBJ_TAINTED(fname)) {
05216             rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
05217         }
05218         f = RSTRING_PTR(fname);
05219         *filep = fname;
05220         expanded = 1;
05221     }
05222 
05223     if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) {
05224         if (safe_level >= 1 && !fpath_check(fname)) {
05225             rb_raise(rb_eSecurityError, "loading from unsafe path %s", f);
05226         }
05227         if (!expanded) fname = file_expand_path_1(fname);
05228         fnlen = RSTRING_LEN(fname);
05229         for (i=0; ext[i]; i++) {
05230             rb_str_cat2(fname, ext[i]);
05231             if (rb_file_load_ok(RSTRING_PTR(fname))) {
05232                 *filep = copy_path_class(fname, *filep);
05233                 return (int)(i+1);
05234             }
05235             rb_str_set_len(fname, fnlen);
05236         }
05237         return 0;
05238     }
05239 
05240     if (safe_level >= 4) {
05241         rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f);
05242     }
05243 
05244     RB_GC_GUARD(load_path) = rb_get_load_path();
05245     if (!load_path) return 0;
05246 
05247     fname = rb_str_dup(*filep);
05248     RBASIC(fname)->klass = 0;
05249     fnlen = RSTRING_LEN(fname);
05250     tmp = rb_str_tmp_new(MAXPATHLEN + 2);
05251     rb_enc_associate_index(tmp, rb_usascii_encindex());
05252     for (j=0; ext[j]; j++) {
05253         rb_str_cat2(fname, ext[j]);
05254         for (i = 0; i < RARRAY_LEN(load_path); i++) {
05255             VALUE str = RARRAY_PTR(load_path)[i];
05256 
05257             RB_GC_GUARD(str) = rb_get_path_check(str, safe_level);
05258             if (RSTRING_LEN(str) == 0) continue;
05259             rb_file_expand_path_internal(fname, str, 0, 0, tmp);
05260             if (rb_file_load_ok(RSTRING_PTR(tmp))) {
05261                 *filep = copy_path_class(tmp, *filep);
05262                 return (int)(j+1);
05263             }
05264             FL_UNSET(tmp, FL_TAINT | FL_UNTRUSTED);
05265         }
05266         rb_str_set_len(fname, fnlen);
05267     }
05268     RB_GC_GUARD(load_path);
05269     return 0;
05270 }
05271 
05272 VALUE
05273 rb_find_file(VALUE path)
05274 {
05275     return rb_find_file_safe(path, rb_safe_level());
05276 }
05277 
05278 VALUE
05279 rb_find_file_safe(VALUE path, int safe_level)
05280 {
05281     VALUE tmp, load_path;
05282     const char *f = StringValueCStr(path);
05283     int expanded = 0;
05284 
05285     if (f[0] == '~') {
05286         tmp = file_expand_path_1(path);
05287         if (safe_level >= 1 && OBJ_TAINTED(tmp)) {
05288             rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
05289         }
05290         path = copy_path_class(tmp, path);
05291         f = RSTRING_PTR(path);
05292         expanded = 1;
05293     }
05294 
05295     if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) {
05296         if (safe_level >= 1 && !fpath_check(path)) {
05297             rb_raise(rb_eSecurityError, "loading from unsafe path %s", f);
05298         }
05299         if (!rb_file_load_ok(f)) return 0;
05300         if (!expanded)
05301             path = copy_path_class(file_expand_path_1(path), path);
05302         return path;
05303     }
05304 
05305     if (safe_level >= 4) {
05306         rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f);
05307     }
05308 
05309     RB_GC_GUARD(load_path) = rb_get_load_path();
05310     if (load_path) {
05311         long i;
05312 
05313         tmp = rb_str_tmp_new(MAXPATHLEN + 2);
05314         rb_enc_associate_index(tmp, rb_usascii_encindex());
05315         for (i = 0; i < RARRAY_LEN(load_path); i++) {
05316             VALUE str = RARRAY_PTR(load_path)[i];
05317             RB_GC_GUARD(str) = rb_get_path_check(str, safe_level);
05318             if (RSTRING_LEN(str) > 0) {
05319                 rb_file_expand_path_internal(path, str, 0, 0, tmp);
05320                 f = RSTRING_PTR(tmp);
05321                 if (rb_file_load_ok(f)) goto found;
05322             }
05323         }
05324         return 0;
05325     }
05326     else {
05327         return 0;               /* no path, no load */
05328     }
05329 
05330   found:
05331     if (safe_level >= 1 && !fpath_check(tmp)) {
05332         rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
05333     }
05334 
05335     return copy_path_class(tmp, path);
05336 }
05337 
05338 static void
05339 define_filetest_function(const char *name, VALUE (*func)(ANYARGS), int argc)
05340 {
05341     rb_define_module_function(rb_mFileTest, name, func, argc);
05342     rb_define_singleton_method(rb_cFile, name, func, argc);
05343 }
05344 
05345 static const char null_device[] =
05346 #if defined DOSISH
05347     "NUL"
05348 #elif defined AMIGA || defined __amigaos__
05349     "NIL"
05350 #elif defined __VMS
05351     "NL:"
05352 #else
05353     "/dev/null"
05354 #endif
05355     ;
05356 
05357 /*
05358  *  A <code>File</code> is an abstraction of any file object accessible
05359  *  by the program and is closely associated with class <code>IO</code>
05360  *  <code>File</code> includes the methods of module
05361  *  <code>FileTest</code> as class methods, allowing you to write (for
05362  *  example) <code>File.exist?("foo")</code>.
05363  *
05364  *  In the description of File methods,
05365  *  <em>permission bits</em> are a platform-specific
05366  *  set of bits that indicate permissions of a file. On Unix-based
05367  *  systems, permissions are viewed as a set of three octets, for the
05368  *  owner, the group, and the rest of the world. For each of these
05369  *  entities, permissions may be set to read, write, or execute the
05370  *  file:
05371  *
05372  *  The permission bits <code>0644</code> (in octal) would thus be
05373  *  interpreted as read/write for owner, and read-only for group and
05374  *  other. Higher-order bits may also be used to indicate the type of
05375  *  file (plain, directory, pipe, socket, and so on) and various other
05376  *  special features. If the permissions are for a directory, the
05377  *  meaning of the execute bit changes; when set the directory can be
05378  *  searched.
05379  *
05380  *  On non-Posix operating systems, there may be only the ability to
05381  *  make a file read-only or read-write. In this case, the remaining
05382  *  permission bits will be synthesized to resemble typical values. For
05383  *  instance, on Windows NT the default permission bits are
05384  *  <code>0644</code>, which means read/write for owner, read-only for
05385  *  all others. The only change that can be made is to make the file
05386  *  read-only, which is reported as <code>0444</code>.
05387  */
05388 
05389 void
05390 Init_File(void)
05391 {
05392     rb_mFileTest = rb_define_module("FileTest");
05393     rb_cFile = rb_define_class("File", rb_cIO);
05394 
05395     define_filetest_function("directory?", rb_file_directory_p, 1);
05396     define_filetest_function("exist?", rb_file_exist_p, 1);
05397     define_filetest_function("exists?", rb_file_exist_p, 1);
05398     define_filetest_function("readable?", rb_file_readable_p, 1);
05399     define_filetest_function("readable_real?", rb_file_readable_real_p, 1);
05400     define_filetest_function("world_readable?", rb_file_world_readable_p, 1);
05401     define_filetest_function("writable?", rb_file_writable_p, 1);
05402     define_filetest_function("writable_real?", rb_file_writable_real_p, 1);
05403     define_filetest_function("world_writable?", rb_file_world_writable_p, 1);
05404     define_filetest_function("executable?", rb_file_executable_p, 1);
05405     define_filetest_function("executable_real?", rb_file_executable_real_p, 1);
05406     define_filetest_function("file?", rb_file_file_p, 1);
05407     define_filetest_function("zero?", rb_file_zero_p, 1);
05408     define_filetest_function("size?", rb_file_size_p, 1);
05409     define_filetest_function("size", rb_file_s_size, 1);
05410     define_filetest_function("owned?", rb_file_owned_p, 1);
05411     define_filetest_function("grpowned?", rb_file_grpowned_p, 1);
05412 
05413     define_filetest_function("pipe?", rb_file_pipe_p, 1);
05414     define_filetest_function("symlink?", rb_file_symlink_p, 1);
05415     define_filetest_function("socket?", rb_file_socket_p, 1);
05416 
05417     define_filetest_function("blockdev?", rb_file_blockdev_p, 1);
05418     define_filetest_function("chardev?", rb_file_chardev_p, 1);
05419 
05420     define_filetest_function("setuid?", rb_file_suid_p, 1);
05421     define_filetest_function("setgid?", rb_file_sgid_p, 1);
05422     define_filetest_function("sticky?", rb_file_sticky_p, 1);
05423 
05424     define_filetest_function("identical?", rb_file_identical_p, 2);
05425 
05426     rb_define_singleton_method(rb_cFile, "stat",  rb_file_s_stat, 1);
05427     rb_define_singleton_method(rb_cFile, "lstat", rb_file_s_lstat, 1);
05428     rb_define_singleton_method(rb_cFile, "ftype", rb_file_s_ftype, 1);
05429 
05430     rb_define_singleton_method(rb_cFile, "atime", rb_file_s_atime, 1);
05431     rb_define_singleton_method(rb_cFile, "mtime", rb_file_s_mtime, 1);
05432     rb_define_singleton_method(rb_cFile, "ctime", rb_file_s_ctime, 1);
05433 
05434     rb_define_singleton_method(rb_cFile, "utime", rb_file_s_utime, -1);
05435     rb_define_singleton_method(rb_cFile, "chmod", rb_file_s_chmod, -1);
05436     rb_define_singleton_method(rb_cFile, "chown", rb_file_s_chown, -1);
05437     rb_define_singleton_method(rb_cFile, "lchmod", rb_file_s_lchmod, -1);
05438     rb_define_singleton_method(rb_cFile, "lchown", rb_file_s_lchown, -1);
05439 
05440     rb_define_singleton_method(rb_cFile, "link", rb_file_s_link, 2);
05441     rb_define_singleton_method(rb_cFile, "symlink", rb_file_s_symlink, 2);
05442     rb_define_singleton_method(rb_cFile, "readlink", rb_file_s_readlink, 1);
05443 
05444     rb_define_singleton_method(rb_cFile, "unlink", rb_file_s_unlink, -2);
05445     rb_define_singleton_method(rb_cFile, "delete", rb_file_s_unlink, -2);
05446     rb_define_singleton_method(rb_cFile, "rename", rb_file_s_rename, 2);
05447     rb_define_singleton_method(rb_cFile, "umask", rb_file_s_umask, -1);
05448     rb_define_singleton_method(rb_cFile, "truncate", rb_file_s_truncate, 2);
05449     rb_define_singleton_method(rb_cFile, "expand_path", rb_file_s_expand_path, -1);
05450     rb_define_singleton_method(rb_cFile, "absolute_path", rb_file_s_absolute_path, -1);
05451     rb_define_singleton_method(rb_cFile, "realpath", rb_file_s_realpath, -1);
05452     rb_define_singleton_method(rb_cFile, "realdirpath", rb_file_s_realdirpath, -1);
05453     rb_define_singleton_method(rb_cFile, "basename", rb_file_s_basename, -1);
05454     rb_define_singleton_method(rb_cFile, "dirname", rb_file_s_dirname, 1);
05455     rb_define_singleton_method(rb_cFile, "extname", rb_file_s_extname, 1);
05456     rb_define_singleton_method(rb_cFile, "path", rb_file_s_path, 1);
05457 
05458     separator = rb_obj_freeze(rb_usascii_str_new2("/"));
05459     rb_define_const(rb_cFile, "Separator", separator);
05460     rb_define_const(rb_cFile, "SEPARATOR", separator);
05461     rb_define_singleton_method(rb_cFile, "split",  rb_file_s_split, 1);
05462     rb_define_singleton_method(rb_cFile, "join",   rb_file_s_join, -2);
05463 
05464 #ifdef DOSISH
05465     rb_define_const(rb_cFile, "ALT_SEPARATOR", rb_obj_freeze(rb_usascii_str_new2(file_alt_separator)));
05466 #else
05467     rb_define_const(rb_cFile, "ALT_SEPARATOR", Qnil);
05468 #endif
05469     rb_define_const(rb_cFile, "PATH_SEPARATOR", rb_obj_freeze(rb_str_new2(PATH_SEP)));
05470 
05471     rb_define_method(rb_cIO, "stat",  rb_io_stat, 0); /* this is IO's method */
05472     rb_define_method(rb_cFile, "lstat",  rb_file_lstat, 0);
05473 
05474     rb_define_method(rb_cFile, "atime", rb_file_atime, 0);
05475     rb_define_method(rb_cFile, "mtime", rb_file_mtime, 0);
05476     rb_define_method(rb_cFile, "ctime", rb_file_ctime, 0);
05477     rb_define_method(rb_cFile, "size", rb_file_size, 0);
05478 
05479     rb_define_method(rb_cFile, "chmod", rb_file_chmod, 1);
05480     rb_define_method(rb_cFile, "chown", rb_file_chown, 2);
05481     rb_define_method(rb_cFile, "truncate", rb_file_truncate, 1);
05482 
05483     rb_define_method(rb_cFile, "flock", rb_file_flock, 1);
05484 
05485     rb_mFConst = rb_define_module_under(rb_cFile, "Constants");
05486     rb_include_module(rb_cIO, rb_mFConst);
05487     rb_file_const("LOCK_SH", INT2FIX(LOCK_SH));
05488     rb_file_const("LOCK_EX", INT2FIX(LOCK_EX));
05489     rb_file_const("LOCK_UN", INT2FIX(LOCK_UN));
05490     rb_file_const("LOCK_NB", INT2FIX(LOCK_NB));
05491 
05492     rb_file_const("NULL", rb_obj_freeze(rb_usascii_str_new2(null_device)));
05493 
05494     rb_define_method(rb_cFile, "path",  rb_file_path, 0);
05495     rb_define_method(rb_cFile, "to_path",  rb_file_path, 0);
05496     rb_define_global_function("test", rb_f_test, -1);
05497 
05498     rb_cStat = rb_define_class_under(rb_cFile, "Stat", rb_cObject);
05499     rb_define_alloc_func(rb_cStat,  rb_stat_s_alloc);
05500     rb_define_method(rb_cStat, "initialize", rb_stat_init, 1);
05501     rb_define_method(rb_cStat, "initialize_copy", rb_stat_init_copy, 1);
05502 
05503     rb_include_module(rb_cStat, rb_mComparable);
05504 
05505     rb_define_method(rb_cStat, "<=>", rb_stat_cmp, 1);
05506 
05507     rb_define_method(rb_cStat, "dev", rb_stat_dev, 0);
05508     rb_define_method(rb_cStat, "dev_major", rb_stat_dev_major, 0);
05509     rb_define_method(rb_cStat, "dev_minor", rb_stat_dev_minor, 0);
05510     rb_define_method(rb_cStat, "ino", rb_stat_ino, 0);
05511     rb_define_method(rb_cStat, "mode", rb_stat_mode, 0);
05512     rb_define_method(rb_cStat, "nlink", rb_stat_nlink, 0);
05513     rb_define_method(rb_cStat, "uid", rb_stat_uid, 0);
05514     rb_define_method(rb_cStat, "gid", rb_stat_gid, 0);
05515     rb_define_method(rb_cStat, "rdev", rb_stat_rdev, 0);
05516     rb_define_method(rb_cStat, "rdev_major", rb_stat_rdev_major, 0);
05517     rb_define_method(rb_cStat, "rdev_minor", rb_stat_rdev_minor, 0);
05518     rb_define_method(rb_cStat, "size", rb_stat_size, 0);
05519     rb_define_method(rb_cStat, "blksize", rb_stat_blksize, 0);
05520     rb_define_method(rb_cStat, "blocks", rb_stat_blocks, 0);
05521     rb_define_method(rb_cStat, "atime", rb_stat_atime, 0);
05522     rb_define_method(rb_cStat, "mtime", rb_stat_mtime, 0);
05523     rb_define_method(rb_cStat, "ctime", rb_stat_ctime, 0);
05524 
05525     rb_define_method(rb_cStat, "inspect", rb_stat_inspect, 0);
05526 
05527     rb_define_method(rb_cStat, "ftype", rb_stat_ftype, 0);
05528 
05529     rb_define_method(rb_cStat, "directory?",  rb_stat_d, 0);
05530     rb_define_method(rb_cStat, "readable?",  rb_stat_r, 0);
05531     rb_define_method(rb_cStat, "readable_real?",  rb_stat_R, 0);
05532     rb_define_method(rb_cStat, "world_readable?", rb_stat_wr, 0);
05533     rb_define_method(rb_cStat, "writable?",  rb_stat_w, 0);
05534     rb_define_method(rb_cStat, "writable_real?",  rb_stat_W, 0);
05535     rb_define_method(rb_cStat, "world_writable?", rb_stat_ww, 0);
05536     rb_define_method(rb_cStat, "executable?",  rb_stat_x, 0);
05537     rb_define_method(rb_cStat, "executable_real?",  rb_stat_X, 0);
05538     rb_define_method(rb_cStat, "file?",  rb_stat_f, 0);
05539     rb_define_method(rb_cStat, "zero?",  rb_stat_z, 0);
05540     rb_define_method(rb_cStat, "size?",  rb_stat_s, 0);
05541     rb_define_method(rb_cStat, "owned?",  rb_stat_owned, 0);
05542     rb_define_method(rb_cStat, "grpowned?",  rb_stat_grpowned, 0);
05543 
05544     rb_define_method(rb_cStat, "pipe?",  rb_stat_p, 0);
05545     rb_define_method(rb_cStat, "symlink?",  rb_stat_l, 0);
05546     rb_define_method(rb_cStat, "socket?",  rb_stat_S, 0);
05547 
05548     rb_define_method(rb_cStat, "blockdev?",  rb_stat_b, 0);
05549     rb_define_method(rb_cStat, "chardev?",  rb_stat_c, 0);
05550 
05551     rb_define_method(rb_cStat, "setuid?",  rb_stat_suid, 0);
05552     rb_define_method(rb_cStat, "setgid?",  rb_stat_sgid, 0);
05553     rb_define_method(rb_cStat, "sticky?",  rb_stat_sticky, 0);
05554 
05555 #ifdef _WIN32
05556     rb_w32_init_file();
05557 #endif
05558 }
05559