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