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