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