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