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