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