Ruby 1.9.2p136(2010-12-25revision30365)

- Main Page
- Modules
- Data Structures
- Files

- File List
- Globals

# file.c

Go to the documentation of this file.

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