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