Ruby  1.9.3p286(2012-10-12revision37165)
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 /*
02434  *  call-seq:
02435  *     File.readlink(link_name)  ->  file_name
02436  *
02437  *  Returns the name of the file referenced by the given link.
02438  *  Not available on all platforms.
02439  *
02440  *     File.symlink("testfile", "link2test")   #=> 0
02441  *     File.readlink("link2test")              #=> "testfile"
02442  */
02443 
02444 static VALUE
02445 rb_file_s_readlink(VALUE klass, VALUE path)
02446 {
02447     char *buf;
02448     int size = 100;
02449     ssize_t rv;
02450     VALUE v;
02451 
02452     rb_secure(2);
02453     FilePathValue(path);
02454     path = rb_str_encode_ospath(path);
02455     buf = xmalloc(size);
02456     while ((rv = readlink(RSTRING_PTR(path), buf, size)) == size
02457 #ifdef _AIX
02458             || (rv < 0 && errno == ERANGE) /* quirky behavior of GPFS */
02459 #endif
02460         ) {
02461         size *= 2;
02462         buf = xrealloc(buf, size);
02463     }
02464     if (rv < 0) {
02465         xfree(buf);
02466         rb_sys_fail_path(path);
02467     }
02468     v = rb_filesystem_str_new(buf, rv);
02469     xfree(buf);
02470 
02471     return v;
02472 }
02473 #else
02474 #define rb_file_s_readlink rb_f_notimplement
02475 #endif
02476 
02477 static void
02478 unlink_internal(const char *path, VALUE pathv, void *arg)
02479 {
02480     if (unlink(path) < 0)
02481         rb_sys_fail_path(pathv);
02482 }
02483 
02484 /*
02485  *  call-seq:
02486  *     File.delete(file_name, ...)  -> integer
02487  *     File.unlink(file_name, ...)  -> integer
02488  *
02489  *  Deletes the named files, returning the number of names
02490  *  passed as arguments. Raises an exception on any error.
02491  *  See also <code>Dir::rmdir</code>.
02492  */
02493 
02494 static VALUE
02495 rb_file_s_unlink(VALUE klass, VALUE args)
02496 {
02497     long n;
02498 
02499     rb_secure(2);
02500     n = apply2files(unlink_internal, args, 0);
02501     return LONG2FIX(n);
02502 }
02503 
02504 /*
02505  *  call-seq:
02506  *     File.rename(old_name, new_name)   -> 0
02507  *
02508  *  Renames the given file to the new name. Raises a
02509  *  <code>SystemCallError</code> if the file cannot be renamed.
02510  *
02511  *     File.rename("afile", "afile.bak")   #=> 0
02512  */
02513 
02514 static VALUE
02515 rb_file_s_rename(VALUE klass, VALUE from, VALUE to)
02516 {
02517     const char *src, *dst;
02518     VALUE f, t;
02519 
02520     rb_secure(2);
02521     FilePathValue(from);
02522     FilePathValue(to);
02523     f = rb_str_encode_ospath(from);
02524     t = rb_str_encode_ospath(to);
02525     src = StringValueCStr(f);
02526     dst = StringValueCStr(t);
02527 #if defined __CYGWIN__
02528     errno = 0;
02529 #endif
02530     if (rename(src, dst) < 0) {
02531 #if defined DOSISH
02532         switch (errno) {
02533           case EEXIST:
02534 #if defined (__EMX__)
02535           case EACCES:
02536 #endif
02537             if (chmod(dst, 0666) == 0 &&
02538                 unlink(dst) == 0 &&
02539                 rename(src, dst) == 0)
02540                 return INT2FIX(0);
02541         }
02542 #endif
02543         sys_fail2(from, to);
02544     }
02545 
02546     return INT2FIX(0);
02547 }
02548 
02549 /*
02550  *  call-seq:
02551  *     File.umask()          -> integer
02552  *     File.umask(integer)   -> integer
02553  *
02554  *  Returns the current umask value for this process. If the optional
02555  *  argument is given, set the umask to that value and return the
02556  *  previous value. Umask values are <em>subtracted</em> from the
02557  *  default permissions, so a umask of <code>0222</code> would make a
02558  *  file read-only for everyone.
02559  *
02560  *     File.umask(0006)   #=> 18
02561  *     File.umask         #=> 6
02562  */
02563 
02564 static VALUE
02565 rb_file_s_umask(int argc, VALUE *argv)
02566 {
02567     int omask = 0;
02568 
02569     rb_secure(2);
02570     if (argc == 0) {
02571         omask = umask(0);
02572         umask(omask);
02573     }
02574     else if (argc == 1) {
02575         omask = umask(NUM2INT(argv[0]));
02576     }
02577     else {
02578         rb_raise(rb_eArgError, "wrong number of arguments (%d for 0..1)", argc);
02579     }
02580     return INT2FIX(omask);
02581 }
02582 
02583 #ifdef __CYGWIN__
02584 #undef DOSISH
02585 #endif
02586 #if defined __CYGWIN__ || defined DOSISH
02587 #define DOSISH_UNC
02588 #define DOSISH_DRIVE_LETTER
02589 #define FILE_ALT_SEPARATOR '\\'
02590 #endif
02591 #ifdef FILE_ALT_SEPARATOR
02592 #define isdirsep(x) ((x) == '/' || (x) == FILE_ALT_SEPARATOR)
02593 static const char file_alt_separator[] = {FILE_ALT_SEPARATOR, '\0'};
02594 #else
02595 #define isdirsep(x) ((x) == '/')
02596 #endif
02597 
02598 #ifndef USE_NTFS
02599 #if defined _WIN32 || defined __CYGWIN__
02600 #define USE_NTFS 1
02601 #else
02602 #define USE_NTFS 0
02603 #endif
02604 #endif
02605 
02606 #if USE_NTFS
02607 #define istrailinggarbage(x) ((x) == '.' || (x) == ' ')
02608 #else
02609 #define istrailinggarbage(x) 0
02610 #endif
02611 
02612 #ifndef CharNext                /* defined as CharNext[AW] on Windows. */
02613 # define CharNext(p) ((p) + 1)
02614 #endif
02615 
02616 #if defined(DOSISH_UNC)
02617 #define has_unc(buf) (isdirsep((buf)[0]) && isdirsep((buf)[1]))
02618 #else
02619 #define has_unc(buf) 0
02620 #endif
02621 
02622 #ifdef DOSISH_DRIVE_LETTER
02623 static inline int
02624 has_drive_letter(const char *buf)
02625 {
02626     if (ISALPHA(buf[0]) && buf[1] == ':') {
02627         return 1;
02628     }
02629     else {
02630         return 0;
02631     }
02632 }
02633 
02634 static char*
02635 getcwdofdrv(int drv)
02636 {
02637     char drive[4];
02638     char *drvcwd, *oldcwd;
02639 
02640     drive[0] = drv;
02641     drive[1] = ':';
02642     drive[2] = '\0';
02643 
02644     /* the only way that I know to get the current directory
02645        of a particular drive is to change chdir() to that drive,
02646        so save the old cwd before chdir()
02647     */
02648     oldcwd = my_getcwd();
02649     if (chdir(drive) == 0) {
02650         drvcwd = my_getcwd();
02651         chdir(oldcwd);
02652         xfree(oldcwd);
02653     }
02654     else {
02655         /* perhaps the drive is not exist. we return only drive letter */
02656         drvcwd = strdup(drive);
02657     }
02658     return drvcwd;
02659 }
02660 
02661 static inline int
02662 not_same_drive(VALUE path, int drive)
02663 {
02664     const char *p = RSTRING_PTR(path);
02665     if (RSTRING_LEN(path) < 2) return 0;
02666     if (has_drive_letter(p)) {
02667         return TOLOWER(p[0]) != TOLOWER(drive);
02668     }
02669     else {
02670         return has_unc(p);
02671     }
02672 }
02673 #endif
02674 
02675 static inline char *
02676 skiproot(const char *path)
02677 {
02678 #ifdef DOSISH_DRIVE_LETTER
02679     if (has_drive_letter(path)) path += 2;
02680 #endif
02681     while (isdirsep(*path)) path++;
02682     return (char *)path;
02683 }
02684 
02685 #define nextdirsep rb_path_next
02686 char *
02687 rb_path_next(const char *s)
02688 {
02689     while (*s && !isdirsep(*s)) {
02690         s = CharNext(s);
02691     }
02692     return (char *)s;
02693 }
02694 
02695 #if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
02696 #define skipprefix rb_path_skip_prefix
02697 #else
02698 #define skipprefix(path) (path)
02699 #endif
02700 char *
02701 rb_path_skip_prefix(const char *path)
02702 {
02703 #if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
02704 #ifdef DOSISH_UNC
02705     if (isdirsep(path[0]) && isdirsep(path[1])) {
02706         path += 2;
02707         while (isdirsep(*path)) path++;
02708         if (*(path = nextdirsep(path)) && path[1] && !isdirsep(path[1]))
02709             path = nextdirsep(path + 1);
02710         return (char *)path;
02711     }
02712 #endif
02713 #ifdef DOSISH_DRIVE_LETTER
02714     if (has_drive_letter(path))
02715         return (char *)(path + 2);
02716 #endif
02717 #endif
02718     return (char *)path;
02719 }
02720 
02721 static inline char *
02722 skipprefixroot(const char *path)
02723 {
02724 #if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
02725     char *p = skipprefix(path);
02726     while (isdirsep(*p)) p++;
02727     return p;
02728 #else
02729     return skiproot(path);
02730 #endif
02731 }
02732 
02733 #define strrdirsep rb_path_last_separator
02734 char *
02735 rb_path_last_separator(const char *path)
02736 {
02737     char *last = NULL;
02738     while (*path) {
02739         if (isdirsep(*path)) {
02740             const char *tmp = path++;
02741             while (isdirsep(*path)) path++;
02742             if (!*path) break;
02743             last = (char *)tmp;
02744         }
02745         else {
02746             path = CharNext(path);
02747         }
02748     }
02749     return last;
02750 }
02751 
02752 static char *
02753 chompdirsep(const char *path)
02754 {
02755     while (*path) {
02756         if (isdirsep(*path)) {
02757             const char *last = path++;
02758             while (isdirsep(*path)) path++;
02759             if (!*path) return (char *)last;
02760         }
02761         else {
02762             path = CharNext(path);
02763         }
02764     }
02765     return (char *)path;
02766 }
02767 
02768 char *
02769 rb_path_end(const char *path)
02770 {
02771     if (isdirsep(*path)) path++;
02772     return chompdirsep(path);
02773 }
02774 
02775 #if USE_NTFS
02776 static char *
02777 ntfs_tail(const char *path)
02778 {
02779     while (*path == '.') path++;
02780     while (*path && *path != ':') {
02781         if (istrailinggarbage(*path)) {
02782             const char *last = path++;
02783             while (istrailinggarbage(*path)) path++;
02784             if (!*path || *path == ':') return (char *)last;
02785         }
02786         else if (isdirsep(*path)) {
02787             const char *last = path++;
02788             while (isdirsep(*path)) path++;
02789             if (!*path) return (char *)last;
02790             if (*path == ':') path++;
02791         }
02792         else {
02793             path = CharNext(path);
02794         }
02795     }
02796     return (char *)path;
02797 }
02798 #endif
02799 
02800 #define BUFCHECK(cond) do {\
02801     bdiff = p - buf;\
02802     if (cond) {\
02803         do {buflen *= 2;} while (cond);\
02804         rb_str_resize(result, buflen);\
02805         buf = RSTRING_PTR(result);\
02806         p = buf + bdiff;\
02807         pend = buf + buflen;\
02808     }\
02809 } while (0)
02810 
02811 #define BUFINIT() (\
02812     p = buf = RSTRING_PTR(result),\
02813     buflen = RSTRING_LEN(result),\
02814     pend = p + buflen)
02815 
02816 VALUE
02817 rb_home_dir(const char *user, VALUE result)
02818 {
02819     const char *dir;
02820     char *buf;
02821 #if defined DOSISH || defined __CYGWIN__
02822     char *p;
02823 #endif
02824     long dirlen;
02825 
02826     if (!user || !*user) {
02827         if (!(dir = getenv("HOME"))) {
02828             rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'");
02829         }
02830         dirlen = strlen(dir);
02831         rb_str_resize(result, dirlen);
02832         memcpy(buf = RSTRING_PTR(result), dir, dirlen);
02833     }
02834     else {
02835 #ifdef HAVE_PWD_H
02836         struct passwd *pwPtr = getpwnam(user);
02837         if (!pwPtr) {
02838             endpwent();
02839             rb_raise(rb_eArgError, "user %s doesn't exist", user);
02840         }
02841         dirlen = strlen(pwPtr->pw_dir);
02842         rb_str_resize(result, dirlen);
02843         strcpy(buf = RSTRING_PTR(result), pwPtr->pw_dir);
02844         endpwent();
02845 #else
02846         return Qnil;
02847 #endif
02848     }
02849 #if defined DOSISH || defined __CYGWIN__
02850     for (p = buf; *p; p = CharNext(p)) {
02851         if (*p == '\\') {
02852             *p = '/';
02853         }
02854     }
02855 #endif
02856     rb_enc_associate_index(result, rb_filesystem_encindex());
02857     return result;
02858 }
02859 
02860 static VALUE
02861 file_expand_path(VALUE fname, VALUE dname, int abs_mode, VALUE result)
02862 {
02863     const char *s, *b;
02864     char *buf, *p, *pend, *root;
02865     size_t buflen, dirlen, bdiff;
02866     int tainted;
02867 
02868     s = StringValuePtr(fname);
02869     BUFINIT();
02870     tainted = OBJ_TAINTED(fname);
02871 
02872     if (s[0] == '~' && abs_mode == 0) {      /* execute only if NOT absolute_path() */
02873         long userlen = 0;
02874         tainted = 1;
02875         if (isdirsep(s[1]) || s[1] == '\0') {
02876             buf = 0;
02877             b = 0;
02878             rb_str_set_len(result, 0);
02879             if (*++s) ++s;
02880         }
02881         else {
02882             s = nextdirsep(b = s);
02883             userlen = s - b;
02884             BUFCHECK(bdiff + userlen >= buflen);
02885             memcpy(p, b, userlen);
02886             rb_str_set_len(result, userlen);
02887             buf = p + 1;
02888             p += userlen;
02889         }
02890         if (NIL_P(rb_home_dir(buf, result))) {
02891             rb_raise(rb_eArgError, "can't find user %s", buf);
02892         }
02893         if (!rb_is_absolute_path(RSTRING_PTR(result))) {
02894             if (userlen) {
02895                 rb_raise(rb_eArgError, "non-absolute home of %.*s", (int)userlen, b);
02896             }
02897             else {
02898                 rb_raise(rb_eArgError, "non-absolute home");
02899             }
02900         }
02901         BUFINIT();
02902         p = pend;
02903     }
02904 #ifdef DOSISH_DRIVE_LETTER
02905     /* skip drive letter */
02906     else if (has_drive_letter(s)) {
02907         if (isdirsep(s[2])) {
02908             /* specified drive letter, and full path */
02909             /* skip drive letter */
02910             BUFCHECK(bdiff + 2 >= buflen);
02911             memcpy(p, s, 2);
02912             p += 2;
02913             s += 2;
02914             rb_enc_copy(result, fname);
02915         }
02916         else {
02917             /* specified drive, but not full path */
02918             int same = 0;
02919             if (!NIL_P(dname) && !not_same_drive(dname, s[0])) {
02920                 file_expand_path(dname, Qnil, abs_mode, result);
02921                 BUFINIT();
02922                 if (has_drive_letter(p) && TOLOWER(p[0]) == TOLOWER(s[0])) {
02923                     /* ok, same drive */
02924                     same = 1;
02925                 }
02926             }
02927             if (!same) {
02928                 char *dir = getcwdofdrv(*s);
02929 
02930                 tainted = 1;
02931                 dirlen = strlen(dir);
02932                 BUFCHECK(dirlen > buflen);
02933                 strcpy(buf, dir);
02934                 xfree(dir);
02935                 rb_enc_associate_index(result, rb_filesystem_encindex());
02936             }
02937             else
02938                 rb_enc_associate(result, rb_enc_check(result, fname));
02939             p = chompdirsep(skiproot(buf));
02940             s += 2;
02941         }
02942     }
02943 #endif
02944     else if (!rb_is_absolute_path(s)) {
02945         if (!NIL_P(dname)) {
02946             file_expand_path(dname, Qnil, abs_mode, result);
02947             BUFINIT();
02948             rb_enc_associate(result, rb_enc_check(result, fname));
02949         }
02950         else {
02951             char *dir = my_getcwd();
02952 
02953             tainted = 1;
02954             dirlen = strlen(dir);
02955             BUFCHECK(dirlen > buflen);
02956             strcpy(buf, dir);
02957             xfree(dir);
02958             rb_enc_associate_index(result, rb_filesystem_encindex());
02959         }
02960 #if defined DOSISH || defined __CYGWIN__
02961         if (isdirsep(*s)) {
02962             /* specified full path, but not drive letter nor UNC */
02963             /* we need to get the drive letter or UNC share name */
02964             p = skipprefix(buf);
02965         }
02966         else
02967 #endif
02968             p = chompdirsep(skiproot(buf));
02969     }
02970     else {
02971         size_t len;
02972         b = s;
02973         do s++; while (isdirsep(*s));
02974         len = s - b;
02975         p = buf + len;
02976         BUFCHECK(bdiff >= buflen);
02977         memset(buf, '/', len);
02978         rb_str_set_len(result, len);
02979         rb_enc_associate(result, rb_enc_check(result, fname));
02980     }
02981     if (p > buf && p[-1] == '/')
02982         --p;
02983     else {
02984         rb_str_set_len(result, p-buf);
02985         BUFCHECK(bdiff + 1 >= buflen);
02986         *p = '/';
02987     }
02988 
02989     rb_str_set_len(result, p-buf+1);
02990     BUFCHECK(bdiff + 1 >= buflen);
02991     p[1] = 0;
02992     root = skipprefix(buf);
02993 
02994     b = s;
02995     while (*s) {
02996         switch (*s) {
02997           case '.':
02998             if (b == s++) {     /* beginning of path element */
02999                 switch (*s) {
03000                   case '\0':
03001                     b = s;
03002                     break;
03003                   case '.':
03004                     if (*(s+1) == '\0' || isdirsep(*(s+1))) {
03005                         /* We must go back to the parent */
03006                         char *n;
03007                         *p = '\0';
03008                         if (!(n = strrdirsep(root))) {
03009                             *p = '/';
03010                         }
03011                         else {
03012                             p = n;
03013                         }
03014                         b = ++s;
03015                     }
03016 #if USE_NTFS
03017                     else {
03018                         do ++s; while (istrailinggarbage(*s));
03019                     }
03020 #endif
03021                     break;
03022                   case '/':
03023 #if defined DOSISH || defined __CYGWIN__
03024                   case '\\':
03025 #endif
03026                     b = ++s;
03027                     break;
03028                   default:
03029                     /* ordinary path element, beginning don't move */
03030                     break;
03031                 }
03032             }
03033 #if USE_NTFS
03034             else {
03035                 --s;
03036               case ' ': {
03037                 const char *e = s;
03038                 while (istrailinggarbage(*s)) s++;
03039                 if (!*s) {
03040                     s = e;
03041                     goto endpath;
03042                 }
03043               }
03044             }
03045 #endif
03046             break;
03047           case '/':
03048 #if defined DOSISH || defined __CYGWIN__
03049           case '\\':
03050 #endif
03051             if (s > b) {
03052                 long rootdiff = root - buf;
03053                 rb_str_set_len(result, p-buf+1);
03054                 BUFCHECK(bdiff + (s-b+1) >= buflen);
03055                 root = buf + rootdiff;
03056                 memcpy(++p, b, s-b);
03057                 p += s-b;
03058                 *p = '/';
03059             }
03060             b = ++s;
03061             break;
03062           default:
03063             s = CharNext(s);
03064             break;
03065         }
03066     }
03067 
03068     if (s > b) {
03069 #if USE_NTFS
03070         static const char prime[] = ":$DATA";
03071         enum {prime_len = sizeof(prime) -1};
03072       endpath:
03073         if (s > b + prime_len && strncasecmp(s - prime_len, prime, prime_len) == 0) {
03074             /* alias of stream */
03075             /* get rid of a bug of x64 VC++ */
03076             if (*(s - (prime_len+1)) == ':') {
03077                 s -= prime_len + 1; /* prime */
03078             }
03079             else if (memchr(b, ':', s - prime_len - b)) {
03080                 s -= prime_len; /* alternative */
03081             }
03082         }
03083 #endif
03084         rb_str_set_len(result, p-buf+1);
03085         BUFCHECK(bdiff + (s-b) >= buflen);
03086         memcpy(++p, b, s-b);
03087         p += s-b;
03088     }
03089     if (p == skiproot(buf) - 1) p++;
03090 
03091 #if USE_NTFS
03092     *p = '\0';
03093     if ((s = strrdirsep(b = buf)) != 0 && !strpbrk(s, "*?")) {
03094         size_t len;
03095         WIN32_FIND_DATA wfd;
03096         HANDLE h;
03097 #ifdef __CYGWIN__
03098 #ifdef HAVE_CYGWIN_CONV_PATH
03099         char *w32buf = NULL;
03100         const int flags = CCP_POSIX_TO_WIN_A | CCP_RELATIVE;
03101 #else
03102         char w32buf[MAXPATHLEN];
03103 #endif
03104         const char *path;
03105         ssize_t bufsize;
03106         int lnk_added = 0, is_symlink = 0;
03107         struct stat st;
03108         p = (char *)s;
03109         len = strlen(p);
03110         if (lstat(buf, &st) == 0 && S_ISLNK(st.st_mode)) {
03111             is_symlink = 1;
03112             if (len > 4 && STRCASECMP(p + len - 4, ".lnk") != 0) {
03113                 lnk_added = 1;
03114             }
03115         }
03116         path = *buf ? buf : "/";
03117 #ifdef HAVE_CYGWIN_CONV_PATH
03118         bufsize = cygwin_conv_path(flags, path, NULL, 0);
03119         if (bufsize > 0) {
03120             bufsize += len;
03121             if (lnk_added) bufsize += 4;
03122             w32buf = ALLOCA_N(char, bufsize);
03123             if (cygwin_conv_path(flags, path, w32buf, bufsize) == 0) {
03124                 b = w32buf;
03125             }
03126         }
03127 #else
03128         bufsize = MAXPATHLEN;
03129         if (cygwin_conv_to_win32_path(path, w32buf) == 0) {
03130             b = w32buf;
03131         }
03132 #endif
03133         if (is_symlink && b == w32buf) {
03134             *p = '\\';
03135             strlcat(w32buf, p, bufsize);
03136             if (lnk_added) {
03137                 strlcat(w32buf, ".lnk", bufsize);
03138             }
03139         }
03140         else {
03141             lnk_added = 0;
03142         }
03143         *p = '/';
03144 #endif
03145         h = FindFirstFile(b, &wfd);
03146         if (h != INVALID_HANDLE_VALUE) {
03147             FindClose(h);
03148             len = strlen(wfd.cFileName);
03149 #ifdef __CYGWIN__
03150             if (lnk_added && len > 4 &&
03151                 STRCASECMP(wfd.cFileName + len - 4, ".lnk") == 0) {
03152                 wfd.cFileName[len -= 4] = '\0';
03153             }
03154 #else
03155             p = (char *)s;
03156 #endif
03157             ++p;
03158             BUFCHECK(bdiff + len >= buflen);
03159             memcpy(p, wfd.cFileName, len + 1);
03160             p += len;
03161         }
03162 #ifdef __CYGWIN__
03163         else {
03164             p += strlen(p);
03165         }
03166 #endif
03167     }
03168 #endif
03169 
03170     if (tainted) OBJ_TAINT(result);
03171     rb_str_set_len(result, p - buf);
03172     rb_enc_check(fname, result);
03173     ENC_CODERANGE_CLEAR(result);
03174     return result;
03175 }
03176 
03177 #define EXPAND_PATH_BUFFER() rb_usascii_str_new(0, MAXPATHLEN + 2)
03178 
03179 #define check_expand_path_args(fname, dname) \
03180     (((fname) = rb_get_path(fname)), \
03181      (void)(NIL_P(dname) ? (dname) : ((dname) = rb_get_path(dname))))
03182 
03183 static VALUE
03184 file_expand_path_1(VALUE fname)
03185 {
03186     return file_expand_path(fname, Qnil, 0, EXPAND_PATH_BUFFER());
03187 }
03188 
03189 VALUE
03190 rb_file_expand_path(VALUE fname, VALUE dname)
03191 {
03192     check_expand_path_args(fname, dname);
03193     return file_expand_path(fname, dname, 0, EXPAND_PATH_BUFFER());
03194 }
03195 
03196 /*
03197  *  call-seq:
03198  *     File.expand_path(file_name [, dir_string] )  ->  abs_file_name
03199  *
03200  *  Converts a pathname to an absolute pathname. Relative paths are
03201  *  referenced from the current working directory of the process unless
03202  *  <i>dir_string</i> is given, in which case it will be used as the
03203  *  starting point. The given pathname may start with a
03204  *  ``<code>~</code>'', which expands to the process owner's home
03205  *  directory (the environment variable <code>HOME</code> must be set
03206  *  correctly). ``<code>~</code><i>user</i>'' expands to the named
03207  *  user's home directory.
03208  *
03209  *     File.expand_path("~oracle/bin")           #=> "/home/oracle/bin"
03210  *     File.expand_path("../../bin", "/tmp/x")   #=> "/bin"
03211  */
03212 
03213 VALUE
03214 rb_file_s_expand_path(int argc, VALUE *argv)
03215 {
03216     VALUE fname, dname;
03217 
03218     if (argc == 1) {
03219         return rb_file_expand_path(argv[0], Qnil);
03220     }
03221     rb_scan_args(argc, argv, "11", &fname, &dname);
03222 
03223     return rb_file_expand_path(fname, dname);
03224 }
03225 
03226 VALUE
03227 rb_file_absolute_path(VALUE fname, VALUE dname)
03228 {
03229     check_expand_path_args(fname, dname);
03230     return file_expand_path(fname, dname, 1, EXPAND_PATH_BUFFER());
03231 }
03232 
03233 /*
03234  *  call-seq:
03235  *     File.absolute_path(file_name [, dir_string] )  ->  abs_file_name
03236  *
03237  *  Converts a pathname to an absolute pathname. Relative paths are
03238  *  referenced from the current working directory of the process unless
03239  *  <i>dir_string</i> is given, in which case it will be used as the
03240  *  starting point. If the given pathname starts with a ``<code>~</code>''
03241  *  it is NOT expanded, it is treated as a normal directory name.
03242  *
03243  *     File.absolute_path("~oracle/bin")       #=> "<relative_path>/~oracle/bin"
03244  */
03245 
03246 VALUE
03247 rb_file_s_absolute_path(int argc, VALUE *argv)
03248 {
03249     VALUE fname, dname;
03250 
03251     if (argc == 1) {
03252         return rb_file_absolute_path(argv[0], Qnil);
03253     }
03254     rb_scan_args(argc, argv, "11", &fname, &dname);
03255 
03256     return rb_file_absolute_path(fname, dname);
03257 }
03258 
03259 static void
03260 realpath_rec(long *prefixlenp, VALUE *resolvedp, char *unresolved, VALUE loopcheck, int strict, int last)
03261 {
03262     ID resolving;
03263     CONST_ID(resolving, "resolving");
03264     while (*unresolved) {
03265         char *testname = unresolved;
03266         char *unresolved_firstsep = rb_path_next(unresolved);
03267         long testnamelen = unresolved_firstsep - unresolved;
03268         char *unresolved_nextname = unresolved_firstsep;
03269         while (isdirsep(*unresolved_nextname)) unresolved_nextname++;
03270         unresolved = unresolved_nextname;
03271         if (testnamelen == 1 && testname[0] == '.') {
03272         }
03273         else if (testnamelen == 2 && testname[0] == '.' && testname[1] == '.') {
03274             if (*prefixlenp < RSTRING_LEN(*resolvedp)) {
03275                 char *resolved_names = RSTRING_PTR(*resolvedp) + *prefixlenp;
03276                 char *lastsep = rb_path_last_separator(resolved_names);
03277                 long len = lastsep ? lastsep - resolved_names : 0;
03278                 rb_str_resize(*resolvedp, *prefixlenp + len);
03279             }
03280         }
03281         else {
03282             VALUE checkval;
03283             VALUE testpath = rb_str_dup(*resolvedp);
03284             if (*prefixlenp < RSTRING_LEN(testpath))
03285                 rb_str_cat2(testpath, "/");
03286             rb_str_cat(testpath, testname, testnamelen);
03287             checkval = rb_hash_aref(loopcheck, testpath);
03288             if (!NIL_P(checkval)) {
03289                 if (checkval == ID2SYM(resolving)) {
03290                     errno = ELOOP;
03291                     rb_sys_fail_path(testpath);
03292                 }
03293                 else {
03294                     *resolvedp = rb_str_dup(checkval);
03295                 }
03296             }
03297             else {
03298                 struct stat sbuf;
03299                 int ret;
03300                 VALUE testpath2 = rb_str_encode_ospath(testpath);
03301                 ret = lstat(RSTRING_PTR(testpath2), &sbuf);
03302                 if (ret == -1) {
03303                     if (errno == ENOENT) {
03304                         if (strict || !last || *unresolved_firstsep)
03305                             rb_sys_fail_path(testpath);
03306                         *resolvedp = testpath;
03307                         break;
03308                     }
03309                     else {
03310                         rb_sys_fail_path(testpath);
03311                     }
03312                 }
03313 #ifdef HAVE_READLINK
03314                 if (S_ISLNK(sbuf.st_mode)) {
03315                     volatile VALUE link;
03316                     char *link_prefix, *link_names;
03317                     long link_prefixlen;
03318                     rb_hash_aset(loopcheck, testpath, ID2SYM(resolving));
03319                     link = rb_file_s_readlink(rb_cFile, testpath);
03320                     link_prefix = RSTRING_PTR(link);
03321                     link_names = skipprefixroot(link_prefix);
03322                     link_prefixlen = link_names - link_prefix;
03323                     if (link_prefixlen == 0) {
03324                         realpath_rec(prefixlenp, resolvedp, link_names, loopcheck, strict, *unresolved_firstsep == '\0');
03325                     }
03326                     else {
03327                         *resolvedp = rb_str_new(link_prefix, link_prefixlen);
03328                         *prefixlenp = link_prefixlen;
03329                         realpath_rec(prefixlenp, resolvedp, link_names, loopcheck, strict, *unresolved_firstsep == '\0');
03330                     }
03331                     rb_hash_aset(loopcheck, testpath, rb_str_dup_frozen(*resolvedp));
03332                 }
03333                 else
03334 #endif
03335                 {
03336                     VALUE s = rb_str_dup_frozen(testpath);
03337                     rb_hash_aset(loopcheck, s, s);
03338                     *resolvedp = testpath;
03339                 }
03340             }
03341         }
03342     }
03343 }
03344 
03345 VALUE
03346 rb_realpath_internal(VALUE basedir, VALUE path, int strict)
03347 {
03348     long prefixlen;
03349     VALUE resolved;
03350     volatile VALUE unresolved_path;
03351     VALUE loopcheck;
03352     volatile VALUE curdir = Qnil;
03353 
03354     char *path_names = NULL, *basedir_names = NULL, *curdir_names = NULL;
03355     char *ptr, *prefixptr = NULL;
03356 
03357     rb_secure(2);
03358 
03359     FilePathValue(path);
03360     unresolved_path = rb_str_dup_frozen(path);
03361 
03362     if (!NIL_P(basedir)) {
03363         FilePathValue(basedir);
03364         basedir = rb_str_dup_frozen(basedir);
03365     }
03366 
03367     ptr = RSTRING_PTR(unresolved_path);
03368     path_names = skipprefixroot(ptr);
03369     if (ptr != path_names) {
03370         resolved = rb_enc_str_new(ptr, path_names - ptr,
03371                                   rb_enc_get(unresolved_path));
03372         goto root_found;
03373     }
03374 
03375     if (!NIL_P(basedir)) {
03376         ptr = RSTRING_PTR(basedir);
03377         basedir_names = skipprefixroot(ptr);
03378         if (ptr != basedir_names) {
03379             resolved = rb_enc_str_new(ptr, basedir_names - ptr,
03380                                       rb_enc_get(basedir));
03381             goto root_found;
03382         }
03383     }
03384 
03385     curdir = rb_dir_getwd();
03386     ptr = RSTRING_PTR(curdir);
03387     curdir_names = skipprefixroot(ptr);
03388     resolved = rb_enc_str_new(ptr, curdir_names - ptr, rb_enc_get(curdir));
03389 
03390   root_found:
03391     prefixptr = RSTRING_PTR(resolved);
03392     prefixlen = RSTRING_LEN(resolved);
03393     ptr = chompdirsep(prefixptr);
03394     if (*ptr) {
03395         prefixlen = ++ptr - prefixptr;
03396         rb_str_set_len(resolved, prefixlen);
03397     }
03398 #ifdef FILE_ALT_SEPARATOR
03399     while (prefixptr < ptr) {
03400         if (*prefixptr == FILE_ALT_SEPARATOR) {
03401             *prefixptr = '/';
03402         }
03403         prefixptr = CharNext(prefixptr);
03404     }
03405 #endif
03406 
03407     loopcheck = rb_hash_new();
03408     if (curdir_names)
03409         realpath_rec(&prefixlen, &resolved, curdir_names, loopcheck, 1, 0);
03410     if (basedir_names)
03411         realpath_rec(&prefixlen, &resolved, basedir_names, loopcheck, 1, 0);
03412     realpath_rec(&prefixlen, &resolved, path_names, loopcheck, strict, 1);
03413 
03414     OBJ_TAINT(resolved);
03415     return resolved;
03416 }
03417 
03418 /*
03419  * call-seq:
03420  *     File.realpath(pathname [, dir_string])  ->  real_pathname
03421  *
03422  *  Returns the real (absolute) pathname of _pathname_ in the actual
03423  *  filesystem not containing symlinks or useless dots.
03424  *
03425  *  If _dir_string_ is given, it is used as a base directory
03426  *  for interpreting relative pathname instead of the current directory.
03427  *
03428  *  All components of the pathname must exist when this method is
03429  *  called.
03430  */
03431 static VALUE
03432 rb_file_s_realpath(int argc, VALUE *argv, VALUE klass)
03433 {
03434     VALUE path, basedir;
03435     rb_scan_args(argc, argv, "11", &path, &basedir);
03436     return rb_realpath_internal(basedir, path, 1);
03437 }
03438 
03439 /*
03440  * call-seq:
03441  *     File.realdirpath(pathname [, dir_string])  ->  real_pathname
03442  *
03443  *  Returns the real (absolute) pathname of _pathname_ in the actual filesystem.
03444  *  The real pathname doesn't contain symlinks or useless dots.
03445  *
03446  *  If _dir_string_ is given, it is used as a base directory
03447  *  for interpreting relative pathname instead of the current directory.
03448  *
03449  *  The last component of the real pathname can be nonexistent.
03450  */
03451 static VALUE
03452 rb_file_s_realdirpath(int argc, VALUE *argv, VALUE klass)
03453 {
03454     VALUE path, basedir;
03455     rb_scan_args(argc, argv, "11", &path, &basedir);
03456     return rb_realpath_internal(basedir, path, 0);
03457 }
03458 
03459 static size_t
03460 rmext(const char *p, long l0, long l1, const char *e)
03461 {
03462     long l2;
03463 
03464     if (!e) return 0;
03465 
03466     l2 = strlen(e);
03467     if (!l2) return 0;
03468     if (l2 == 2 && e[1] == '*') {
03469         unsigned char c = *e;
03470         e = p + l1;
03471         do {
03472             if (e <= p + l0) return 0;
03473         } while (*--e != c);
03474         return e - p;
03475     }
03476     if (l1 < l2) return l1;
03477 
03478 #if CASEFOLD_FILESYSTEM
03479 #define fncomp strncasecmp
03480 #else
03481 #define fncomp strncmp
03482 #endif
03483     if (fncomp(p+l1-l2, e, l2) == 0) {
03484         return l1-l2;
03485     }
03486     return 0;
03487 }
03488 
03489 const char *
03490 ruby_find_basename(const char *name, long *baselen, long *alllen)
03491 {
03492     const char *p, *q, *e;
03493 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
03494     const char *root;
03495 #endif
03496     long f = 0, n = -1;
03497 
03498     name = skipprefix(name);
03499 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
03500     root = name;
03501 #endif
03502     while (isdirsep(*name))
03503         name++;
03504     if (!*name) {
03505         p = name - 1;
03506         f = 1;
03507 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
03508         if (name != root) {
03509             /* has slashes */
03510         }
03511 #ifdef DOSISH_DRIVE_LETTER
03512         else if (*p == ':') {
03513             p++;
03514             f = 0;
03515         }
03516 #endif
03517 #ifdef DOSISH_UNC
03518         else {
03519             p = "/";
03520         }
03521 #endif
03522 #endif
03523     }
03524     else {
03525         if (!(p = strrdirsep(name))) {
03526             p = name;
03527         }
03528         else {
03529             while (isdirsep(*p)) p++; /* skip last / */
03530         }
03531 #if USE_NTFS
03532         n = ntfs_tail(p) - p;
03533 #else
03534         n = chompdirsep(p) - p;
03535 #endif
03536         for (q = p; q - p < n && *q == '.'; q++);
03537         for (e = 0; q - p < n; q = CharNext(q)) {
03538             if (*q == '.') e = q;
03539         }
03540         if (e) f = e - p;
03541         else f = n;
03542     }
03543 
03544     if (baselen)
03545         *baselen = f;
03546     if (alllen)
03547         *alllen = n;
03548     return p;
03549 }
03550 
03551 /*
03552  *  call-seq:
03553  *     File.basename(file_name [, suffix] )  ->  base_name
03554  *
03555  *  Returns the last component of the filename given in <i>file_name</i>,
03556  *  which must be formed using forward slashes (``<code>/</code>'')
03557  *  regardless of the separator used on the local file system. If
03558  *  <i>suffix</i> is given and present at the end of <i>file_name</i>,
03559  *  it is removed.
03560  *
03561  *     File.basename("/home/gumby/work/ruby.rb")          #=> "ruby.rb"
03562  *     File.basename("/home/gumby/work/ruby.rb", ".rb")   #=> "ruby"
03563  */
03564 
03565 static VALUE
03566 rb_file_s_basename(int argc, VALUE *argv)
03567 {
03568     VALUE fname, fext, basename;
03569     const char *name, *p;
03570     long f, n;
03571 
03572     if (rb_scan_args(argc, argv, "11", &fname, &fext) == 2) {
03573         rb_encoding *enc;
03574         StringValue(fext);
03575         if (!rb_enc_asciicompat(enc = rb_enc_get(fext))) {
03576             rb_raise(rb_eEncCompatError, "ascii incompatible character encodings: %s",
03577                      rb_enc_name(enc));
03578         }
03579     }
03580     FilePathStringValue(fname);
03581     if (!NIL_P(fext)) rb_enc_check(fname, fext);
03582     if (RSTRING_LEN(fname) == 0 || !*(name = RSTRING_PTR(fname)))
03583         return rb_str_new_shared(fname);
03584 
03585     p = ruby_find_basename(name, &f, &n);
03586     if (n >= 0) {
03587         if (NIL_P(fext) || !(f = rmext(p, f, n, StringValueCStr(fext)))) {
03588             f = n;
03589         }
03590         if (f == RSTRING_LEN(fname)) return rb_str_new_shared(fname);
03591     }
03592 
03593     basename = rb_str_new(p, f);
03594     rb_enc_copy(basename, fname);
03595     OBJ_INFECT(basename, fname);
03596     return basename;
03597 }
03598 
03599 /*
03600  *  call-seq:
03601  *     File.dirname(file_name )  ->  dir_name
03602  *
03603  *  Returns all components of the filename given in <i>file_name</i>
03604  *  except the last one. The filename must be formed using forward
03605  *  slashes (``<code>/</code>'') regardless of the separator used on the
03606  *  local file system.
03607  *
03608  *     File.dirname("/home/gumby/work/ruby.rb")   #=> "/home/gumby/work"
03609  */
03610 
03611 static VALUE
03612 rb_file_s_dirname(VALUE klass, VALUE fname)
03613 {
03614     return rb_file_dirname(fname);
03615 }
03616 
03617 VALUE
03618 rb_file_dirname(VALUE fname)
03619 {
03620     const char *name, *root, *p;
03621     VALUE dirname;
03622 
03623     FilePathStringValue(fname);
03624     name = StringValueCStr(fname);
03625     root = skiproot(name);
03626 #ifdef DOSISH_UNC
03627     if (root > name + 1 && isdirsep(*name))
03628         root = skipprefix(name = root - 2);
03629 #else
03630     if (root > name + 1)
03631         name = root - 1;
03632 #endif
03633     p = strrdirsep(root);
03634     if (!p) {
03635         p = root;
03636     }
03637     if (p == name)
03638         return rb_usascii_str_new2(".");
03639 #ifdef DOSISH_DRIVE_LETTER
03640     if (has_drive_letter(name) && isdirsep(*(name + 2))) {
03641         const char *top = skiproot(name + 2);
03642         dirname = rb_str_new(name, 3);
03643         rb_str_cat(dirname, top, p - top);
03644     }
03645     else
03646 #endif
03647     dirname = rb_str_new(name, p - name);
03648 #ifdef DOSISH_DRIVE_LETTER
03649     if (has_drive_letter(name) && root == name + 2 && p - name == 2)
03650         rb_str_cat(dirname, ".", 1);
03651 #endif
03652     rb_enc_copy(dirname, fname);
03653     OBJ_INFECT(dirname, fname);
03654     return dirname;
03655 }
03656 
03657 /*
03658  * accept a String, and return the pointer of the extension.
03659  * if len is passed, set the length of extension to it.
03660  * returned pointer is in ``name'' or NULL.
03661  *                 returns   *len
03662  *   no dot        NULL      0
03663  *   dotfile       top       0
03664  *   end with dot  dot       1
03665  *   .ext          dot       len of .ext
03666  *   .ext:stream   dot       len of .ext without :stream (NT only)
03667  *
03668  */
03669 const char *
03670 ruby_find_extname(const char *name, long *len)
03671 {
03672     const char *p, *e;
03673 
03674     p = strrdirsep(name);       /* get the last path component */
03675     if (!p)
03676         p = name;
03677     else
03678         do name = ++p; while (isdirsep(*p));
03679 
03680     e = 0;
03681     while (*p && *p == '.') p++;
03682     while (*p) {
03683         if (*p == '.' || istrailinggarbage(*p)) {
03684 #if USE_NTFS
03685             const char *last = p++, *dot = last;
03686             while (istrailinggarbage(*p)) {
03687                 if (*p == '.') dot = p;
03688                 p++;
03689             }
03690             if (!*p || *p == ':') {
03691                 p = last;
03692                 break;
03693             }
03694             if (*last == '.' || dot > last) e = dot;
03695             continue;
03696 #else
03697             e = p;        /* get the last dot of the last component */
03698 #endif
03699         }
03700 #if USE_NTFS
03701         else if (*p == ':') {
03702             break;
03703         }
03704 #endif
03705         else if (isdirsep(*p))
03706             break;
03707         p = CharNext(p);
03708     }
03709 
03710     if (len) {
03711         /* no dot, or the only dot is first or end? */
03712         if (!e || e == name)
03713             *len = 0;
03714         else if (e+1 == p)
03715             *len = 1;
03716         else
03717             *len = p - e;
03718     }
03719     return e;
03720 }
03721 
03722 /*
03723  *  call-seq:
03724  *     File.extname(path)  ->  string
03725  *
03726  *  Returns the extension (the portion of file name in <i>path</i>
03727  *  after the period).
03728  *
03729  *     File.extname("test.rb")         #=> ".rb"
03730  *     File.extname("a/b/d/test.rb")   #=> ".rb"
03731  *     File.extname("test")            #=> ""
03732  *     File.extname(".profile")        #=> ""
03733  *
03734  */
03735 
03736 static VALUE
03737 rb_file_s_extname(VALUE klass, VALUE fname)
03738 {
03739     const char *name, *e;
03740     long len;
03741     VALUE extname;
03742 
03743     FilePathStringValue(fname);
03744     name = StringValueCStr(fname);
03745     e = ruby_find_extname(name, &len);
03746     if (len <= 1)
03747         return rb_str_new(0, 0);
03748     extname = rb_str_new(e, len);       /* keep the dot, too! */
03749     rb_enc_copy(extname, fname);
03750     OBJ_INFECT(extname, fname);
03751     return extname;
03752 }
03753 
03754 /*
03755  *  call-seq:
03756  *     File.path(path)  ->  string
03757  *
03758  *  Returns the string representation of the path
03759  *
03760  *     File.path("/dev/null")          #=> "/dev/null"
03761  *     File.path(Pathname.new("/tmp")) #=> "/tmp"
03762  *
03763  */
03764 
03765 static VALUE
03766 rb_file_s_path(VALUE klass, VALUE fname)
03767 {
03768     return rb_get_path(fname);
03769 }
03770 
03771 /*
03772  *  call-seq:
03773  *     File.split(file_name)   -> array
03774  *
03775  *  Splits the given string into a directory and a file component and
03776  *  returns them in a two-element array. See also
03777  *  <code>File::dirname</code> and <code>File::basename</code>.
03778  *
03779  *     File.split("/home/gumby/.profile")   #=> ["/home/gumby", ".profile"]
03780  */
03781 
03782 static VALUE
03783 rb_file_s_split(VALUE klass, VALUE path)
03784 {
03785     FilePathStringValue(path);          /* get rid of converting twice */
03786     return rb_assoc_new(rb_file_s_dirname(Qnil, path), rb_file_s_basename(1,&path));
03787 }
03788 
03789 static VALUE separator;
03790 
03791 static VALUE rb_file_join(VALUE ary, VALUE sep);
03792 
03793 static VALUE
03794 file_inspect_join(VALUE ary, VALUE argp, int recur)
03795 {
03796     VALUE *arg = (VALUE *)argp;
03797     if (recur || ary == arg[0]) rb_raise(rb_eArgError, "recursive array");
03798     return rb_file_join(arg[0], arg[1]);
03799 }
03800 
03801 static VALUE
03802 rb_file_join(VALUE ary, VALUE sep)
03803 {
03804     long len, i;
03805     VALUE result, tmp;
03806     const char *name, *tail;
03807 
03808     if (RARRAY_LEN(ary) == 0) return rb_str_new(0, 0);
03809 
03810     len = 1;
03811     for (i=0; i<RARRAY_LEN(ary); i++) {
03812         if (TYPE(RARRAY_PTR(ary)[i]) == T_STRING) {
03813             len += RSTRING_LEN(RARRAY_PTR(ary)[i]);
03814         }
03815         else {
03816             len += 10;
03817         }
03818     }
03819     if (!NIL_P(sep)) {
03820         StringValue(sep);
03821         len += RSTRING_LEN(sep) * RARRAY_LEN(ary) - 1;
03822     }
03823     result = rb_str_buf_new(len);
03824     OBJ_INFECT(result, ary);
03825     for (i=0; i<RARRAY_LEN(ary); i++) {
03826         tmp = RARRAY_PTR(ary)[i];
03827         switch (TYPE(tmp)) {
03828           case T_STRING:
03829             break;
03830           case T_ARRAY:
03831             if (ary == tmp) {
03832                 rb_raise(rb_eArgError, "recursive array");
03833             }
03834             else {
03835                 VALUE args[2];
03836 
03837                 args[0] = tmp;
03838                 args[1] = sep;
03839                 tmp = rb_exec_recursive(file_inspect_join, ary, (VALUE)args);
03840             }
03841             break;
03842           default:
03843             FilePathStringValue(tmp);
03844         }
03845         name = StringValueCStr(result);
03846         if (i == 0) {
03847             rb_enc_copy(result, tmp);
03848         }
03849         else if (!NIL_P(sep)) {
03850             tail = chompdirsep(name);
03851             if (RSTRING_PTR(tmp) && isdirsep(RSTRING_PTR(tmp)[0])) {
03852                 rb_str_set_len(result, tail - name);
03853             }
03854             else if (!*tail) {
03855                 rb_str_buf_append(result, sep);
03856             }
03857         }
03858         rb_str_buf_append(result, tmp);
03859     }
03860 
03861     return result;
03862 }
03863 
03864 /*
03865  *  call-seq:
03866  *     File.join(string, ...)  ->  path
03867  *
03868  *  Returns a new string formed by joining the strings using
03869  *  <code>File::SEPARATOR</code>.
03870  *
03871  *     File.join("usr", "mail", "gumby")   #=> "usr/mail/gumby"
03872  *
03873  */
03874 
03875 static VALUE
03876 rb_file_s_join(VALUE klass, VALUE args)
03877 {
03878     return rb_file_join(args, separator);
03879 }
03880 
03881 #if defined(HAVE_TRUNCATE) || defined(HAVE_CHSIZE)
03882 /*
03883  *  call-seq:
03884  *     File.truncate(file_name, integer)  -> 0
03885  *
03886  *  Truncates the file <i>file_name</i> to be at most <i>integer</i>
03887  *  bytes long. Not available on all platforms.
03888  *
03889  *     f = File.new("out", "w")
03890  *     f.write("1234567890")     #=> 10
03891  *     f.close                   #=> nil
03892  *     File.truncate("out", 5)   #=> 0
03893  *     File.size("out")          #=> 5
03894  *
03895  */
03896 
03897 static VALUE
03898 rb_file_s_truncate(VALUE klass, VALUE path, VALUE len)
03899 {
03900     off_t pos;
03901 
03902     rb_secure(2);
03903     pos = NUM2OFFT(len);
03904     FilePathValue(path);
03905     path = rb_str_encode_ospath(path);
03906 #ifdef HAVE_TRUNCATE
03907     if (truncate(StringValueCStr(path), pos) < 0)
03908         rb_sys_fail_path(path);
03909 #else /* defined(HAVE_CHSIZE) */
03910     {
03911         int tmpfd;
03912 
03913         if ((tmpfd = open(StringValueCStr(path), 0)) < 0) {
03914             rb_sys_fail_path(path);
03915         }
03916         rb_update_max_fd(tmpfd);
03917         if (chsize(tmpfd, pos) < 0) {
03918             close(tmpfd);
03919             rb_sys_fail_path(path);
03920         }
03921         close(tmpfd);
03922     }
03923 #endif
03924     return INT2FIX(0);
03925 }
03926 #else
03927 #define rb_file_s_truncate rb_f_notimplement
03928 #endif
03929 
03930 #if defined(HAVE_FTRUNCATE) || defined(HAVE_CHSIZE)
03931 /*
03932  *  call-seq:
03933  *     file.truncate(integer)    -> 0
03934  *
03935  *  Truncates <i>file</i> to at most <i>integer</i> bytes. The file
03936  *  must be opened for writing. Not available on all platforms.
03937  *
03938  *     f = File.new("out", "w")
03939  *     f.syswrite("1234567890")   #=> 10
03940  *     f.truncate(5)              #=> 0
03941  *     f.close()                  #=> nil
03942  *     File.size("out")           #=> 5
03943  */
03944 
03945 static VALUE
03946 rb_file_truncate(VALUE obj, VALUE len)
03947 {
03948     rb_io_t *fptr;
03949     off_t pos;
03950 
03951     rb_secure(2);
03952     pos = NUM2OFFT(len);
03953     GetOpenFile(obj, fptr);
03954     if (!(fptr->mode & FMODE_WRITABLE)) {
03955         rb_raise(rb_eIOError, "not opened for writing");
03956     }
03957     rb_io_flush(obj);
03958 #ifdef HAVE_FTRUNCATE
03959     if (ftruncate(fptr->fd, pos) < 0)
03960         rb_sys_fail_path(fptr->pathv);
03961 #else /* defined(HAVE_CHSIZE) */
03962     if (chsize(fptr->fd, pos) < 0)
03963         rb_sys_fail_path(fptr->pathv);
03964 #endif
03965     return INT2FIX(0);
03966 }
03967 #else
03968 #define rb_file_truncate rb_f_notimplement
03969 #endif
03970 
03971 # ifndef LOCK_SH
03972 #  define LOCK_SH 1
03973 # endif
03974 # ifndef LOCK_EX
03975 #  define LOCK_EX 2
03976 # endif
03977 # ifndef LOCK_NB
03978 #  define LOCK_NB 4
03979 # endif
03980 # ifndef LOCK_UN
03981 #  define LOCK_UN 8
03982 # endif
03983 
03984 #ifdef __CYGWIN__
03985 #include <winerror.h>
03986 extern unsigned long __attribute__((stdcall)) GetLastError(void);
03987 #endif
03988 
03989 static VALUE
03990 rb_thread_flock(void *data)
03991 {
03992 #ifdef __CYGWIN__
03993     int old_errno = errno;
03994 #endif
03995     int *op = data, ret = flock(op[0], op[1]);
03996 
03997 #ifdef __CYGWIN__
03998     if (GetLastError() == ERROR_NOT_LOCKED) {
03999         ret = 0;
04000         errno = old_errno;
04001     }
04002 #endif
04003     return (VALUE)ret;
04004 }
04005 
04006 /*
04007  *  call-seq:
04008  *     file.flock (locking_constant )-> 0 or false
04009  *
04010  *  Locks or unlocks a file according to <i>locking_constant</i> (a
04011  *  logical <em>or</em> of the values in the table below).
04012  *  Returns <code>false</code> if <code>File::LOCK_NB</code> is
04013  *  specified and the operation would otherwise have blocked. Not
04014  *  available on all platforms.
04015  *
04016  *  Locking constants (in class File):
04017  *
04018  *     LOCK_EX   | Exclusive lock. Only one process may hold an
04019  *               | exclusive lock for a given file at a time.
04020  *     ----------+------------------------------------------------
04021  *     LOCK_NB   | Don't block when locking. May be combined
04022  *               | with other lock options using logical or.
04023  *     ----------+------------------------------------------------
04024  *     LOCK_SH   | Shared lock. Multiple processes may each hold a
04025  *               | shared lock for a given file at the same time.
04026  *     ----------+------------------------------------------------
04027  *     LOCK_UN   | Unlock.
04028  *
04029  *  Example:
04030  *
04031  *     # update a counter using write lock
04032  *     # don't use "w" because it truncates the file before lock.
04033  *     File.open("counter", File::RDWR|File::CREAT, 0644) {|f|
04034  *       f.flock(File::LOCK_EX)
04035  *       value = f.read.to_i + 1
04036  *       f.rewind
04037  *       f.write("#{value}\n")
04038  *       f.flush
04039  *       f.truncate(f.pos)
04040  *     }
04041  *
04042  *     # read the counter using read lock
04043  *     File.open("counter", "r") {|f|
04044  *       f.flock(File::LOCK_SH)
04045  *       p f.read
04046  *     }
04047  *
04048  */
04049 
04050 static VALUE
04051 rb_file_flock(VALUE obj, VALUE operation)
04052 {
04053     rb_io_t *fptr;
04054     int op[2], op1;
04055 
04056     rb_secure(2);
04057     op[1] = op1 = NUM2INT(operation);
04058     GetOpenFile(obj, fptr);
04059     op[0] = fptr->fd;
04060 
04061     if (fptr->mode & FMODE_WRITABLE) {
04062         rb_io_flush(obj);
04063     }
04064     while ((int)rb_thread_io_blocking_region(rb_thread_flock, op, fptr->fd) < 0) {
04065         switch (errno) {
04066           case EAGAIN:
04067           case EACCES:
04068 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
04069           case EWOULDBLOCK:
04070 #endif
04071             if (op1 & LOCK_NB) return Qfalse;
04072             rb_thread_polling();
04073             rb_io_check_closed(fptr);
04074             continue;
04075 
04076           case EINTR:
04077 #if defined(ERESTART)
04078           case ERESTART:
04079 #endif
04080             break;
04081 
04082           default:
04083             rb_sys_fail_path(fptr->pathv);
04084         }
04085     }
04086     return INT2FIX(0);
04087 }
04088 #undef flock
04089 
04090 static void
04091 test_check(int n, int argc, VALUE *argv)
04092 {
04093     int i;
04094 
04095     rb_secure(2);
04096     n+=1;
04097     if (n != argc) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, n);
04098     for (i=1; i<n; i++) {
04099         switch (TYPE(argv[i])) {
04100           case T_STRING:
04101           default:
04102             FilePathValue(argv[i]);
04103             break;
04104           case T_FILE:
04105             break;
04106         }
04107     }
04108 }
04109 
04110 #define CHECK(n) test_check((n), argc, argv)
04111 
04112 /*
04113  *  call-seq:
04114  *     test(int_cmd, file1 [, file2] ) -> obj
04115  *
04116  *  Uses the integer <i>aCmd</i> to perform various tests on
04117  *  <i>file1</i> (first table below) or on <i>file1</i> and
04118  *  <i>file2</i> (second table).
04119  *
04120  *  File tests on a single file:
04121  *
04122  *    Test   Returns   Meaning
04123  *    "A"  | Time    | Last access time for file1
04124  *    "b"  | boolean | True if file1 is a block device
04125  *    "c"  | boolean | True if file1 is a character device
04126  *    "C"  | Time    | Last change time for file1
04127  *    "d"  | boolean | True if file1 exists and is a directory
04128  *    "e"  | boolean | True if file1 exists
04129  *    "f"  | boolean | True if file1 exists and is a regular file
04130  *    "g"  | boolean | True if file1 has the \CF{setgid} bit
04131  *         |         | set (false under NT)
04132  *    "G"  | boolean | True if file1 exists and has a group
04133  *         |         | ownership equal to the caller's group
04134  *    "k"  | boolean | True if file1 exists and has the sticky bit set
04135  *    "l"  | boolean | True if file1 exists and is a symbolic link
04136  *    "M"  | Time    | Last modification time for file1
04137  *    "o"  | boolean | True if file1 exists and is owned by
04138  *         |         | the caller's effective uid
04139  *    "O"  | boolean | True if file1 exists and is owned by
04140  *         |         | the caller's real uid
04141  *    "p"  | boolean | True if file1 exists and is a fifo
04142  *    "r"  | boolean | True if file1 is readable by the effective
04143  *         |         | uid/gid of the caller
04144  *    "R"  | boolean | True if file is readable by the real
04145  *         |         | uid/gid of the caller
04146  *    "s"  | int/nil | If file1 has nonzero size, return the size,
04147  *         |         | otherwise return nil
04148  *    "S"  | boolean | True if file1 exists and is a socket
04149  *    "u"  | boolean | True if file1 has the setuid bit set
04150  *    "w"  | boolean | True if file1 exists and is writable by
04151  *         |         | the effective uid/gid
04152  *    "W"  | boolean | True if file1 exists and is writable by
04153  *         |         | the real uid/gid
04154  *    "x"  | boolean | True if file1 exists and is executable by
04155  *         |         | the effective uid/gid
04156  *    "X"  | boolean | True if file1 exists and is executable by
04157  *         |         | the real uid/gid
04158  *    "z"  | boolean | True if file1 exists and has a zero length
04159  *
04160  * Tests that take two files:
04161  *
04162  *    "-"  | boolean | True if file1 and file2 are identical
04163  *    "="  | boolean | True if the modification times of file1
04164  *         |         | and file2 are equal
04165  *    "<"  | boolean | True if the modification time of file1
04166  *         |         | is prior to that of file2
04167  *    ">"  | boolean | True if the modification time of file1
04168  *         |         | is after that of file2
04169  */
04170 
04171 static VALUE
04172 rb_f_test(int argc, VALUE *argv)
04173 {
04174     int cmd;
04175 
04176     if (argc == 0) rb_raise(rb_eArgError, "wrong number of arguments (0 for 2..3)");
04177     cmd = NUM2CHR(argv[0]);
04178     if (cmd == 0) goto unknown;
04179     if (strchr("bcdefgGkloOprRsSuwWxXz", cmd)) {
04180         CHECK(1);
04181         switch (cmd) {
04182           case 'b':
04183             return rb_file_blockdev_p(0, argv[1]);
04184 
04185           case 'c':
04186             return rb_file_chardev_p(0, argv[1]);
04187 
04188           case 'd':
04189             return rb_file_directory_p(0, argv[1]);
04190 
04191           case 'a':
04192           case 'e':
04193             return rb_file_exist_p(0, argv[1]);
04194 
04195           case 'f':
04196             return rb_file_file_p(0, argv[1]);
04197 
04198           case 'g':
04199             return rb_file_sgid_p(0, argv[1]);
04200 
04201           case 'G':
04202             return rb_file_grpowned_p(0, argv[1]);
04203 
04204           case 'k':
04205             return rb_file_sticky_p(0, argv[1]);
04206 
04207           case 'l':
04208             return rb_file_symlink_p(0, argv[1]);
04209 
04210           case 'o':
04211             return rb_file_owned_p(0, argv[1]);
04212 
04213           case 'O':
04214             return rb_file_rowned_p(0, argv[1]);
04215 
04216           case 'p':
04217             return rb_file_pipe_p(0, argv[1]);
04218 
04219           case 'r':
04220             return rb_file_readable_p(0, argv[1]);
04221 
04222           case 'R':
04223             return rb_file_readable_real_p(0, argv[1]);
04224 
04225           case 's':
04226             return rb_file_size_p(0, argv[1]);
04227 
04228           case 'S':
04229             return rb_file_socket_p(0, argv[1]);
04230 
04231           case 'u':
04232             return rb_file_suid_p(0, argv[1]);
04233 
04234           case 'w':
04235             return rb_file_writable_p(0, argv[1]);
04236 
04237           case 'W':
04238             return rb_file_writable_real_p(0, argv[1]);
04239 
04240           case 'x':
04241             return rb_file_executable_p(0, argv[1]);
04242 
04243           case 'X':
04244             return rb_file_executable_real_p(0, argv[1]);
04245 
04246           case 'z':
04247             return rb_file_zero_p(0, argv[1]);
04248         }
04249     }
04250 
04251     if (strchr("MAC", cmd)) {
04252         struct stat st;
04253         VALUE fname = argv[1];
04254 
04255         CHECK(1);
04256         if (rb_stat(fname, &st) == -1) {
04257             FilePathValue(fname);
04258             rb_sys_fail_path(fname);
04259         }
04260 
04261         switch (cmd) {
04262           case 'A':
04263             return stat_atime(&st);
04264           case 'M':
04265             return stat_mtime(&st);
04266           case 'C':
04267             return stat_ctime(&st);
04268         }
04269     }
04270 
04271     if (cmd == '-') {
04272         CHECK(2);
04273         return rb_file_identical_p(0, argv[1], argv[2]);
04274     }
04275 
04276     if (strchr("=<>", cmd)) {
04277         struct stat st1, st2;
04278 
04279         CHECK(2);
04280         if (rb_stat(argv[1], &st1) < 0) return Qfalse;
04281         if (rb_stat(argv[2], &st2) < 0) return Qfalse;
04282 
04283         switch (cmd) {
04284           case '=':
04285             if (st1.st_mtime == st2.st_mtime) return Qtrue;
04286             return Qfalse;
04287 
04288           case '>':
04289             if (st1.st_mtime > st2.st_mtime) return Qtrue;
04290             return Qfalse;
04291 
04292           case '<':
04293             if (st1.st_mtime < st2.st_mtime) return Qtrue;
04294             return Qfalse;
04295         }
04296     }
04297   unknown:
04298     /* unknown command */
04299     if (ISPRINT(cmd)) {
04300         rb_raise(rb_eArgError, "unknown command '%s%c'", cmd == '\'' || cmd == '\\' ? "\\" : "", cmd);
04301     }
04302     else {
04303         rb_raise(rb_eArgError, "unknown command \"\\x%02X\"", cmd);
04304     }
04305     return Qnil;                /* not reached */
04306 }
04307 
04308 
04309 /*
04310  *  Document-class: File::Stat
04311  *
04312  *  Objects of class <code>File::Stat</code> encapsulate common status
04313  *  information for <code>File</code> objects. The information is
04314  *  recorded at the moment the <code>File::Stat</code> object is
04315  *  created; changes made to the file after that point will not be
04316  *  reflected. <code>File::Stat</code> objects are returned by
04317  *  <code>IO#stat</code>, <code>File::stat</code>,
04318  *  <code>File#lstat</code>, and <code>File::lstat</code>. Many of these
04319  *  methods return platform-specific values, and not all values are
04320  *  meaningful on all systems. See also <code>Kernel#test</code>.
04321  */
04322 
04323 static VALUE
04324 rb_stat_s_alloc(VALUE klass)
04325 {
04326     return stat_new_0(klass, 0);
04327 }
04328 
04329 /*
04330  * call-seq:
04331  *
04332  *   File::Stat.new(file_name)  -> stat
04333  *
04334  * Create a File::Stat object for the given file name (raising an
04335  * exception if the file doesn't exist).
04336  */
04337 
04338 static VALUE
04339 rb_stat_init(VALUE obj, VALUE fname)
04340 {
04341     struct stat st, *nst;
04342 
04343     rb_secure(2);
04344     FilePathValue(fname);
04345     fname = rb_str_encode_ospath(fname);
04346     if (STAT(StringValueCStr(fname), &st) == -1) {
04347         rb_sys_fail_path(fname);
04348     }
04349     if (DATA_PTR(obj)) {
04350         xfree(DATA_PTR(obj));
04351         DATA_PTR(obj) = NULL;
04352     }
04353     nst = ALLOC(struct stat);
04354     *nst = st;
04355     DATA_PTR(obj) = nst;
04356 
04357     return Qnil;
04358 }
04359 
04360 /* :nodoc: */
04361 static VALUE
04362 rb_stat_init_copy(VALUE copy, VALUE orig)
04363 {
04364     struct stat *nst;
04365 
04366     if (copy == orig) return orig;
04367     rb_check_frozen(copy);
04368     /* need better argument type check */
04369     if (!rb_obj_is_instance_of(orig, rb_obj_class(copy))) {
04370         rb_raise(rb_eTypeError, "wrong argument class");
04371     }
04372     if (DATA_PTR(copy)) {
04373         xfree(DATA_PTR(copy));
04374         DATA_PTR(copy) = 0;
04375     }
04376     if (DATA_PTR(orig)) {
04377         nst = ALLOC(struct stat);
04378         *nst = *(struct stat*)DATA_PTR(orig);
04379         DATA_PTR(copy) = nst;
04380     }
04381 
04382     return copy;
04383 }
04384 
04385 /*
04386  *  call-seq:
04387  *     stat.ftype   -> string
04388  *
04389  *  Identifies the type of <i>stat</i>. The return string is one of:
04390  *  ``<code>file</code>'', ``<code>directory</code>'',
04391  *  ``<code>characterSpecial</code>'', ``<code>blockSpecial</code>'',
04392  *  ``<code>fifo</code>'', ``<code>link</code>'',
04393  *  ``<code>socket</code>'', or ``<code>unknown</code>''.
04394  *
04395  *     File.stat("/dev/tty").ftype   #=> "characterSpecial"
04396  *
04397  */
04398 
04399 static VALUE
04400 rb_stat_ftype(VALUE obj)
04401 {
04402     return rb_file_ftype(get_stat(obj));
04403 }
04404 
04405 /*
04406  *  call-seq:
04407  *     stat.directory?   -> true or false
04408  *
04409  *  Returns <code>true</code> if <i>stat</i> is a directory,
04410  *  <code>false</code> otherwise.
04411  *
04412  *     File.stat("testfile").directory?   #=> false
04413  *     File.stat(".").directory?          #=> true
04414  */
04415 
04416 static VALUE
04417 rb_stat_d(VALUE obj)
04418 {
04419     if (S_ISDIR(get_stat(obj)->st_mode)) return Qtrue;
04420     return Qfalse;
04421 }
04422 
04423 /*
04424  *  call-seq:
04425  *     stat.pipe?    -> true or false
04426  *
04427  *  Returns <code>true</code> if the operating system supports pipes and
04428  *  <i>stat</i> is a pipe; <code>false</code> otherwise.
04429  */
04430 
04431 static VALUE
04432 rb_stat_p(VALUE obj)
04433 {
04434 #ifdef S_IFIFO
04435     if (S_ISFIFO(get_stat(obj)->st_mode)) return Qtrue;
04436 
04437 #endif
04438     return Qfalse;
04439 }
04440 
04441 /*
04442  *  call-seq:
04443  *     stat.symlink?    -> true or false
04444  *
04445  *  Returns <code>true</code> if <i>stat</i> is a symbolic link,
04446  *  <code>false</code> if it isn't or if the operating system doesn't
04447  *  support this feature. As <code>File::stat</code> automatically
04448  *  follows symbolic links, <code>symlink?</code> will always be
04449  *  <code>false</code> for an object returned by
04450  *  <code>File::stat</code>.
04451  *
04452  *     File.symlink("testfile", "alink")   #=> 0
04453  *     File.stat("alink").symlink?         #=> false
04454  *     File.lstat("alink").symlink?        #=> true
04455  *
04456  */
04457 
04458 static VALUE
04459 rb_stat_l(VALUE obj)
04460 {
04461 #ifdef S_ISLNK
04462     if (S_ISLNK(get_stat(obj)->st_mode)) return Qtrue;
04463 #endif
04464     return Qfalse;
04465 }
04466 
04467 /*
04468  *  call-seq:
04469  *     stat.socket?    -> true or false
04470  *
04471  *  Returns <code>true</code> if <i>stat</i> is a socket,
04472  *  <code>false</code> if it isn't or if the operating system doesn't
04473  *  support this feature.
04474  *
04475  *     File.stat("testfile").socket?   #=> false
04476  *
04477  */
04478 
04479 static VALUE
04480 rb_stat_S(VALUE obj)
04481 {
04482 #ifdef S_ISSOCK
04483     if (S_ISSOCK(get_stat(obj)->st_mode)) return Qtrue;
04484 
04485 #endif
04486     return Qfalse;
04487 }
04488 
04489 /*
04490  *  call-seq:
04491  *     stat.blockdev?   -> true or false
04492  *
04493  *  Returns <code>true</code> if the file is a block device,
04494  *  <code>false</code> if it isn't or if the operating system doesn't
04495  *  support this feature.
04496  *
04497  *     File.stat("testfile").blockdev?    #=> false
04498  *     File.stat("/dev/hda1").blockdev?   #=> true
04499  *
04500  */
04501 
04502 static VALUE
04503 rb_stat_b(VALUE obj)
04504 {
04505 #ifdef S_ISBLK
04506     if (S_ISBLK(get_stat(obj)->st_mode)) return Qtrue;
04507 
04508 #endif
04509     return Qfalse;
04510 }
04511 
04512 /*
04513  *  call-seq:
04514  *     stat.chardev?    -> true or false
04515  *
04516  *  Returns <code>true</code> if the file is a character device,
04517  *  <code>false</code> if it isn't or if the operating system doesn't
04518  *  support this feature.
04519  *
04520  *     File.stat("/dev/tty").chardev?   #=> true
04521  *
04522  */
04523 
04524 static VALUE
04525 rb_stat_c(VALUE obj)
04526 {
04527     if (S_ISCHR(get_stat(obj)->st_mode)) return Qtrue;
04528 
04529     return Qfalse;
04530 }
04531 
04532 /*
04533  *  call-seq:
04534  *     stat.owned?    -> true or false
04535  *
04536  *  Returns <code>true</code> if the effective user id of the process is
04537  *  the same as the owner of <i>stat</i>.
04538  *
04539  *     File.stat("testfile").owned?      #=> true
04540  *     File.stat("/etc/passwd").owned?   #=> false
04541  *
04542  */
04543 
04544 static VALUE
04545 rb_stat_owned(VALUE obj)
04546 {
04547     if (get_stat(obj)->st_uid == geteuid()) return Qtrue;
04548     return Qfalse;
04549 }
04550 
04551 static VALUE
04552 rb_stat_rowned(VALUE obj)
04553 {
04554     if (get_stat(obj)->st_uid == getuid()) return Qtrue;
04555     return Qfalse;
04556 }
04557 
04558 /*
04559  *  call-seq:
04560  *     stat.grpowned?   -> true or false
04561  *
04562  *  Returns true if the effective group id of the process is the same as
04563  *  the group id of <i>stat</i>. On Windows NT, returns <code>false</code>.
04564  *
04565  *     File.stat("testfile").grpowned?      #=> true
04566  *     File.stat("/etc/passwd").grpowned?   #=> false
04567  *
04568  */
04569 
04570 static VALUE
04571 rb_stat_grpowned(VALUE obj)
04572 {
04573 #ifndef _WIN32
04574     if (rb_group_member(get_stat(obj)->st_gid)) return Qtrue;
04575 #endif
04576     return Qfalse;
04577 }
04578 
04579 /*
04580  *  call-seq:
04581  *     stat.readable?    -> true or false
04582  *
04583  *  Returns <code>true</code> if <i>stat</i> is readable by the
04584  *  effective user id of this process.
04585  *
04586  *     File.stat("testfile").readable?   #=> true
04587  *
04588  */
04589 
04590 static VALUE
04591 rb_stat_r(VALUE obj)
04592 {
04593     struct stat *st = get_stat(obj);
04594 
04595 #ifdef USE_GETEUID
04596     if (geteuid() == 0) return Qtrue;
04597 #endif
04598 #ifdef S_IRUSR
04599     if (rb_stat_owned(obj))
04600         return st->st_mode & S_IRUSR ? Qtrue : Qfalse;
04601 #endif
04602 #ifdef S_IRGRP
04603     if (rb_stat_grpowned(obj))
04604         return st->st_mode & S_IRGRP ? Qtrue : Qfalse;
04605 #endif
04606 #ifdef S_IROTH
04607     if (!(st->st_mode & S_IROTH)) return Qfalse;
04608 #endif
04609     return Qtrue;
04610 }
04611 
04612 /*
04613  *  call-seq:
04614  *     stat.readable_real?  ->  true or false
04615  *
04616  *  Returns <code>true</code> if <i>stat</i> is readable by the real
04617  *  user id of this process.
04618  *
04619  *     File.stat("testfile").readable_real?   #=> true
04620  *
04621  */
04622 
04623 static VALUE
04624 rb_stat_R(VALUE obj)
04625 {
04626     struct stat *st = get_stat(obj);
04627 
04628 #ifdef USE_GETEUID
04629     if (getuid() == 0) return Qtrue;
04630 #endif
04631 #ifdef S_IRUSR
04632     if (rb_stat_rowned(obj))
04633         return st->st_mode & S_IRUSR ? Qtrue : Qfalse;
04634 #endif
04635 #ifdef S_IRGRP
04636     if (rb_group_member(get_stat(obj)->st_gid))
04637         return st->st_mode & S_IRGRP ? Qtrue : Qfalse;
04638 #endif
04639 #ifdef S_IROTH
04640     if (!(st->st_mode & S_IROTH)) return Qfalse;
04641 #endif
04642     return Qtrue;
04643 }
04644 
04645 /*
04646  * call-seq:
04647  *    stat.world_readable? -> fixnum or nil
04648  *
04649  * If <i>stat</i> is readable by others, returns an integer
04650  * representing the file permission bits of <i>stat</i>. Returns
04651  * <code>nil</code> otherwise. The meaning of the bits is platform
04652  * dependent; on Unix systems, see <code>stat(2)</code>.
04653  *
04654  *    m = File.stat("/etc/passwd").world_readable?  #=> 420
04655  *    sprintf("%o", m)                              #=> "644"
04656  */
04657 
04658 static VALUE
04659 rb_stat_wr(VALUE obj)
04660 {
04661 #ifdef S_IROTH
04662     if ((get_stat(obj)->st_mode & (S_IROTH)) == S_IROTH) {
04663         return UINT2NUM(get_stat(obj)->st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
04664     }
04665     else {
04666         return Qnil;
04667     }
04668 #endif
04669 }
04670 
04671 /*
04672  *  call-seq:
04673  *     stat.writable?  ->  true or false
04674  *
04675  *  Returns <code>true</code> if <i>stat</i> is writable by the
04676  *  effective user id of this process.
04677  *
04678  *     File.stat("testfile").writable?   #=> true
04679  *
04680  */
04681 
04682 static VALUE
04683 rb_stat_w(VALUE obj)
04684 {
04685     struct stat *st = get_stat(obj);
04686 
04687 #ifdef USE_GETEUID
04688     if (geteuid() == 0) return Qtrue;
04689 #endif
04690 #ifdef S_IWUSR
04691     if (rb_stat_owned(obj))
04692         return st->st_mode & S_IWUSR ? Qtrue : Qfalse;
04693 #endif
04694 #ifdef S_IWGRP
04695     if (rb_stat_grpowned(obj))
04696         return st->st_mode & S_IWGRP ? Qtrue : Qfalse;
04697 #endif
04698 #ifdef S_IWOTH
04699     if (!(st->st_mode & S_IWOTH)) return Qfalse;
04700 #endif
04701     return Qtrue;
04702 }
04703 
04704 /*
04705  *  call-seq:
04706  *     stat.writable_real?  ->  true or false
04707  *
04708  *  Returns <code>true</code> if <i>stat</i> is writable by the real
04709  *  user id of this process.
04710  *
04711  *     File.stat("testfile").writable_real?   #=> true
04712  *
04713  */
04714 
04715 static VALUE
04716 rb_stat_W(VALUE obj)
04717 {
04718     struct stat *st = get_stat(obj);
04719 
04720 #ifdef USE_GETEUID
04721     if (getuid() == 0) return Qtrue;
04722 #endif
04723 #ifdef S_IWUSR
04724     if (rb_stat_rowned(obj))
04725         return st->st_mode & S_IWUSR ? Qtrue : Qfalse;
04726 #endif
04727 #ifdef S_IWGRP
04728     if (rb_group_member(get_stat(obj)->st_gid))
04729         return st->st_mode & S_IWGRP ? Qtrue : Qfalse;
04730 #endif
04731 #ifdef S_IWOTH
04732     if (!(st->st_mode & S_IWOTH)) return Qfalse;
04733 #endif
04734     return Qtrue;
04735 }
04736 
04737 /*
04738  * call-seq:
04739  *    stat.world_writable?  ->  fixnum or nil
04740  *
04741  * If <i>stat</i> is writable by others, returns an integer
04742  * representing the file permission bits of <i>stat</i>. Returns
04743  * <code>nil</code> otherwise. The meaning of the bits is platform
04744  * dependent; on Unix systems, see <code>stat(2)</code>.
04745  *
04746  *    m = File.stat("/tmp").world_writable?         #=> 511
04747  *    sprintf("%o", m)                              #=> "777"
04748  */
04749 
04750 static VALUE
04751 rb_stat_ww(VALUE obj)
04752 {
04753 #ifdef S_IROTH
04754     if ((get_stat(obj)->st_mode & (S_IWOTH)) == S_IWOTH) {
04755         return UINT2NUM(get_stat(obj)->st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
04756     }
04757     else {
04758         return Qnil;
04759     }
04760 #endif
04761 }
04762 
04763 /*
04764  *  call-seq:
04765  *     stat.executable?    -> true or false
04766  *
04767  *  Returns <code>true</code> if <i>stat</i> is executable or if the
04768  *  operating system doesn't distinguish executable files from
04769  *  nonexecutable files. The tests are made using the effective owner of
04770  *  the process.
04771  *
04772  *     File.stat("testfile").executable?   #=> false
04773  *
04774  */
04775 
04776 static VALUE
04777 rb_stat_x(VALUE obj)
04778 {
04779     struct stat *st = get_stat(obj);
04780 
04781 #ifdef USE_GETEUID
04782     if (geteuid() == 0) {
04783         return st->st_mode & S_IXUGO ? Qtrue : Qfalse;
04784     }
04785 #endif
04786 #ifdef S_IXUSR
04787     if (rb_stat_owned(obj))
04788         return st->st_mode & S_IXUSR ? Qtrue : Qfalse;
04789 #endif
04790 #ifdef S_IXGRP
04791     if (rb_stat_grpowned(obj))
04792         return st->st_mode & S_IXGRP ? Qtrue : Qfalse;
04793 #endif
04794 #ifdef S_IXOTH
04795     if (!(st->st_mode & S_IXOTH)) return Qfalse;
04796 #endif
04797     return Qtrue;
04798 }
04799 
04800 /*
04801  *  call-seq:
04802  *     stat.executable_real?    -> true or false
04803  *
04804  *  Same as <code>executable?</code>, but tests using the real owner of
04805  *  the process.
04806  */
04807 
04808 static VALUE
04809 rb_stat_X(VALUE obj)
04810 {
04811     struct stat *st = get_stat(obj);
04812 
04813 #ifdef USE_GETEUID
04814     if (getuid() == 0) {
04815         return st->st_mode & S_IXUGO ? Qtrue : Qfalse;
04816     }
04817 #endif
04818 #ifdef S_IXUSR
04819     if (rb_stat_rowned(obj))
04820         return st->st_mode & S_IXUSR ? Qtrue : Qfalse;
04821 #endif
04822 #ifdef S_IXGRP
04823     if (rb_group_member(get_stat(obj)->st_gid))
04824         return st->st_mode & S_IXGRP ? Qtrue : Qfalse;
04825 #endif
04826 #ifdef S_IXOTH
04827     if (!(st->st_mode & S_IXOTH)) return Qfalse;
04828 #endif
04829     return Qtrue;
04830 }
04831 
04832 /*
04833  *  call-seq:
04834  *     stat.file?    -> true or false
04835  *
04836  *  Returns <code>true</code> if <i>stat</i> is a regular file (not
04837  *  a device file, pipe, socket, etc.).
04838  *
04839  *     File.stat("testfile").file?   #=> true
04840  *
04841  */
04842 
04843 static VALUE
04844 rb_stat_f(VALUE obj)
04845 {
04846     if (S_ISREG(get_stat(obj)->st_mode)) return Qtrue;
04847     return Qfalse;
04848 }
04849 
04850 /*
04851  *  call-seq:
04852  *     stat.zero?    -> true or false
04853  *
04854  *  Returns <code>true</code> if <i>stat</i> is a zero-length file;
04855  *  <code>false</code> otherwise.
04856  *
04857  *     File.stat("testfile").zero?   #=> false
04858  *
04859  */
04860 
04861 static VALUE
04862 rb_stat_z(VALUE obj)
04863 {
04864     if (get_stat(obj)->st_size == 0) return Qtrue;
04865     return Qfalse;
04866 }
04867 
04868 /*
04869  *  call-seq:
04870  *     state.size    -> integer
04871  *
04872  *  Returns the size of <i>stat</i> in bytes.
04873  *
04874  *     File.stat("testfile").size   #=> 66
04875  *
04876  */
04877 
04878 static VALUE
04879 rb_stat_s(VALUE obj)
04880 {
04881     off_t size = get_stat(obj)->st_size;
04882 
04883     if (size == 0) return Qnil;
04884     return OFFT2NUM(size);
04885 }
04886 
04887 /*
04888  *  call-seq:
04889  *     stat.setuid?    -> true or false
04890  *
04891  *  Returns <code>true</code> if <i>stat</i> has the set-user-id
04892  *  permission bit set, <code>false</code> if it doesn't or if the
04893  *  operating system doesn't support this feature.
04894  *
04895  *     File.stat("/bin/su").setuid?   #=> true
04896  */
04897 
04898 static VALUE
04899 rb_stat_suid(VALUE obj)
04900 {
04901 #ifdef S_ISUID
04902     if (get_stat(obj)->st_mode & S_ISUID) return Qtrue;
04903 #endif
04904     return Qfalse;
04905 }
04906 
04907 /*
04908  *  call-seq:
04909  *     stat.setgid?   -> true or false
04910  *
04911  *  Returns <code>true</code> if <i>stat</i> has the set-group-id
04912  *  permission bit set, <code>false</code> if it doesn't or if the
04913  *  operating system doesn't support this feature.
04914  *
04915  *     File.stat("/usr/sbin/lpc").setgid?   #=> true
04916  *
04917  */
04918 
04919 static VALUE
04920 rb_stat_sgid(VALUE obj)
04921 {
04922 #ifdef S_ISGID
04923     if (get_stat(obj)->st_mode & S_ISGID) return Qtrue;
04924 #endif
04925     return Qfalse;
04926 }
04927 
04928 /*
04929  *  call-seq:
04930  *     stat.sticky?    -> true or false
04931  *
04932  *  Returns <code>true</code> if <i>stat</i> has its sticky bit set,
04933  *  <code>false</code> if it doesn't or if the operating system doesn't
04934  *  support this feature.
04935  *
04936  *     File.stat("testfile").sticky?   #=> false
04937  *
04938  */
04939 
04940 static VALUE
04941 rb_stat_sticky(VALUE obj)
04942 {
04943 #ifdef S_ISVTX
04944     if (get_stat(obj)->st_mode & S_ISVTX) return Qtrue;
04945 #endif
04946     return Qfalse;
04947 }
04948 
04949 VALUE rb_mFConst;
04950 
04951 void
04952 rb_file_const(const char *name, VALUE value)
04953 {
04954     rb_define_const(rb_mFConst, name, value);
04955 }
04956 
04957 int
04958 rb_is_absolute_path(const char *path)
04959 {
04960 #ifdef DOSISH_DRIVE_LETTER
04961     if (has_drive_letter(path) && isdirsep(path[2])) return 1;
04962 #endif
04963 #ifdef DOSISH_UNC
04964     if (isdirsep(path[0]) && isdirsep(path[1])) return 1;
04965 #endif
04966 #ifndef DOSISH
04967     if (path[0] == '/') return 1;
04968 #endif
04969     return 0;
04970 }
04971 
04972 #ifndef ENABLE_PATH_CHECK
04973 # if defined DOSISH || defined __CYGWIN__
04974 #   define ENABLE_PATH_CHECK 0
04975 # else
04976 #   define ENABLE_PATH_CHECK 1
04977 # endif
04978 #endif
04979 
04980 #if ENABLE_PATH_CHECK
04981 static int
04982 path_check_0(VALUE path, int execpath)
04983 {
04984     struct stat st;
04985     const char *p0 = StringValueCStr(path);
04986     char *p = 0, *s;
04987 
04988     if (!rb_is_absolute_path(p0)) {
04989         char *buf = my_getcwd();
04990         VALUE newpath;
04991 
04992         newpath = rb_str_new2(buf);
04993         xfree(buf);
04994 
04995         rb_str_cat2(newpath, "/");
04996         rb_str_cat2(newpath, p0);
04997         path = newpath;
04998         p0 = RSTRING_PTR(path);
04999     }
05000     for (;;) {
05001 #ifndef S_IWOTH
05002 # define S_IWOTH 002
05003 #endif
05004         if (STAT(p0, &st) == 0 && S_ISDIR(st.st_mode) && (st.st_mode & S_IWOTH)
05005 #ifdef S_ISVTX
05006             && !(p && execpath && (st.st_mode & S_ISVTX))
05007 #endif
05008             && !access(p0, W_OK)) {
05009             rb_warn("Insecure world writable dir %s in %sPATH, mode 0%"
05010                     PRI_MODET_PREFIX"o",
05011                     p0, (execpath ? "" : "LOAD_"), st.st_mode);
05012             if (p) *p = '/';
05013             RB_GC_GUARD(path);
05014             return 0;
05015         }
05016         s = strrdirsep(p0);
05017         if (p) *p = '/';
05018         if (!s || s == p0) return 1;
05019         p = s;
05020         *p = '\0';
05021     }
05022 }
05023 #endif
05024 
05025 #if ENABLE_PATH_CHECK
05026 #define fpath_check(path) path_check_0((path), FALSE)
05027 #else
05028 #define fpath_check(path) 1
05029 #endif
05030 
05031 int
05032 rb_path_check(const char *path)
05033 {
05034 #if ENABLE_PATH_CHECK
05035     const char *p0, *p, *pend;
05036     const char sep = PATH_SEP_CHAR;
05037 
05038     if (!path) return 1;
05039 
05040     pend = path + strlen(path);
05041     p0 = path;
05042     p = strchr(path, sep);
05043     if (!p) p = pend;
05044 
05045     for (;;) {
05046         if (!path_check_0(rb_str_new(p0, p - p0), TRUE)) {
05047             return 0;           /* not safe */
05048         }
05049         p0 = p + 1;
05050         if (p0 > pend) break;
05051         p = strchr(p0, sep);
05052         if (!p) p = pend;
05053     }
05054 #endif
05055     return 1;
05056 }
05057 
05058 static int
05059 file_load_ok(const char *path)
05060 {
05061     int ret = 1;
05062     int fd = open(path, O_RDONLY);
05063     if (fd == -1) return 0;
05064     rb_update_max_fd(fd);
05065 #if !defined DOSISH
05066     {
05067         struct stat st;
05068         if (fstat(fd, &st) || !S_ISREG(st.st_mode)) {
05069             ret = 0;
05070         }
05071     }
05072 #endif
05073     (void)close(fd);
05074     return ret;
05075 }
05076 
05077 int
05078 rb_file_load_ok(const char *path)
05079 {
05080     return file_load_ok(path);
05081 }
05082 
05083 static int
05084 is_explicit_relative(const char *path)
05085 {
05086     if (*path++ != '.') return 0;
05087     if (*path == '.') path++;
05088     return isdirsep(*path);
05089 }
05090 
05091 static VALUE
05092 copy_path_class(VALUE path, VALUE orig)
05093 {
05094     RBASIC(path)->klass = rb_obj_class(orig);
05095     OBJ_FREEZE(path);
05096     return path;
05097 }
05098 
05099 int
05100 rb_find_file_ext(VALUE *filep, const char *const *ext)
05101 {
05102     return rb_find_file_ext_safe(filep, ext, rb_safe_level());
05103 }
05104 
05105 int
05106 rb_find_file_ext_safe(VALUE *filep, const char *const *ext, int safe_level)
05107 {
05108     const char *f = StringValueCStr(*filep);
05109     VALUE fname = *filep, load_path, tmp;
05110     long i, j, fnlen;
05111     int expanded = 0;
05112 
05113     if (!ext[0]) return 0;
05114 
05115     if (f[0] == '~') {
05116         fname = file_expand_path_1(fname);
05117         if (safe_level >= 1 && OBJ_TAINTED(fname)) {
05118             rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
05119         }
05120         f = RSTRING_PTR(fname);
05121         *filep = fname;
05122         expanded = 1;
05123     }
05124 
05125     if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) {
05126         if (safe_level >= 1 && !fpath_check(fname)) {
05127             rb_raise(rb_eSecurityError, "loading from unsafe path %s", f);
05128         }
05129         if (!expanded) fname = file_expand_path_1(fname);
05130         fnlen = RSTRING_LEN(fname);
05131         for (i=0; ext[i]; i++) {
05132             rb_str_cat2(fname, ext[i]);
05133             if (file_load_ok(RSTRING_PTR(fname))) {
05134                 *filep = copy_path_class(fname, *filep);
05135                 return (int)(i+1);
05136             }
05137             rb_str_set_len(fname, fnlen);
05138         }
05139         return 0;
05140     }
05141 
05142     if (safe_level >= 4) {
05143         rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f);
05144     }
05145 
05146     RB_GC_GUARD(load_path) = rb_get_load_path();
05147     if (!load_path) return 0;
05148 
05149     fname = rb_str_dup(*filep);
05150     RBASIC(fname)->klass = 0;
05151     fnlen = RSTRING_LEN(fname);
05152     tmp = rb_str_tmp_new(MAXPATHLEN + 2);
05153     for (j=0; ext[j]; j++) {
05154         rb_str_cat2(fname, ext[j]);
05155         for (i = 0; i < RARRAY_LEN(load_path); i++) {
05156             VALUE str = RARRAY_PTR(load_path)[i];
05157 
05158             RB_GC_GUARD(str) = rb_get_path_check(str, safe_level);
05159             if (RSTRING_LEN(str) == 0) continue;
05160             file_expand_path(fname, str, 0, tmp);
05161             if (file_load_ok(RSTRING_PTR(tmp))) {
05162                 *filep = copy_path_class(tmp, *filep);
05163                 return (int)(j+1);
05164             }
05165             FL_UNSET(tmp, FL_TAINT | FL_UNTRUSTED);
05166         }
05167         rb_str_set_len(fname, fnlen);
05168     }
05169     RB_GC_GUARD(load_path);
05170     return 0;
05171 }
05172 
05173 VALUE
05174 rb_find_file(VALUE path)
05175 {
05176     return rb_find_file_safe(path, rb_safe_level());
05177 }
05178 
05179 VALUE
05180 rb_find_file_safe(VALUE path, int safe_level)
05181 {
05182     VALUE tmp, load_path;
05183     const char *f = StringValueCStr(path);
05184     int expanded = 0;
05185 
05186     if (f[0] == '~') {
05187         tmp = file_expand_path_1(path);
05188         if (safe_level >= 1 && OBJ_TAINTED(tmp)) {
05189             rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
05190         }
05191         path = copy_path_class(tmp, path);
05192         f = RSTRING_PTR(path);
05193         expanded = 1;
05194     }
05195 
05196     if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) {
05197         if (safe_level >= 1 && !fpath_check(path)) {
05198             rb_raise(rb_eSecurityError, "loading from unsafe path %s", f);
05199         }
05200         if (!file_load_ok(f)) return 0;
05201         if (!expanded)
05202             path = copy_path_class(file_expand_path_1(path), path);
05203         return path;
05204     }
05205 
05206     if (safe_level >= 4) {
05207         rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f);
05208     }
05209 
05210     RB_GC_GUARD(load_path) = rb_get_load_path();
05211     if (load_path) {
05212         long i;
05213 
05214         tmp = rb_str_tmp_new(MAXPATHLEN + 2);
05215         for (i = 0; i < RARRAY_LEN(load_path); i++) {
05216             VALUE str = RARRAY_PTR(load_path)[i];
05217             RB_GC_GUARD(str) = rb_get_path_check(str, safe_level);
05218             if (RSTRING_LEN(str) > 0) {
05219                 file_expand_path(path, str, 0, tmp);
05220                 f = RSTRING_PTR(tmp);
05221                 if (file_load_ok(f)) goto found;
05222             }
05223         }
05224         return 0;
05225     }
05226     else {
05227         return 0;               /* no path, no load */
05228     }
05229 
05230   found:
05231     if (safe_level >= 1 && !fpath_check(tmp)) {
05232         rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
05233     }
05234 
05235     return copy_path_class(tmp, path);
05236 }
05237 
05238 static void
05239 define_filetest_function(const char *name, VALUE (*func)(ANYARGS), int argc)
05240 {
05241     rb_define_module_function(rb_mFileTest, name, func, argc);
05242     rb_define_singleton_method(rb_cFile, name, func, argc);
05243 }
05244 
05245 static const char null_device[] =
05246 #if defined DOSISH
05247     "NUL"
05248 #elif defined AMIGA || defined __amigaos__
05249     "NIL"
05250 #elif defined __VMS
05251     "NL:"
05252 #else
05253     "/dev/null"
05254 #endif
05255     ;
05256 
05257 /*
05258  *  A <code>File</code> is an abstraction of any file object accessible
05259  *  by the program and is closely associated with class <code>IO</code>
05260  *  <code>File</code> includes the methods of module
05261  *  <code>FileTest</code> as class methods, allowing you to write (for
05262  *  example) <code>File.exist?("foo")</code>.
05263  *
05264  *  In the description of File methods,
05265  *  <em>permission bits</em> are a platform-specific
05266  *  set of bits that indicate permissions of a file. On Unix-based
05267  *  systems, permissions are viewed as a set of three octets, for the
05268  *  owner, the group, and the rest of the world. For each of these
05269  *  entities, permissions may be set to read, write, or execute the
05270  *  file:
05271  *
05272  *  The permission bits <code>0644</code> (in octal) would thus be
05273  *  interpreted as read/write for owner, and read-only for group and
05274  *  other. Higher-order bits may also be used to indicate the type of
05275  *  file (plain, directory, pipe, socket, and so on) and various other
05276  *  special features. If the permissions are for a directory, the
05277  *  meaning of the execute bit changes; when set the directory can be
05278  *  searched.
05279  *
05280  *  On non-Posix operating systems, there may be only the ability to
05281  *  make a file read-only or read-write. In this case, the remaining
05282  *  permission bits will be synthesized to resemble typical values. For
05283  *  instance, on Windows NT the default permission bits are
05284  *  <code>0644</code>, which means read/write for owner, read-only for
05285  *  all others. The only change that can be made is to make the file
05286  *  read-only, which is reported as <code>0444</code>.
05287  */
05288 
05289 void
05290 Init_File(void)
05291 {
05292     rb_mFileTest = rb_define_module("FileTest");
05293     rb_cFile = rb_define_class("File", rb_cIO);
05294 
05295     define_filetest_function("directory?", rb_file_directory_p, 1);
05296     define_filetest_function("exist?", rb_file_exist_p, 1);
05297     define_filetest_function("exists?", rb_file_exist_p, 1);
05298     define_filetest_function("readable?", rb_file_readable_p, 1);
05299     define_filetest_function("readable_real?", rb_file_readable_real_p, 1);
05300     define_filetest_function("world_readable?", rb_file_world_readable_p, 1);
05301     define_filetest_function("writable?", rb_file_writable_p, 1);
05302     define_filetest_function("writable_real?", rb_file_writable_real_p, 1);
05303     define_filetest_function("world_writable?", rb_file_world_writable_p, 1);
05304     define_filetest_function("executable?", rb_file_executable_p, 1);
05305     define_filetest_function("executable_real?", rb_file_executable_real_p, 1);
05306     define_filetest_function("file?", rb_file_file_p, 1);
05307     define_filetest_function("zero?", rb_file_zero_p, 1);
05308     define_filetest_function("size?", rb_file_size_p, 1);
05309     define_filetest_function("size", rb_file_s_size, 1);
05310     define_filetest_function("owned?", rb_file_owned_p, 1);
05311     define_filetest_function("grpowned?", rb_file_grpowned_p, 1);
05312 
05313     define_filetest_function("pipe?", rb_file_pipe_p, 1);
05314     define_filetest_function("symlink?", rb_file_symlink_p, 1);
05315     define_filetest_function("socket?", rb_file_socket_p, 1);
05316 
05317     define_filetest_function("blockdev?", rb_file_blockdev_p, 1);
05318     define_filetest_function("chardev?", rb_file_chardev_p, 1);
05319 
05320     define_filetest_function("setuid?", rb_file_suid_p, 1);
05321     define_filetest_function("setgid?", rb_file_sgid_p, 1);
05322     define_filetest_function("sticky?", rb_file_sticky_p, 1);
05323 
05324     define_filetest_function("identical?", rb_file_identical_p, 2);
05325 
05326     rb_define_singleton_method(rb_cFile, "stat",  rb_file_s_stat, 1);
05327     rb_define_singleton_method(rb_cFile, "lstat", rb_file_s_lstat, 1);
05328     rb_define_singleton_method(rb_cFile, "ftype", rb_file_s_ftype, 1);
05329 
05330     rb_define_singleton_method(rb_cFile, "atime", rb_file_s_atime, 1);
05331     rb_define_singleton_method(rb_cFile, "mtime", rb_file_s_mtime, 1);
05332     rb_define_singleton_method(rb_cFile, "ctime", rb_file_s_ctime, 1);
05333 
05334     rb_define_singleton_method(rb_cFile, "utime", rb_file_s_utime, -1);
05335     rb_define_singleton_method(rb_cFile, "chmod", rb_file_s_chmod, -1);
05336     rb_define_singleton_method(rb_cFile, "chown", rb_file_s_chown, -1);
05337     rb_define_singleton_method(rb_cFile, "lchmod", rb_file_s_lchmod, -1);
05338     rb_define_singleton_method(rb_cFile, "lchown", rb_file_s_lchown, -1);
05339 
05340     rb_define_singleton_method(rb_cFile, "link", rb_file_s_link, 2);
05341     rb_define_singleton_method(rb_cFile, "symlink", rb_file_s_symlink, 2);
05342     rb_define_singleton_method(rb_cFile, "readlink", rb_file_s_readlink, 1);
05343 
05344     rb_define_singleton_method(rb_cFile, "unlink", rb_file_s_unlink, -2);
05345     rb_define_singleton_method(rb_cFile, "delete", rb_file_s_unlink, -2);
05346     rb_define_singleton_method(rb_cFile, "rename", rb_file_s_rename, 2);
05347     rb_define_singleton_method(rb_cFile, "umask", rb_file_s_umask, -1);
05348     rb_define_singleton_method(rb_cFile, "truncate", rb_file_s_truncate, 2);
05349     rb_define_singleton_method(rb_cFile, "expand_path", rb_file_s_expand_path, -1);
05350     rb_define_singleton_method(rb_cFile, "absolute_path", rb_file_s_absolute_path, -1);
05351     rb_define_singleton_method(rb_cFile, "realpath", rb_file_s_realpath, -1);
05352     rb_define_singleton_method(rb_cFile, "realdirpath", rb_file_s_realdirpath, -1);
05353     rb_define_singleton_method(rb_cFile, "basename", rb_file_s_basename, -1);
05354     rb_define_singleton_method(rb_cFile, "dirname", rb_file_s_dirname, 1);
05355     rb_define_singleton_method(rb_cFile, "extname", rb_file_s_extname, 1);
05356     rb_define_singleton_method(rb_cFile, "path", rb_file_s_path, 1);
05357 
05358     separator = rb_obj_freeze(rb_usascii_str_new2("/"));
05359     rb_define_const(rb_cFile, "Separator", separator);
05360     rb_define_const(rb_cFile, "SEPARATOR", separator);
05361     rb_define_singleton_method(rb_cFile, "split",  rb_file_s_split, 1);
05362     rb_define_singleton_method(rb_cFile, "join",   rb_file_s_join, -2);
05363 
05364 #ifdef DOSISH
05365     rb_define_const(rb_cFile, "ALT_SEPARATOR", rb_obj_freeze(rb_usascii_str_new2(file_alt_separator)));
05366 #else
05367     rb_define_const(rb_cFile, "ALT_SEPARATOR", Qnil);
05368 #endif
05369     rb_define_const(rb_cFile, "PATH_SEPARATOR", rb_obj_freeze(rb_str_new2(PATH_SEP)));
05370 
05371     rb_define_method(rb_cIO, "stat",  rb_io_stat, 0); /* this is IO's method */
05372     rb_define_method(rb_cFile, "lstat",  rb_file_lstat, 0);
05373 
05374     rb_define_method(rb_cFile, "atime", rb_file_atime, 0);
05375     rb_define_method(rb_cFile, "mtime", rb_file_mtime, 0);
05376     rb_define_method(rb_cFile, "ctime", rb_file_ctime, 0);
05377     rb_define_method(rb_cFile, "size", rb_file_size, 0);
05378 
05379     rb_define_method(rb_cFile, "chmod", rb_file_chmod, 1);
05380     rb_define_method(rb_cFile, "chown", rb_file_chown, 2);
05381     rb_define_method(rb_cFile, "truncate", rb_file_truncate, 1);
05382 
05383     rb_define_method(rb_cFile, "flock", rb_file_flock, 1);
05384 
05385     rb_mFConst = rb_define_module_under(rb_cFile, "Constants");
05386     rb_include_module(rb_cIO, rb_mFConst);
05387     rb_file_const("LOCK_SH", INT2FIX(LOCK_SH));
05388     rb_file_const("LOCK_EX", INT2FIX(LOCK_EX));
05389     rb_file_const("LOCK_UN", INT2FIX(LOCK_UN));
05390     rb_file_const("LOCK_NB", INT2FIX(LOCK_NB));
05391 
05392     rb_file_const("NULL", rb_obj_freeze(rb_usascii_str_new2(null_device)));
05393 
05394     rb_define_method(rb_cFile, "path",  rb_file_path, 0);
05395     rb_define_method(rb_cFile, "to_path",  rb_file_path, 0);
05396     rb_define_global_function("test", rb_f_test, -1);
05397 
05398     rb_cStat = rb_define_class_under(rb_cFile, "Stat", rb_cObject);
05399     rb_define_alloc_func(rb_cStat,  rb_stat_s_alloc);
05400     rb_define_method(rb_cStat, "initialize", rb_stat_init, 1);
05401     rb_define_method(rb_cStat, "initialize_copy", rb_stat_init_copy, 1);
05402 
05403     rb_include_module(rb_cStat, rb_mComparable);
05404 
05405     rb_define_method(rb_cStat, "<=>", rb_stat_cmp, 1);
05406 
05407     rb_define_method(rb_cStat, "dev", rb_stat_dev, 0);
05408     rb_define_method(rb_cStat, "dev_major", rb_stat_dev_major, 0);
05409     rb_define_method(rb_cStat, "dev_minor", rb_stat_dev_minor, 0);
05410     rb_define_method(rb_cStat, "ino", rb_stat_ino, 0);
05411     rb_define_method(rb_cStat, "mode", rb_stat_mode, 0);
05412     rb_define_method(rb_cStat, "nlink", rb_stat_nlink, 0);
05413     rb_define_method(rb_cStat, "uid", rb_stat_uid, 0);
05414     rb_define_method(rb_cStat, "gid", rb_stat_gid, 0);
05415     rb_define_method(rb_cStat, "rdev", rb_stat_rdev, 0);
05416     rb_define_method(rb_cStat, "rdev_major", rb_stat_rdev_major, 0);
05417     rb_define_method(rb_cStat, "rdev_minor", rb_stat_rdev_minor, 0);
05418     rb_define_method(rb_cStat, "size", rb_stat_size, 0);
05419     rb_define_method(rb_cStat, "blksize", rb_stat_blksize, 0);
05420     rb_define_method(rb_cStat, "blocks", rb_stat_blocks, 0);
05421     rb_define_method(rb_cStat, "atime", rb_stat_atime, 0);
05422     rb_define_method(rb_cStat, "mtime", rb_stat_mtime, 0);
05423     rb_define_method(rb_cStat, "ctime", rb_stat_ctime, 0);
05424 
05425     rb_define_method(rb_cStat, "inspect", rb_stat_inspect, 0);
05426 
05427     rb_define_method(rb_cStat, "ftype", rb_stat_ftype, 0);
05428 
05429     rb_define_method(rb_cStat, "directory?",  rb_stat_d, 0);
05430     rb_define_method(rb_cStat, "readable?",  rb_stat_r, 0);
05431     rb_define_method(rb_cStat, "readable_real?",  rb_stat_R, 0);
05432     rb_define_method(rb_cStat, "world_readable?", rb_stat_wr, 0);
05433     rb_define_method(rb_cStat, "writable?",  rb_stat_w, 0);
05434     rb_define_method(rb_cStat, "writable_real?",  rb_stat_W, 0);
05435     rb_define_method(rb_cStat, "world_writable?", rb_stat_ww, 0);
05436     rb_define_method(rb_cStat, "executable?",  rb_stat_x, 0);
05437     rb_define_method(rb_cStat, "executable_real?",  rb_stat_X, 0);
05438     rb_define_method(rb_cStat, "file?",  rb_stat_f, 0);
05439     rb_define_method(rb_cStat, "zero?",  rb_stat_z, 0);
05440     rb_define_method(rb_cStat, "size?",  rb_stat_s, 0);
05441     rb_define_method(rb_cStat, "owned?",  rb_stat_owned, 0);
05442     rb_define_method(rb_cStat, "grpowned?",  rb_stat_grpowned, 0);
05443 
05444     rb_define_method(rb_cStat, "pipe?",  rb_stat_p, 0);
05445     rb_define_method(rb_cStat, "symlink?",  rb_stat_l, 0);
05446     rb_define_method(rb_cStat, "socket?",  rb_stat_S, 0);
05447 
05448     rb_define_method(rb_cStat, "blockdev?",  rb_stat_b, 0);
05449     rb_define_method(rb_cStat, "chardev?",  rb_stat_c, 0);
05450 
05451     rb_define_method(rb_cStat, "setuid?",  rb_stat_suid, 0);
05452     rb_define_method(rb_cStat, "setgid?",  rb_stat_sgid, 0);
05453     rb_define_method(rb_cStat, "sticky?",  rb_stat_sticky, 0);
05454 }
05455