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