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 #if USE_NTFS
02788 static char *
02789 ntfs_tail(const char *path, const char *end, rb_encoding *enc)
02790 {
02791 while (path < end && *path == '.') path++;
02792 while (path < end && *path != ':') {
02793 if (istrailinggarbage(*path)) {
02794 const char *last = path++;
02795 while (path < end && istrailinggarbage(*path)) path++;
02796 if (path >= end || *path == ':') return (char *)last;
02797 }
02798 else if (isdirsep(*path)) {
02799 const char *last = path++;
02800 while (path < end && isdirsep(*path)) path++;
02801 if (path >= end) return (char *)last;
02802 if (*path == ':') path++;
02803 }
02804 else {
02805 Inc(path, end, enc);
02806 }
02807 }
02808 return (char *)path;
02809 }
02810 #endif
02811
02812 #define BUFCHECK(cond) do {\
02813 bdiff = p - buf;\
02814 if (cond) {\
02815 do {buflen *= 2;} while (cond);\
02816 rb_str_resize(result, buflen);\
02817 buf = RSTRING_PTR(result);\
02818 p = buf + bdiff;\
02819 pend = buf + buflen;\
02820 }\
02821 } while (0)
02822
02823 #define BUFINIT() (\
02824 p = buf = RSTRING_PTR(result),\
02825 buflen = RSTRING_LEN(result),\
02826 pend = p + buflen)
02827
02828 VALUE
02829 rb_home_dir(const char *user, VALUE result)
02830 {
02831 const char *dir;
02832 char *buf;
02833 #if defined DOSISH || defined __CYGWIN__
02834 char *p, *bend;
02835 #endif
02836 long dirlen;
02837 rb_encoding *enc;
02838
02839 if (!user || !*user) {
02840 if (!(dir = getenv("HOME"))) {
02841 rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'");
02842 }
02843 dirlen = strlen(dir);
02844 rb_str_resize(result, dirlen);
02845 memcpy(buf = RSTRING_PTR(result), dir, dirlen);
02846 }
02847 else {
02848 #ifdef HAVE_PWD_H
02849 struct passwd *pwPtr = getpwnam(user);
02850 if (!pwPtr) {
02851 endpwent();
02852 rb_raise(rb_eArgError, "user %s doesn't exist", user);
02853 }
02854 dirlen = strlen(pwPtr->pw_dir);
02855 rb_str_resize(result, dirlen);
02856 memcpy(buf = RSTRING_PTR(result), pwPtr->pw_dir, dirlen + 1);
02857 endpwent();
02858 #else
02859 return Qnil;
02860 #endif
02861 }
02862 enc = rb_filesystem_encoding();
02863 rb_enc_associate(result, enc);
02864 #if defined DOSISH || defined __CYGWIN__
02865 for (bend = (p = buf) + dirlen; p < bend; Inc(p, bend, enc)) {
02866 if (*p == '\\') {
02867 *p = '/';
02868 }
02869 }
02870 #endif
02871 return result;
02872 }
02873
02874 #ifndef _WIN32
02875 static char *
02876 append_fspath(VALUE result, VALUE fname, char *dir, rb_encoding **enc, rb_encoding *fsenc)
02877 {
02878 char *buf, *cwdp = dir;
02879 VALUE dirname = Qnil;
02880 size_t dirlen = strlen(dir), buflen = rb_str_capacity(result);
02881
02882 *enc = fsenc;
02883 do {buflen *= 2;} while (dirlen > buflen);
02884 rb_str_resize(result, buflen);
02885 buf = RSTRING_PTR(result);
02886 memcpy(buf, cwdp, dirlen);
02887 xfree(dir);
02888 if (!NIL_P(dirname)) rb_str_resize(dirname, 0);
02889 rb_enc_associate(result, *enc);
02890 return buf + dirlen;
02891 }
02892
02893 VALUE
02894 rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_name, VALUE result)
02895 {
02896 const char *s, *b, *fend;
02897 char *buf, *p, *pend, *root;
02898 size_t buflen, bdiff;
02899 int tainted;
02900 rb_encoding *enc, *fsenc = rb_filesystem_encoding();
02901
02902 s = StringValuePtr(fname);
02903 fend = s + RSTRING_LEN(fname);
02904 enc = rb_enc_get(fname);
02905 BUFINIT();
02906 tainted = OBJ_TAINTED(fname);
02907
02908 if (s[0] == '~' && abs_mode == 0) {
02909 long userlen = 0;
02910 tainted = 1;
02911 if (isdirsep(s[1]) || s[1] == '\0') {
02912 buf = 0;
02913 b = 0;
02914 rb_str_set_len(result, 0);
02915 if (*++s) ++s;
02916 }
02917 else {
02918 s = nextdirsep(b = s, fend, enc);
02919 userlen = s - b;
02920 BUFCHECK(bdiff + userlen >= buflen);
02921 memcpy(p, b, userlen);
02922 rb_str_set_len(result, userlen);
02923 buf = p + 1;
02924 p += userlen;
02925 }
02926 if (NIL_P(rb_home_dir(buf, result))) {
02927 rb_raise(rb_eArgError, "can't find user %s", buf);
02928 }
02929 if (!rb_is_absolute_path(RSTRING_PTR(result))) {
02930 if (userlen) {
02931 rb_raise(rb_eArgError, "non-absolute home of %.*s", (int)userlen, b);
02932 }
02933 else {
02934 rb_raise(rb_eArgError, "non-absolute home");
02935 }
02936 }
02937 BUFINIT();
02938 p = pend;
02939 }
02940 #ifdef DOSISH_DRIVE_LETTER
02941
02942 else if (has_drive_letter(s)) {
02943 if (isdirsep(s[2])) {
02944
02945
02946 BUFCHECK(bdiff + 2 >= buflen);
02947 memcpy(p, s, 2);
02948 p += 2;
02949 s += 2;
02950 rb_enc_copy(result, fname);
02951 }
02952 else {
02953
02954 int same = 0;
02955 if (!NIL_P(dname) && !not_same_drive(dname, s[0])) {
02956 rb_file_expand_path_internal(dname, Qnil, abs_mode, long_name, result);
02957 BUFINIT();
02958 if (has_drive_letter(p) && TOLOWER(p[0]) == TOLOWER(s[0])) {
02959
02960 same = 1;
02961 }
02962 }
02963 if (!same) {
02964 char *e = append_fspath(result, fname, getcwdofdrv(*s), &enc, fsenc);
02965 tainted = 1;
02966 BUFINIT();
02967 p = e;
02968 }
02969 else {
02970 rb_enc_associate(result, enc = rb_enc_check(result, fname));
02971 p = pend;
02972 }
02973 p = chompdirsep(skiproot(buf, p, enc), p, enc);
02974 s += 2;
02975 }
02976 }
02977 #endif
02978 else if (!rb_is_absolute_path(s)) {
02979 if (!NIL_P(dname)) {
02980 rb_file_expand_path_internal(dname, Qnil, abs_mode, long_name, result);
02981 rb_enc_associate(result, rb_enc_check(result, fname));
02982 BUFINIT();
02983 p = pend;
02984 }
02985 else {
02986 char *e = append_fspath(result, fname, my_getcwd(), &enc, fsenc);
02987 tainted = 1;
02988 BUFINIT();
02989 p = e;
02990 }
02991 #if defined DOSISH || defined __CYGWIN__
02992 if (isdirsep(*s)) {
02993
02994
02995 p = skipprefix(buf, p, enc);
02996 }
02997 else
02998 #endif
02999 p = chompdirsep(skiproot(buf, p, enc), p, enc);
03000 }
03001 else {
03002 size_t len;
03003 b = s;
03004 do s++; while (isdirsep(*s));
03005 len = s - b;
03006 p = buf + len;
03007 BUFCHECK(bdiff >= buflen);
03008 memset(buf, '/', len);
03009 rb_str_set_len(result, len);
03010 rb_enc_associate(result, rb_enc_check(result, fname));
03011 }
03012 if (p > buf && p[-1] == '/')
03013 --p;
03014 else {
03015 rb_str_set_len(result, p-buf);
03016 BUFCHECK(bdiff + 1 >= buflen);
03017 *p = '/';
03018 }
03019
03020 rb_str_set_len(result, p-buf+1);
03021 BUFCHECK(bdiff + 1 >= buflen);
03022 p[1] = 0;
03023 root = skipprefix(buf, p+1, enc);
03024
03025 b = s;
03026 while (*s) {
03027 switch (*s) {
03028 case '.':
03029 if (b == s++) {
03030 switch (*s) {
03031 case '\0':
03032 b = s;
03033 break;
03034 case '.':
03035 if (*(s+1) == '\0' || isdirsep(*(s+1))) {
03036
03037 char *n;
03038 *p = '\0';
03039 if (!(n = strrdirsep(root, p, enc))) {
03040 *p = '/';
03041 }
03042 else {
03043 p = n;
03044 }
03045 b = ++s;
03046 }
03047 #if USE_NTFS
03048 else {
03049 do ++s; while (istrailinggarbage(*s));
03050 }
03051 #endif
03052 break;
03053 case '/':
03054 #if defined DOSISH || defined __CYGWIN__
03055 case '\\':
03056 #endif
03057 b = ++s;
03058 break;
03059 default:
03060
03061 break;
03062 }
03063 }
03064 #if USE_NTFS
03065 else {
03066 --s;
03067 case ' ': {
03068 const char *e = s;
03069 while (s < fend && istrailinggarbage(*s)) s++;
03070 if (!*s) {
03071 s = e;
03072 goto endpath;
03073 }
03074 }
03075 }
03076 #endif
03077 break;
03078 case '/':
03079 #if defined DOSISH || defined __CYGWIN__
03080 case '\\':
03081 #endif
03082 if (s > b) {
03083 long rootdiff = root - buf;
03084 rb_str_set_len(result, p-buf+1);
03085 BUFCHECK(bdiff + (s-b+1) >= buflen);
03086 root = buf + rootdiff;
03087 memcpy(++p, b, s-b);
03088 p += s-b;
03089 *p = '/';
03090 }
03091 b = ++s;
03092 break;
03093 default:
03094 Inc(s, fend, enc);
03095 break;
03096 }
03097 }
03098
03099 if (s > b) {
03100 #if USE_NTFS
03101 static const char prime[] = ":$DATA";
03102 enum {prime_len = sizeof(prime) -1};
03103 endpath:
03104 if (s > b + prime_len && strncasecmp(s - prime_len, prime, prime_len) == 0) {
03105
03106
03107 if (*(s - (prime_len+1)) == ':') {
03108 s -= prime_len + 1;
03109 }
03110 else if (memchr(b, ':', s - prime_len - b)) {
03111 s -= prime_len;
03112 }
03113 }
03114 #endif
03115 rb_str_set_len(result, p-buf+1);
03116 BUFCHECK(bdiff + (s-b) >= buflen);
03117 memcpy(++p, b, s-b);
03118 p += s-b;
03119 rb_str_set_len(result, p-buf);
03120 }
03121 if (p == skiproot(buf, p + !!*p, enc) - 1) p++;
03122
03123 #if USE_NTFS
03124 *p = '\0';
03125 if ((s = strrdirsep(b = buf, p, enc)) != 0 && !strpbrk(s, "*?")) {
03126 VALUE tmp, v;
03127 size_t len;
03128 rb_encoding *enc;
03129 WCHAR *wstr;
03130 WIN32_FIND_DATAW wfd;
03131 HANDLE h;
03132 #ifdef __CYGWIN__
03133 #ifdef HAVE_CYGWIN_CONV_PATH
03134 char *w32buf = NULL;
03135 const int flags = CCP_POSIX_TO_WIN_A | CCP_RELATIVE;
03136 #else
03137 char w32buf[MAXPATHLEN];
03138 #endif
03139 const char *path;
03140 ssize_t bufsize;
03141 int lnk_added = 0, is_symlink = 0;
03142 struct stat st;
03143 p = (char *)s;
03144 len = strlen(p);
03145 if (lstat(buf, &st) == 0 && S_ISLNK(st.st_mode)) {
03146 is_symlink = 1;
03147 if (len > 4 && STRCASECMP(p + len - 4, ".lnk") != 0) {
03148 lnk_added = 1;
03149 }
03150 }
03151 path = *buf ? buf : "/";
03152 #ifdef HAVE_CYGWIN_CONV_PATH
03153 bufsize = cygwin_conv_path(flags, path, NULL, 0);
03154 if (bufsize > 0) {
03155 bufsize += len;
03156 if (lnk_added) bufsize += 4;
03157 w32buf = ALLOCA_N(char, bufsize);
03158 if (cygwin_conv_path(flags, path, w32buf, bufsize) == 0) {
03159 b = w32buf;
03160 }
03161 }
03162 #else
03163 bufsize = MAXPATHLEN;
03164 if (cygwin_conv_to_win32_path(path, w32buf) == 0) {
03165 b = w32buf;
03166 }
03167 #endif
03168 if (is_symlink && b == w32buf) {
03169 *p = '\\';
03170 strlcat(w32buf, p, bufsize);
03171 if (lnk_added) {
03172 strlcat(w32buf, ".lnk", bufsize);
03173 }
03174 }
03175 else {
03176 lnk_added = 0;
03177 }
03178 *p = '/';
03179 #endif
03180 rb_str_set_len(result, p - buf + strlen(p));
03181 enc = rb_enc_get(result);
03182 tmp = result;
03183 if (enc != rb_utf8_encoding() && rb_enc_str_coderange(result) != ENC_CODERANGE_7BIT) {
03184 tmp = rb_str_encode_ospath(result);
03185 }
03186 len = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, NULL, 0);
03187 wstr = ALLOCV_N(WCHAR, v, len);
03188 MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, wstr, len);
03189 if (tmp != result) rb_str_resize(tmp, 0);
03190 h = FindFirstFileW(wstr, &wfd);
03191 ALLOCV_END(v);
03192 if (h != INVALID_HANDLE_VALUE) {
03193 size_t wlen;
03194 FindClose(h);
03195 len = lstrlenW(wfd.cFileName);
03196 #ifdef __CYGWIN__
03197 if (lnk_added && len > 4 &&
03198 wcscasecmp(wfd.cFileName + len - 4, L".lnk") == 0) {
03199 wfd.cFileName[len -= 4] = L'\0';
03200 }
03201 #else
03202 p = (char *)s;
03203 #endif
03204 ++p;
03205 wlen = (int)len;
03206 len = WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, NULL, 0, NULL, NULL);
03207 BUFCHECK(bdiff + len >= buflen);
03208 WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, p, len + 1, NULL, NULL);
03209 if (tmp != result) {
03210 rb_str_buf_cat(tmp, p, len);
03211 tmp = rb_str_encode(tmp, rb_enc_from_encoding(enc), 0, Qnil);
03212 len = RSTRING_LEN(tmp);
03213 BUFCHECK(bdiff + len >= buflen);
03214 memcpy(p, RSTRING_PTR(tmp), len);
03215 rb_str_resize(tmp, 0);
03216 }
03217 p += len;
03218 }
03219 #ifdef __CYGWIN__
03220 else {
03221 p += strlen(p);
03222 }
03223 #endif
03224 }
03225 #endif
03226
03227 if (tainted) OBJ_TAINT(result);
03228 rb_str_set_len(result, p - buf);
03229 rb_enc_check(fname, result);
03230 ENC_CODERANGE_CLEAR(result);
03231 return result;
03232 }
03233 #endif
03234
03235 #define EXPAND_PATH_BUFFER() rb_enc_str_new(0, MAXPATHLEN + 2, rb_filesystem_encoding())
03236
03237 #define check_expand_path_args(fname, dname) \
03238 (((fname) = rb_get_path(fname)), \
03239 (void)(NIL_P(dname) ? (dname) : ((dname) = rb_get_path(dname))))
03240
03241 static VALUE
03242 file_expand_path_1(VALUE fname)
03243 {
03244 return rb_file_expand_path_internal(fname, Qnil, 0, 0, EXPAND_PATH_BUFFER());
03245 }
03246
03247 VALUE
03248 rb_file_expand_path(VALUE fname, VALUE dname)
03249 {
03250 check_expand_path_args(fname, dname);
03251 return rb_file_expand_path_internal(fname, dname, 0, 1, EXPAND_PATH_BUFFER());
03252 }
03253
03254 VALUE
03255 rb_file_expand_path_fast(VALUE fname, VALUE dname)
03256 {
03257 check_expand_path_args(fname, dname);
03258 return rb_file_expand_path_internal(fname, dname, 0, 0, EXPAND_PATH_BUFFER());
03259 }
03260
03261
03262
03263
03264
03265
03266
03267
03268
03269
03270
03271
03272
03273
03274
03275
03276
03277
03278 VALUE
03279 rb_file_s_expand_path(int argc, VALUE *argv)
03280 {
03281 VALUE fname, dname;
03282
03283 if (argc == 1) {
03284 return rb_file_expand_path(argv[0], Qnil);
03285 }
03286 rb_scan_args(argc, argv, "11", &fname, &dname);
03287
03288 return rb_file_expand_path(fname, dname);
03289 }
03290
03291 VALUE
03292 rb_file_absolute_path(VALUE fname, VALUE dname)
03293 {
03294 check_expand_path_args(fname, dname);
03295 return rb_file_expand_path_internal(fname, dname, 1, 1, EXPAND_PATH_BUFFER());
03296 }
03297
03298
03299
03300
03301
03302
03303
03304
03305
03306
03307
03308
03309
03310
03311 VALUE
03312 rb_file_s_absolute_path(int argc, VALUE *argv)
03313 {
03314 VALUE fname, dname;
03315
03316 if (argc == 1) {
03317 return rb_file_absolute_path(argv[0], Qnil);
03318 }
03319 rb_scan_args(argc, argv, "11", &fname, &dname);
03320
03321 return rb_file_absolute_path(fname, dname);
03322 }
03323
03324 static void
03325 realpath_rec(long *prefixlenp, VALUE *resolvedp, const char *unresolved, VALUE loopcheck, int strict, int last)
03326 {
03327 const char *pend = unresolved + strlen(unresolved);
03328 rb_encoding *enc = rb_enc_get(*resolvedp);
03329 ID resolving;
03330 CONST_ID(resolving, "resolving");
03331 while (unresolved < pend) {
03332 const char *testname = unresolved;
03333 const char *unresolved_firstsep = rb_enc_path_next(unresolved, pend, enc);
03334 long testnamelen = unresolved_firstsep - unresolved;
03335 const char *unresolved_nextname = unresolved_firstsep;
03336 while (unresolved_nextname < pend && isdirsep(*unresolved_nextname))
03337 unresolved_nextname++;
03338 unresolved = unresolved_nextname;
03339 if (testnamelen == 1 && testname[0] == '.') {
03340 }
03341 else if (testnamelen == 2 && testname[0] == '.' && testname[1] == '.') {
03342 if (*prefixlenp < RSTRING_LEN(*resolvedp)) {
03343 const char *resolved_str = RSTRING_PTR(*resolvedp);
03344 const char *resolved_names = resolved_str + *prefixlenp;
03345 const char *lastsep = strrdirsep(resolved_names, resolved_str + RSTRING_LEN(*resolvedp), enc);
03346 long len = lastsep ? lastsep - resolved_names : 0;
03347 rb_str_resize(*resolvedp, *prefixlenp + len);
03348 }
03349 }
03350 else {
03351 VALUE checkval;
03352 VALUE testpath = rb_str_dup(*resolvedp);
03353 if (*prefixlenp < RSTRING_LEN(testpath))
03354 rb_str_cat2(testpath, "/");
03355 rb_str_cat(testpath, testname, testnamelen);
03356 checkval = rb_hash_aref(loopcheck, testpath);
03357 if (!NIL_P(checkval)) {
03358 if (checkval == ID2SYM(resolving)) {
03359 errno = ELOOP;
03360 rb_sys_fail_path(testpath);
03361 }
03362 else {
03363 *resolvedp = rb_str_dup(checkval);
03364 }
03365 }
03366 else {
03367 struct stat sbuf;
03368 int ret;
03369 VALUE testpath2 = rb_str_encode_ospath(testpath);
03370 ret = lstat(RSTRING_PTR(testpath2), &sbuf);
03371 if (ret == -1) {
03372 if (errno == ENOENT) {
03373 if (strict || !last || *unresolved_firstsep)
03374 rb_sys_fail_path(testpath);
03375 *resolvedp = testpath;
03376 break;
03377 }
03378 else {
03379 rb_sys_fail_path(testpath);
03380 }
03381 }
03382 #ifdef HAVE_READLINK
03383 if (S_ISLNK(sbuf.st_mode)) {
03384 VALUE link;
03385 volatile VALUE link_orig = Qnil;
03386 const char *link_prefix, *link_names;
03387 long link_prefixlen;
03388 rb_hash_aset(loopcheck, testpath, ID2SYM(resolving));
03389 link = rb_readlink(testpath);
03390 link_prefix = RSTRING_PTR(link);
03391 link_names = skipprefixroot(link_prefix, link_prefix + RSTRING_LEN(link), rb_enc_get(link));
03392 link_prefixlen = link_names - link_prefix;
03393 if (link_prefixlen > 0) {
03394 rb_encoding *enc, *linkenc = rb_enc_get(link);
03395 link_orig = link;
03396 link = rb_str_subseq(link, 0, link_prefixlen);
03397 enc = rb_enc_check(*resolvedp, link);
03398 if (enc != linkenc) link = rb_str_conv_enc(link, linkenc, enc);
03399 *resolvedp = link;
03400 *prefixlenp = link_prefixlen;
03401 }
03402 realpath_rec(prefixlenp, resolvedp, link_names, loopcheck, strict, *unresolved_firstsep == '\0');
03403 RB_GC_GUARD(link_orig);
03404 rb_hash_aset(loopcheck, testpath, rb_str_dup_frozen(*resolvedp));
03405 }
03406 else
03407 #endif
03408 {
03409 VALUE s = rb_str_dup_frozen(testpath);
03410 rb_hash_aset(loopcheck, s, s);
03411 *resolvedp = testpath;
03412 }
03413 }
03414 }
03415 }
03416 }
03417
03418 VALUE
03419 rb_realpath_internal(VALUE basedir, VALUE path, int strict)
03420 {
03421 long prefixlen;
03422 VALUE resolved;
03423 volatile VALUE unresolved_path;
03424 VALUE loopcheck;
03425 volatile VALUE curdir = Qnil;
03426
03427 rb_encoding *enc;
03428 char *path_names = NULL, *basedir_names = NULL, *curdir_names = NULL;
03429 char *ptr, *prefixptr = NULL, *pend;
03430 long len;
03431
03432 rb_secure(2);
03433
03434 FilePathValue(path);
03435 unresolved_path = rb_str_dup_frozen(path);
03436
03437 if (!NIL_P(basedir)) {
03438 FilePathValue(basedir);
03439 basedir = rb_str_dup_frozen(basedir);
03440 }
03441
03442 RSTRING_GETMEM(unresolved_path, ptr, len);
03443 path_names = skipprefixroot(ptr, ptr + len, rb_enc_get(unresolved_path));
03444 if (ptr != path_names) {
03445 resolved = rb_str_subseq(unresolved_path, 0, path_names - ptr);
03446 goto root_found;
03447 }
03448
03449 if (!NIL_P(basedir)) {
03450 RSTRING_GETMEM(basedir, ptr, len);
03451 basedir_names = skipprefixroot(ptr, ptr + len, rb_enc_get(basedir));
03452 if (ptr != basedir_names) {
03453 resolved = rb_str_subseq(basedir, 0, basedir_names - ptr);
03454 goto root_found;
03455 }
03456 }
03457
03458 curdir = rb_dir_getwd();
03459 RSTRING_GETMEM(curdir, ptr, len);
03460 curdir_names = skipprefixroot(ptr, ptr + len, rb_enc_get(curdir));
03461 resolved = rb_str_subseq(curdir, 0, curdir_names - ptr);
03462
03463 root_found:
03464 RSTRING_GETMEM(resolved, prefixptr, prefixlen);
03465 pend = prefixptr + prefixlen;
03466 enc = rb_enc_get(resolved);
03467 ptr = chompdirsep(prefixptr, pend, enc);
03468 if (ptr < pend) {
03469 prefixlen = ++ptr - prefixptr;
03470 rb_str_set_len(resolved, prefixlen);
03471 }
03472 #ifdef FILE_ALT_SEPARATOR
03473 while (prefixptr < ptr) {
03474 if (*prefixptr == FILE_ALT_SEPARATOR) {
03475 *prefixptr = '/';
03476 }
03477 Inc(prefixptr, pend, enc);
03478 }
03479 #endif
03480
03481 loopcheck = rb_hash_new();
03482 if (curdir_names)
03483 realpath_rec(&prefixlen, &resolved, curdir_names, loopcheck, 1, 0);
03484 if (basedir_names)
03485 realpath_rec(&prefixlen, &resolved, basedir_names, loopcheck, 1, 0);
03486 realpath_rec(&prefixlen, &resolved, path_names, loopcheck, strict, 1);
03487
03488 OBJ_TAINT(resolved);
03489 return resolved;
03490 }
03491
03492
03493
03494
03495
03496
03497
03498
03499
03500
03501
03502
03503
03504
03505 static VALUE
03506 rb_file_s_realpath(int argc, VALUE *argv, VALUE klass)
03507 {
03508 VALUE path, basedir;
03509 rb_scan_args(argc, argv, "11", &path, &basedir);
03510 return rb_realpath_internal(basedir, path, 1);
03511 }
03512
03513
03514
03515
03516
03517
03518
03519
03520
03521
03522
03523
03524
03525 static VALUE
03526 rb_file_s_realdirpath(int argc, VALUE *argv, VALUE klass)
03527 {
03528 VALUE path, basedir;
03529 rb_scan_args(argc, argv, "11", &path, &basedir);
03530 return rb_realpath_internal(basedir, path, 0);
03531 }
03532
03533 static size_t
03534 rmext(const char *p, long l0, long l1, const char *e, long l2, rb_encoding *enc)
03535 {
03536 int len1, len2;
03537 unsigned int c;
03538 const char *s, *last;
03539
03540 if (!e || !l2) return 0;
03541
03542 c = rb_enc_codepoint_len(e, e + l2, &len1, enc);
03543 if (rb_enc_ascget(e + len1, e + l2, &len2, enc) == '*' && len1 + len2 == l2) {
03544 if (c == '.') return l0;
03545 s = p;
03546 e = p + l1;
03547 last = e;
03548 while (s < e) {
03549 if (rb_enc_codepoint_len(s, e, &len1, enc) == c) last = s;
03550 s += len1;
03551 }
03552 return last - p;
03553 }
03554 if (l1 < l2) return l1;
03555
03556 s = p+l1-l2;
03557 if (rb_enc_left_char_head(p, s, p+l1, enc) != s) return 0;
03558 #if CASEFOLD_FILESYSTEM
03559 #define fncomp strncasecmp
03560 #else
03561 #define fncomp strncmp
03562 #endif
03563 if (fncomp(s, e, l2) == 0) {
03564 return l1-l2;
03565 }
03566 return 0;
03567 }
03568
03569 const char *
03570 ruby_enc_find_basename(const char *name, long *baselen, long *alllen, rb_encoding *enc)
03571 {
03572 const char *p, *q, *e, *end;
03573 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
03574 const char *root;
03575 #endif
03576 long f = 0, n = -1;
03577
03578 end = name + *alllen;
03579 name = skipprefix(name, end, enc);
03580 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
03581 root = name;
03582 #endif
03583 while (isdirsep(*name))
03584 name++;
03585 if (!*name) {
03586 p = name - 1;
03587 f = 1;
03588 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
03589 if (name != root) {
03590
03591 }
03592 #ifdef DOSISH_DRIVE_LETTER
03593 else if (*p == ':') {
03594 p++;
03595 f = 0;
03596 }
03597 #endif
03598 #ifdef DOSISH_UNC
03599 else {
03600 p = "/";
03601 }
03602 #endif
03603 #endif
03604 }
03605 else {
03606 if (!(p = strrdirsep(name, end, enc))) {
03607 p = name;
03608 }
03609 else {
03610 while (isdirsep(*p)) p++;
03611 }
03612 #if USE_NTFS
03613 n = ntfs_tail(p, end, enc) - p;
03614 #else
03615 n = chompdirsep(p, end, enc) - p;
03616 #endif
03617 for (q = p; q - p < n && *q == '.'; q++);
03618 for (e = 0; q - p < n; Inc(q, end, enc)) {
03619 if (*q == '.') e = q;
03620 }
03621 if (e) f = e - p;
03622 else f = n;
03623 }
03624
03625 if (baselen)
03626 *baselen = f;
03627 if (alllen)
03628 *alllen = n;
03629 return p;
03630 }
03631
03632
03633
03634
03635
03636
03637
03638
03639
03640
03641
03642
03643
03644
03645
03646 static VALUE
03647 rb_file_s_basename(int argc, VALUE *argv)
03648 {
03649 VALUE fname, fext, basename;
03650 const char *name, *p;
03651 long f, n;
03652 rb_encoding *enc;
03653
03654 if (rb_scan_args(argc, argv, "11", &fname, &fext) == 2) {
03655 rb_encoding *enc;
03656 StringValue(fext);
03657 if (!rb_enc_asciicompat(enc = rb_enc_get(fext))) {
03658 rb_raise(rb_eEncCompatError, "ascii incompatible character encodings: %s",
03659 rb_enc_name(enc));
03660 }
03661 }
03662 FilePathStringValue(fname);
03663 if (!NIL_P(fext)) enc = rb_enc_check(fname, fext);
03664 else enc = rb_enc_get(fname);
03665 if ((n = RSTRING_LEN(fname)) == 0 || !*(name = RSTRING_PTR(fname)))
03666 return rb_str_new_shared(fname);
03667
03668 p = ruby_enc_find_basename(name, &f, &n, enc);
03669 if (n >= 0) {
03670 if (NIL_P(fext)) {
03671 f = n;
03672 }
03673 else {
03674 rb_encoding *fenc = rb_enc_get(fext);
03675 const char *fp;
03676 if (enc != fenc &&
03677 rb_enc_str_coderange(fext) != ENC_CODERANGE_7BIT) {
03678 fext = rb_str_conv_enc(fext, fenc, enc);
03679 }
03680 fp = StringValueCStr(fext);
03681 if (!(f = rmext(p, f, n, fp, RSTRING_LEN(fext), enc))) {
03682 f = n;
03683 }
03684 RB_GC_GUARD(fext);
03685 }
03686 if (f == RSTRING_LEN(fname)) return rb_str_new_shared(fname);
03687 }
03688
03689 basename = rb_str_new(p, f);
03690 rb_enc_copy(basename, fname);
03691 OBJ_INFECT(basename, fname);
03692 return basename;
03693 }
03694
03695
03696
03697
03698
03699
03700
03701
03702
03703
03704
03705
03706
03707 static VALUE
03708 rb_file_s_dirname(VALUE klass, VALUE fname)
03709 {
03710 return rb_file_dirname(fname);
03711 }
03712
03713 VALUE
03714 rb_file_dirname(VALUE fname)
03715 {
03716 const char *name, *root, *p, *end;
03717 VALUE dirname;
03718 rb_encoding *enc;
03719
03720 FilePathStringValue(fname);
03721 name = StringValueCStr(fname);
03722 end = name + RSTRING_LEN(fname);
03723 enc = rb_enc_get(fname);
03724 root = skiproot(name, end, enc);
03725 #ifdef DOSISH_UNC
03726 if (root > name + 1 && isdirsep(*name))
03727 root = skipprefix(name = root - 2, end, enc);
03728 #else
03729 if (root > name + 1)
03730 name = root - 1;
03731 #endif
03732 p = strrdirsep(root, end, enc);
03733 if (!p) {
03734 p = root;
03735 }
03736 if (p == name)
03737 return rb_usascii_str_new2(".");
03738 #ifdef DOSISH_DRIVE_LETTER
03739 if (has_drive_letter(name) && isdirsep(*(name + 2))) {
03740 const char *top = skiproot(name + 2, end, enc);
03741 dirname = rb_str_new(name, 3);
03742 rb_str_cat(dirname, top, p - top);
03743 }
03744 else
03745 #endif
03746 dirname = rb_str_new(name, p - name);
03747 #ifdef DOSISH_DRIVE_LETTER
03748 if (has_drive_letter(name) && root == name + 2 && p - name == 2)
03749 rb_str_cat(dirname, ".", 1);
03750 #endif
03751 rb_enc_copy(dirname, fname);
03752 OBJ_INFECT(dirname, fname);
03753 return dirname;
03754 }
03755
03756
03757
03758
03759
03760
03761
03762
03763
03764
03765
03766
03767
03768 const char *
03769 ruby_enc_find_extname(const char *name, long *len, rb_encoding *enc)
03770 {
03771 const char *p, *e, *end = name + (len ? *len : (long)strlen(name));
03772
03773 p = strrdirsep(name, end, enc);
03774 if (!p)
03775 p = name;
03776 else
03777 do name = ++p; while (isdirsep(*p));
03778
03779 e = 0;
03780 while (*p && *p == '.') p++;
03781 while (*p) {
03782 if (*p == '.' || istrailinggarbage(*p)) {
03783 #if USE_NTFS
03784 const char *last = p++, *dot = last;
03785 while (istrailinggarbage(*p)) {
03786 if (*p == '.') dot = p;
03787 p++;
03788 }
03789 if (!*p || *p == ':') {
03790 p = last;
03791 break;
03792 }
03793 if (*last == '.' || dot > last) e = dot;
03794 continue;
03795 #else
03796 e = p;
03797 #endif
03798 }
03799 #if USE_NTFS
03800 else if (*p == ':') {
03801 break;
03802 }
03803 #endif
03804 else if (isdirsep(*p))
03805 break;
03806 Inc(p, end, enc);
03807 }
03808
03809 if (len) {
03810
03811 if (!e || e == name)
03812 *len = 0;
03813 else if (e+1 == p)
03814 *len = 1;
03815 else
03816 *len = p - e;
03817 }
03818 return e;
03819 }
03820
03821
03822
03823
03824
03825
03826
03827
03828
03829
03830
03831
03832
03833
03834
03835 static VALUE
03836 rb_file_s_extname(VALUE klass, VALUE fname)
03837 {
03838 const char *name, *e;
03839 long len;
03840 VALUE extname;
03841
03842 FilePathStringValue(fname);
03843 name = StringValueCStr(fname);
03844 len = RSTRING_LEN(fname);
03845 e = ruby_enc_find_extname(name, &len, rb_enc_get(fname));
03846 if (len <= 1)
03847 return rb_str_new(0, 0);
03848 extname = rb_str_subseq(fname, e - name, len);
03849 OBJ_INFECT(extname, fname);
03850 return extname;
03851 }
03852
03853
03854
03855
03856
03857
03858
03859
03860
03861
03862
03863
03864 static VALUE
03865 rb_file_s_path(VALUE klass, VALUE fname)
03866 {
03867 return rb_get_path(fname);
03868 }
03869
03870
03871
03872
03873
03874
03875
03876
03877
03878
03879
03880
03881 static VALUE
03882 rb_file_s_split(VALUE klass, VALUE path)
03883 {
03884 FilePathStringValue(path);
03885 return rb_assoc_new(rb_file_s_dirname(Qnil, path), rb_file_s_basename(1,&path));
03886 }
03887
03888 static VALUE separator;
03889
03890 static VALUE rb_file_join(VALUE ary, VALUE sep);
03891
03892 static VALUE
03893 file_inspect_join(VALUE ary, VALUE argp, int recur)
03894 {
03895 VALUE *arg = (VALUE *)argp;
03896 if (recur || ary == arg[0]) rb_raise(rb_eArgError, "recursive array");
03897 return rb_file_join(arg[0], arg[1]);
03898 }
03899
03900 static VALUE
03901 rb_file_join(VALUE ary, VALUE sep)
03902 {
03903 long len, i;
03904 VALUE result, tmp;
03905 const char *name, *tail;
03906
03907 if (RARRAY_LEN(ary) == 0) return rb_str_new(0, 0);
03908
03909 len = 1;
03910 for (i=0; i<RARRAY_LEN(ary); i++) {
03911 tmp = RARRAY_PTR(ary)[i];
03912 if (RB_TYPE_P(tmp, T_STRING)) {
03913 len += RSTRING_LEN(tmp);
03914 }
03915 else {
03916 len += 10;
03917 }
03918 }
03919 if (!NIL_P(sep)) {
03920 StringValue(sep);
03921 len += RSTRING_LEN(sep) * RARRAY_LEN(ary) - 1;
03922 }
03923 result = rb_str_buf_new(len);
03924 OBJ_INFECT(result, ary);
03925 for (i=0; i<RARRAY_LEN(ary); i++) {
03926 tmp = RARRAY_PTR(ary)[i];
03927 switch (TYPE(tmp)) {
03928 case T_STRING:
03929 break;
03930 case T_ARRAY:
03931 if (ary == tmp) {
03932 rb_raise(rb_eArgError, "recursive array");
03933 }
03934 else {
03935 VALUE args[2];
03936
03937 args[0] = tmp;
03938 args[1] = sep;
03939 tmp = rb_exec_recursive(file_inspect_join, ary, (VALUE)args);
03940 }
03941 break;
03942 default:
03943 FilePathStringValue(tmp);
03944 }
03945 name = StringValueCStr(result);
03946 len = RSTRING_LEN(result);
03947 if (i == 0) {
03948 rb_enc_copy(result, tmp);
03949 }
03950 else if (!NIL_P(sep)) {
03951 tail = chompdirsep(name, name + len, rb_enc_get(result));
03952 if (RSTRING_PTR(tmp) && isdirsep(RSTRING_PTR(tmp)[0])) {
03953 rb_str_set_len(result, tail - name);
03954 }
03955 else if (!*tail) {
03956 rb_str_buf_append(result, sep);
03957 }
03958 }
03959 rb_str_buf_append(result, tmp);
03960 }
03961
03962 return result;
03963 }
03964
03965
03966
03967
03968
03969
03970
03971
03972
03973
03974
03975
03976 static VALUE
03977 rb_file_s_join(VALUE klass, VALUE args)
03978 {
03979 return rb_file_join(args, separator);
03980 }
03981
03982 #if defined(HAVE_TRUNCATE) || defined(HAVE_CHSIZE)
03983
03984
03985
03986
03987
03988
03989
03990
03991
03992
03993
03994
03995
03996
03997
03998 static VALUE
03999 rb_file_s_truncate(VALUE klass, VALUE path, VALUE len)
04000 {
04001 off_t pos;
04002
04003 rb_secure(2);
04004 pos = NUM2OFFT(len);
04005 FilePathValue(path);
04006 path = rb_str_encode_ospath(path);
04007 #ifdef HAVE_TRUNCATE
04008 if (truncate(StringValueCStr(path), pos) < 0)
04009 rb_sys_fail_path(path);
04010 #else
04011 {
04012 int tmpfd;
04013
04014 if ((tmpfd = open(StringValueCStr(path), 0)) < 0) {
04015 rb_sys_fail_path(path);
04016 }
04017 rb_update_max_fd(tmpfd);
04018 if (chsize(tmpfd, pos) < 0) {
04019 close(tmpfd);
04020 rb_sys_fail_path(path);
04021 }
04022 close(tmpfd);
04023 }
04024 #endif
04025 return INT2FIX(0);
04026 }
04027 #else
04028 #define rb_file_s_truncate rb_f_notimplement
04029 #endif
04030
04031 #if defined(HAVE_FTRUNCATE) || defined(HAVE_CHSIZE)
04032
04033
04034
04035
04036
04037
04038
04039
04040
04041
04042
04043
04044
04045
04046 static VALUE
04047 rb_file_truncate(VALUE obj, VALUE len)
04048 {
04049 rb_io_t *fptr;
04050 off_t pos;
04051
04052 rb_secure(2);
04053 pos = NUM2OFFT(len);
04054 GetOpenFile(obj, fptr);
04055 if (!(fptr->mode & FMODE_WRITABLE)) {
04056 rb_raise(rb_eIOError, "not opened for writing");
04057 }
04058 rb_io_flush(obj);
04059 #ifdef HAVE_FTRUNCATE
04060 if (ftruncate(fptr->fd, pos) < 0)
04061 rb_sys_fail_path(fptr->pathv);
04062 #else
04063 if (chsize(fptr->fd, pos) < 0)
04064 rb_sys_fail_path(fptr->pathv);
04065 #endif
04066 return INT2FIX(0);
04067 }
04068 #else
04069 #define rb_file_truncate rb_f_notimplement
04070 #endif
04071
04072 # ifndef LOCK_SH
04073 # define LOCK_SH 1
04074 # endif
04075 # ifndef LOCK_EX
04076 # define LOCK_EX 2
04077 # endif
04078 # ifndef LOCK_NB
04079 # define LOCK_NB 4
04080 # endif
04081 # ifndef LOCK_UN
04082 # define LOCK_UN 8
04083 # endif
04084
04085 #ifdef __CYGWIN__
04086 #include <winerror.h>
04087 extern unsigned long __attribute__((stdcall)) GetLastError(void);
04088 #endif
04089
04090 static VALUE
04091 rb_thread_flock(void *data)
04092 {
04093 #ifdef __CYGWIN__
04094 int old_errno = errno;
04095 #endif
04096 int *op = data, ret = flock(op[0], op[1]);
04097
04098 #ifdef __CYGWIN__
04099 if (GetLastError() == ERROR_NOT_LOCKED) {
04100 ret = 0;
04101 errno = old_errno;
04102 }
04103 #endif
04104 return (VALUE)ret;
04105 }
04106
04107
04108
04109
04110
04111
04112
04113
04114
04115
04116
04117
04118
04119
04120
04121
04122
04123
04124
04125
04126
04127
04128
04129
04130
04131
04132
04133
04134
04135
04136
04137
04138
04139
04140
04141
04142
04143
04144
04145
04146
04147
04148
04149
04150
04151 static VALUE
04152 rb_file_flock(VALUE obj, VALUE operation)
04153 {
04154 rb_io_t *fptr;
04155 int op[2], op1;
04156
04157 rb_secure(2);
04158 op[1] = op1 = NUM2INT(operation);
04159 GetOpenFile(obj, fptr);
04160 op[0] = fptr->fd;
04161
04162 if (fptr->mode & FMODE_WRITABLE) {
04163 rb_io_flush(obj);
04164 }
04165 while ((int)rb_thread_io_blocking_region(rb_thread_flock, op, fptr->fd) < 0) {
04166 switch (errno) {
04167 case EAGAIN:
04168 case EACCES:
04169 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
04170 case EWOULDBLOCK:
04171 #endif
04172 if (op1 & LOCK_NB) return Qfalse;
04173 rb_thread_polling();
04174 rb_io_check_closed(fptr);
04175 continue;
04176
04177 case EINTR:
04178 #if defined(ERESTART)
04179 case ERESTART:
04180 #endif
04181 break;
04182
04183 default:
04184 rb_sys_fail_path(fptr->pathv);
04185 }
04186 }
04187 return INT2FIX(0);
04188 }
04189 #undef flock
04190
04191 static void
04192 test_check(int n, int argc, VALUE *argv)
04193 {
04194 int i;
04195
04196 rb_secure(2);
04197 n+=1;
04198 if (n != argc) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, n);
04199 for (i=1; i<n; i++) {
04200 switch (TYPE(argv[i])) {
04201 case T_STRING:
04202 default:
04203 FilePathValue(argv[i]);
04204 break;
04205 case T_FILE:
04206 break;
04207 }
04208 }
04209 }
04210
04211 #define CHECK(n) test_check((n), argc, argv)
04212
04213
04214
04215
04216
04217
04218
04219
04220
04221
04222
04223
04224
04225
04226
04227
04228
04229
04230
04231
04232
04233
04234
04235
04236
04237
04238
04239
04240
04241
04242
04243
04244
04245
04246
04247
04248
04249
04250
04251
04252
04253
04254
04255
04256
04257
04258
04259
04260
04261
04262
04263
04264
04265
04266
04267
04268
04269
04270
04271
04272 static VALUE
04273 rb_f_test(int argc, VALUE *argv)
04274 {
04275 int cmd;
04276
04277 if (argc == 0) rb_raise(rb_eArgError, "wrong number of arguments (0 for 2..3)");
04278 cmd = NUM2CHR(argv[0]);
04279 if (cmd == 0) goto unknown;
04280 if (strchr("bcdefgGkloOprRsSuwWxXz", cmd)) {
04281 CHECK(1);
04282 switch (cmd) {
04283 case 'b':
04284 return rb_file_blockdev_p(0, argv[1]);
04285
04286 case 'c':
04287 return rb_file_chardev_p(0, argv[1]);
04288
04289 case 'd':
04290 return rb_file_directory_p(0, argv[1]);
04291
04292 case 'a':
04293 case 'e':
04294 return rb_file_exist_p(0, argv[1]);
04295
04296 case 'f':
04297 return rb_file_file_p(0, argv[1]);
04298
04299 case 'g':
04300 return rb_file_sgid_p(0, argv[1]);
04301
04302 case 'G':
04303 return rb_file_grpowned_p(0, argv[1]);
04304
04305 case 'k':
04306 return rb_file_sticky_p(0, argv[1]);
04307
04308 case 'l':
04309 return rb_file_symlink_p(0, argv[1]);
04310
04311 case 'o':
04312 return rb_file_owned_p(0, argv[1]);
04313
04314 case 'O':
04315 return rb_file_rowned_p(0, argv[1]);
04316
04317 case 'p':
04318 return rb_file_pipe_p(0, argv[1]);
04319
04320 case 'r':
04321 return rb_file_readable_p(0, argv[1]);
04322
04323 case 'R':
04324 return rb_file_readable_real_p(0, argv[1]);
04325
04326 case 's':
04327 return rb_file_size_p(0, argv[1]);
04328
04329 case 'S':
04330 return rb_file_socket_p(0, argv[1]);
04331
04332 case 'u':
04333 return rb_file_suid_p(0, argv[1]);
04334
04335 case 'w':
04336 return rb_file_writable_p(0, argv[1]);
04337
04338 case 'W':
04339 return rb_file_writable_real_p(0, argv[1]);
04340
04341 case 'x':
04342 return rb_file_executable_p(0, argv[1]);
04343
04344 case 'X':
04345 return rb_file_executable_real_p(0, argv[1]);
04346
04347 case 'z':
04348 return rb_file_zero_p(0, argv[1]);
04349 }
04350 }
04351
04352 if (strchr("MAC", cmd)) {
04353 struct stat st;
04354 VALUE fname = argv[1];
04355
04356 CHECK(1);
04357 if (rb_stat(fname, &st) == -1) {
04358 FilePathValue(fname);
04359 rb_sys_fail_path(fname);
04360 }
04361
04362 switch (cmd) {
04363 case 'A':
04364 return stat_atime(&st);
04365 case 'M':
04366 return stat_mtime(&st);
04367 case 'C':
04368 return stat_ctime(&st);
04369 }
04370 }
04371
04372 if (cmd == '-') {
04373 CHECK(2);
04374 return rb_file_identical_p(0, argv[1], argv[2]);
04375 }
04376
04377 if (strchr("=<>", cmd)) {
04378 struct stat st1, st2;
04379
04380 CHECK(2);
04381 if (rb_stat(argv[1], &st1) < 0) return Qfalse;
04382 if (rb_stat(argv[2], &st2) < 0) return Qfalse;
04383
04384 switch (cmd) {
04385 case '=':
04386 if (st1.st_mtime == st2.st_mtime) return Qtrue;
04387 return Qfalse;
04388
04389 case '>':
04390 if (st1.st_mtime > st2.st_mtime) return Qtrue;
04391 return Qfalse;
04392
04393 case '<':
04394 if (st1.st_mtime < st2.st_mtime) return Qtrue;
04395 return Qfalse;
04396 }
04397 }
04398 unknown:
04399
04400 if (ISPRINT(cmd)) {
04401 rb_raise(rb_eArgError, "unknown command '%s%c'", cmd == '\'' || cmd == '\\' ? "\\" : "", cmd);
04402 }
04403 else {
04404 rb_raise(rb_eArgError, "unknown command \"\\x%02X\"", cmd);
04405 }
04406 return Qnil;
04407 }
04408
04409
04410
04411
04412
04413
04414
04415
04416
04417
04418
04419
04420
04421
04422
04423
04424 static VALUE
04425 rb_stat_s_alloc(VALUE klass)
04426 {
04427 return stat_new_0(klass, 0);
04428 }
04429
04430
04431
04432
04433
04434
04435
04436
04437
04438
04439 static VALUE
04440 rb_stat_init(VALUE obj, VALUE fname)
04441 {
04442 struct stat st, *nst;
04443
04444 rb_secure(2);
04445 FilePathValue(fname);
04446 fname = rb_str_encode_ospath(fname);
04447 if (STAT(StringValueCStr(fname), &st) == -1) {
04448 rb_sys_fail_path(fname);
04449 }
04450 if (DATA_PTR(obj)) {
04451 xfree(DATA_PTR(obj));
04452 DATA_PTR(obj) = NULL;
04453 }
04454 nst = ALLOC(struct stat);
04455 *nst = st;
04456 DATA_PTR(obj) = nst;
04457
04458 return Qnil;
04459 }
04460
04461
04462 static VALUE
04463 rb_stat_init_copy(VALUE copy, VALUE orig)
04464 {
04465 struct stat *nst;
04466
04467 if (copy == orig) return orig;
04468 rb_check_frozen(copy);
04469
04470 if (!rb_obj_is_instance_of(orig, rb_obj_class(copy))) {
04471 rb_raise(rb_eTypeError, "wrong argument class");
04472 }
04473 if (DATA_PTR(copy)) {
04474 xfree(DATA_PTR(copy));
04475 DATA_PTR(copy) = 0;
04476 }
04477 if (DATA_PTR(orig)) {
04478 nst = ALLOC(struct stat);
04479 *nst = *(struct stat*)DATA_PTR(orig);
04480 DATA_PTR(copy) = nst;
04481 }
04482
04483 return copy;
04484 }
04485
04486
04487
04488
04489
04490
04491
04492
04493
04494
04495
04496
04497
04498
04499
04500 static VALUE
04501 rb_stat_ftype(VALUE obj)
04502 {
04503 return rb_file_ftype(get_stat(obj));
04504 }
04505
04506
04507
04508
04509
04510
04511
04512
04513
04514
04515
04516
04517 static VALUE
04518 rb_stat_d(VALUE obj)
04519 {
04520 if (S_ISDIR(get_stat(obj)->st_mode)) return Qtrue;
04521 return Qfalse;
04522 }
04523
04524
04525
04526
04527
04528
04529
04530
04531
04532 static VALUE
04533 rb_stat_p(VALUE obj)
04534 {
04535 #ifdef S_IFIFO
04536 if (S_ISFIFO(get_stat(obj)->st_mode)) return Qtrue;
04537
04538 #endif
04539 return Qfalse;
04540 }
04541
04542
04543
04544
04545
04546
04547
04548
04549
04550
04551
04552
04553
04554
04555
04556
04557
04558
04559 static VALUE
04560 rb_stat_l(VALUE obj)
04561 {
04562 #ifdef S_ISLNK
04563 if (S_ISLNK(get_stat(obj)->st_mode)) return Qtrue;
04564 #endif
04565 return Qfalse;
04566 }
04567
04568
04569
04570
04571
04572
04573
04574
04575
04576
04577
04578
04579
04580 static VALUE
04581 rb_stat_S(VALUE obj)
04582 {
04583 #ifdef S_ISSOCK
04584 if (S_ISSOCK(get_stat(obj)->st_mode)) return Qtrue;
04585
04586 #endif
04587 return Qfalse;
04588 }
04589
04590
04591
04592
04593
04594
04595
04596
04597
04598
04599
04600
04601
04602
04603 static VALUE
04604 rb_stat_b(VALUE obj)
04605 {
04606 #ifdef S_ISBLK
04607 if (S_ISBLK(get_stat(obj)->st_mode)) return Qtrue;
04608
04609 #endif
04610 return Qfalse;
04611 }
04612
04613
04614
04615
04616
04617
04618
04619
04620
04621
04622
04623
04624
04625 static VALUE
04626 rb_stat_c(VALUE obj)
04627 {
04628 if (S_ISCHR(get_stat(obj)->st_mode)) return Qtrue;
04629
04630 return Qfalse;
04631 }
04632
04633
04634
04635
04636
04637
04638
04639
04640
04641
04642
04643
04644
04645 static VALUE
04646 rb_stat_owned(VALUE obj)
04647 {
04648 if (get_stat(obj)->st_uid == geteuid()) return Qtrue;
04649 return Qfalse;
04650 }
04651
04652 static VALUE
04653 rb_stat_rowned(VALUE obj)
04654 {
04655 if (get_stat(obj)->st_uid == getuid()) return Qtrue;
04656 return Qfalse;
04657 }
04658
04659
04660
04661
04662
04663
04664
04665
04666
04667
04668
04669
04670
04671 static VALUE
04672 rb_stat_grpowned(VALUE obj)
04673 {
04674 #ifndef _WIN32
04675 if (rb_group_member(get_stat(obj)->st_gid)) return Qtrue;
04676 #endif
04677 return Qfalse;
04678 }
04679
04680
04681
04682
04683
04684
04685
04686
04687
04688
04689
04690
04691 static VALUE
04692 rb_stat_r(VALUE obj)
04693 {
04694 struct stat *st = get_stat(obj);
04695
04696 #ifdef USE_GETEUID
04697 if (geteuid() == 0) return Qtrue;
04698 #endif
04699 #ifdef S_IRUSR
04700 if (rb_stat_owned(obj))
04701 return st->st_mode & S_IRUSR ? Qtrue : Qfalse;
04702 #endif
04703 #ifdef S_IRGRP
04704 if (rb_stat_grpowned(obj))
04705 return st->st_mode & S_IRGRP ? Qtrue : Qfalse;
04706 #endif
04707 #ifdef S_IROTH
04708 if (!(st->st_mode & S_IROTH)) return Qfalse;
04709 #endif
04710 return Qtrue;
04711 }
04712
04713
04714
04715
04716
04717
04718
04719
04720
04721
04722
04723
04724 static VALUE
04725 rb_stat_R(VALUE obj)
04726 {
04727 struct stat *st = get_stat(obj);
04728
04729 #ifdef USE_GETEUID
04730 if (getuid() == 0) return Qtrue;
04731 #endif
04732 #ifdef S_IRUSR
04733 if (rb_stat_rowned(obj))
04734 return st->st_mode & S_IRUSR ? Qtrue : Qfalse;
04735 #endif
04736 #ifdef S_IRGRP
04737 if (rb_group_member(get_stat(obj)->st_gid))
04738 return st->st_mode & S_IRGRP ? Qtrue : Qfalse;
04739 #endif
04740 #ifdef S_IROTH
04741 if (!(st->st_mode & S_IROTH)) return Qfalse;
04742 #endif
04743 return Qtrue;
04744 }
04745
04746
04747
04748
04749
04750
04751
04752
04753
04754
04755
04756
04757
04758
04759 static VALUE
04760 rb_stat_wr(VALUE obj)
04761 {
04762 #ifdef S_IROTH
04763 if ((get_stat(obj)->st_mode & (S_IROTH)) == S_IROTH) {
04764 return UINT2NUM(get_stat(obj)->st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
04765 }
04766 else {
04767 return Qnil;
04768 }
04769 #endif
04770 }
04771
04772
04773
04774
04775
04776
04777
04778
04779
04780
04781
04782
04783 static VALUE
04784 rb_stat_w(VALUE obj)
04785 {
04786 struct stat *st = get_stat(obj);
04787
04788 #ifdef USE_GETEUID
04789 if (geteuid() == 0) return Qtrue;
04790 #endif
04791 #ifdef S_IWUSR
04792 if (rb_stat_owned(obj))
04793 return st->st_mode & S_IWUSR ? Qtrue : Qfalse;
04794 #endif
04795 #ifdef S_IWGRP
04796 if (rb_stat_grpowned(obj))
04797 return st->st_mode & S_IWGRP ? Qtrue : Qfalse;
04798 #endif
04799 #ifdef S_IWOTH
04800 if (!(st->st_mode & S_IWOTH)) return Qfalse;
04801 #endif
04802 return Qtrue;
04803 }
04804
04805
04806
04807
04808
04809
04810
04811
04812
04813
04814
04815
04816 static VALUE
04817 rb_stat_W(VALUE obj)
04818 {
04819 struct stat *st = get_stat(obj);
04820
04821 #ifdef USE_GETEUID
04822 if (getuid() == 0) return Qtrue;
04823 #endif
04824 #ifdef S_IWUSR
04825 if (rb_stat_rowned(obj))
04826 return st->st_mode & S_IWUSR ? Qtrue : Qfalse;
04827 #endif
04828 #ifdef S_IWGRP
04829 if (rb_group_member(get_stat(obj)->st_gid))
04830 return st->st_mode & S_IWGRP ? Qtrue : Qfalse;
04831 #endif
04832 #ifdef S_IWOTH
04833 if (!(st->st_mode & S_IWOTH)) return Qfalse;
04834 #endif
04835 return Qtrue;
04836 }
04837
04838
04839
04840
04841
04842
04843
04844
04845
04846
04847
04848
04849
04850
04851 static VALUE
04852 rb_stat_ww(VALUE obj)
04853 {
04854 #ifdef S_IROTH
04855 if ((get_stat(obj)->st_mode & (S_IWOTH)) == S_IWOTH) {
04856 return UINT2NUM(get_stat(obj)->st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
04857 }
04858 else {
04859 return Qnil;
04860 }
04861 #endif
04862 }
04863
04864
04865
04866
04867
04868
04869
04870
04871
04872
04873
04874
04875
04876
04877 static VALUE
04878 rb_stat_x(VALUE obj)
04879 {
04880 struct stat *st = get_stat(obj);
04881
04882 #ifdef USE_GETEUID
04883 if (geteuid() == 0) {
04884 return st->st_mode & S_IXUGO ? Qtrue : Qfalse;
04885 }
04886 #endif
04887 #ifdef S_IXUSR
04888 if (rb_stat_owned(obj))
04889 return st->st_mode & S_IXUSR ? Qtrue : Qfalse;
04890 #endif
04891 #ifdef S_IXGRP
04892 if (rb_stat_grpowned(obj))
04893 return st->st_mode & S_IXGRP ? Qtrue : Qfalse;
04894 #endif
04895 #ifdef S_IXOTH
04896 if (!(st->st_mode & S_IXOTH)) return Qfalse;
04897 #endif
04898 return Qtrue;
04899 }
04900
04901
04902
04903
04904
04905
04906
04907
04908
04909 static VALUE
04910 rb_stat_X(VALUE obj)
04911 {
04912 struct stat *st = get_stat(obj);
04913
04914 #ifdef USE_GETEUID
04915 if (getuid() == 0) {
04916 return st->st_mode & S_IXUGO ? Qtrue : Qfalse;
04917 }
04918 #endif
04919 #ifdef S_IXUSR
04920 if (rb_stat_rowned(obj))
04921 return st->st_mode & S_IXUSR ? Qtrue : Qfalse;
04922 #endif
04923 #ifdef S_IXGRP
04924 if (rb_group_member(get_stat(obj)->st_gid))
04925 return st->st_mode & S_IXGRP ? Qtrue : Qfalse;
04926 #endif
04927 #ifdef S_IXOTH
04928 if (!(st->st_mode & S_IXOTH)) return Qfalse;
04929 #endif
04930 return Qtrue;
04931 }
04932
04933
04934
04935
04936
04937
04938
04939
04940
04941
04942
04943
04944 static VALUE
04945 rb_stat_f(VALUE obj)
04946 {
04947 if (S_ISREG(get_stat(obj)->st_mode)) return Qtrue;
04948 return Qfalse;
04949 }
04950
04951
04952
04953
04954
04955
04956
04957
04958
04959
04960
04961
04962 static VALUE
04963 rb_stat_z(VALUE obj)
04964 {
04965 if (get_stat(obj)->st_size == 0) return Qtrue;
04966 return Qfalse;
04967 }
04968
04969
04970
04971
04972
04973
04974
04975
04976
04977
04978
04979 static VALUE
04980 rb_stat_s(VALUE obj)
04981 {
04982 off_t size = get_stat(obj)->st_size;
04983
04984 if (size == 0) return Qnil;
04985 return OFFT2NUM(size);
04986 }
04987
04988
04989
04990
04991
04992
04993
04994
04995
04996
04997
04998
04999 static VALUE
05000 rb_stat_suid(VALUE obj)
05001 {
05002 #ifdef S_ISUID
05003 if (get_stat(obj)->st_mode & S_ISUID) return Qtrue;
05004 #endif
05005 return Qfalse;
05006 }
05007
05008
05009
05010
05011
05012
05013
05014
05015
05016
05017
05018
05019
05020 static VALUE
05021 rb_stat_sgid(VALUE obj)
05022 {
05023 #ifdef S_ISGID
05024 if (get_stat(obj)->st_mode & S_ISGID) return Qtrue;
05025 #endif
05026 return Qfalse;
05027 }
05028
05029
05030
05031
05032
05033
05034
05035
05036
05037
05038
05039
05040
05041 static VALUE
05042 rb_stat_sticky(VALUE obj)
05043 {
05044 #ifdef S_ISVTX
05045 if (get_stat(obj)->st_mode & S_ISVTX) return Qtrue;
05046 #endif
05047 return Qfalse;
05048 }
05049
05050 VALUE rb_mFConst;
05051
05052 void
05053 rb_file_const(const char *name, VALUE value)
05054 {
05055 rb_define_const(rb_mFConst, name, value);
05056 }
05057
05058 int
05059 rb_is_absolute_path(const char *path)
05060 {
05061 #ifdef DOSISH_DRIVE_LETTER
05062 if (has_drive_letter(path) && isdirsep(path[2])) return 1;
05063 #endif
05064 #ifdef DOSISH_UNC
05065 if (isdirsep(path[0]) && isdirsep(path[1])) return 1;
05066 #endif
05067 #ifndef DOSISH
05068 if (path[0] == '/') return 1;
05069 #endif
05070 return 0;
05071 }
05072
05073 #ifndef ENABLE_PATH_CHECK
05074 # if defined DOSISH || defined __CYGWIN__
05075 # define ENABLE_PATH_CHECK 0
05076 # else
05077 # define ENABLE_PATH_CHECK 1
05078 # endif
05079 #endif
05080
05081 #if ENABLE_PATH_CHECK
05082 static int
05083 path_check_0(VALUE path, int execpath)
05084 {
05085 struct stat st;
05086 const char *p0 = StringValueCStr(path);
05087 const char *e0;
05088 rb_encoding *enc;
05089 char *p = 0, *s;
05090
05091 if (!rb_is_absolute_path(p0)) {
05092 char *buf = my_getcwd();
05093 VALUE newpath;
05094
05095 newpath = rb_str_new2(buf);
05096 xfree(buf);
05097
05098 rb_str_cat2(newpath, "/");
05099 rb_str_cat2(newpath, p0);
05100 path = newpath;
05101 p0 = RSTRING_PTR(path);
05102 }
05103 e0 = p0 + RSTRING_LEN(path);
05104 enc = rb_enc_get(path);
05105 for (;;) {
05106 #ifndef S_IWOTH
05107 # define S_IWOTH 002
05108 #endif
05109 if (STAT(p0, &st) == 0 && S_ISDIR(st.st_mode) && (st.st_mode & S_IWOTH)
05110 #ifdef S_ISVTX
05111 && !(p && execpath && (st.st_mode & S_ISVTX))
05112 #endif
05113 && !access(p0, W_OK)) {
05114 rb_warn("Insecure world writable dir %s in %sPATH, mode 0%"
05115 PRI_MODET_PREFIX"o",
05116 p0, (execpath ? "" : "LOAD_"), st.st_mode);
05117 if (p) *p = '/';
05118 RB_GC_GUARD(path);
05119 return 0;
05120 }
05121 s = strrdirsep(p0, e0, enc);
05122 if (p) *p = '/';
05123 if (!s || s == p0) return 1;
05124 p = s;
05125 e0 = p;
05126 *p = '\0';
05127 }
05128 }
05129 #endif
05130
05131 #if ENABLE_PATH_CHECK
05132 #define fpath_check(path) path_check_0((path), FALSE)
05133 #else
05134 #define fpath_check(path) 1
05135 #endif
05136
05137 int
05138 rb_path_check(const char *path)
05139 {
05140 #if ENABLE_PATH_CHECK
05141 const char *p0, *p, *pend;
05142 const char sep = PATH_SEP_CHAR;
05143
05144 if (!path) return 1;
05145
05146 pend = path + strlen(path);
05147 p0 = path;
05148 p = strchr(path, sep);
05149 if (!p) p = pend;
05150
05151 for (;;) {
05152 if (!path_check_0(rb_str_new(p0, p - p0), TRUE)) {
05153 return 0;
05154 }
05155 p0 = p + 1;
05156 if (p0 > pend) break;
05157 p = strchr(p0, sep);
05158 if (!p) p = pend;
05159 }
05160 #endif
05161 return 1;
05162 }
05163
05164 #ifndef _WIN32
05165 int
05166 rb_file_load_ok(const char *path)
05167 {
05168 int ret = 1;
05169 int fd = open(path, O_RDONLY);
05170 if (fd == -1) return 0;
05171 rb_update_max_fd(fd);
05172 #if !defined DOSISH
05173 {
05174 struct stat st;
05175 if (fstat(fd, &st) || !S_ISREG(st.st_mode)) {
05176 ret = 0;
05177 }
05178 }
05179 #endif
05180 (void)close(fd);
05181 return ret;
05182 }
05183 #endif
05184
05185 static int
05186 is_explicit_relative(const char *path)
05187 {
05188 if (*path++ != '.') return 0;
05189 if (*path == '.') path++;
05190 return isdirsep(*path);
05191 }
05192
05193 static VALUE
05194 copy_path_class(VALUE path, VALUE orig)
05195 {
05196 RBASIC(path)->klass = rb_obj_class(orig);
05197 OBJ_FREEZE(path);
05198 return path;
05199 }
05200
05201 int
05202 rb_find_file_ext(VALUE *filep, const char *const *ext)
05203 {
05204 return rb_find_file_ext_safe(filep, ext, rb_safe_level());
05205 }
05206
05207 int
05208 rb_find_file_ext_safe(VALUE *filep, const char *const *ext, int safe_level)
05209 {
05210 const char *f = StringValueCStr(*filep);
05211 VALUE fname = *filep, load_path, tmp;
05212 long i, j, fnlen;
05213 int expanded = 0;
05214
05215 if (!ext[0]) return 0;
05216
05217 if (f[0] == '~') {
05218 fname = file_expand_path_1(fname);
05219 if (safe_level >= 1 && OBJ_TAINTED(fname)) {
05220 rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
05221 }
05222 f = RSTRING_PTR(fname);
05223 *filep = fname;
05224 expanded = 1;
05225 }
05226
05227 if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) {
05228 if (safe_level >= 1 && !fpath_check(fname)) {
05229 rb_raise(rb_eSecurityError, "loading from unsafe path %s", f);
05230 }
05231 if (!expanded) fname = file_expand_path_1(fname);
05232 fnlen = RSTRING_LEN(fname);
05233 for (i=0; ext[i]; i++) {
05234 rb_str_cat2(fname, ext[i]);
05235 if (rb_file_load_ok(RSTRING_PTR(fname))) {
05236 *filep = copy_path_class(fname, *filep);
05237 return (int)(i+1);
05238 }
05239 rb_str_set_len(fname, fnlen);
05240 }
05241 return 0;
05242 }
05243
05244 if (safe_level >= 4) {
05245 rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f);
05246 }
05247
05248 RB_GC_GUARD(load_path) = rb_get_load_path();
05249 if (!load_path) return 0;
05250
05251 fname = rb_str_dup(*filep);
05252 RBASIC(fname)->klass = 0;
05253 fnlen = RSTRING_LEN(fname);
05254 tmp = rb_str_tmp_new(MAXPATHLEN + 2);
05255 rb_enc_associate_index(tmp, rb_usascii_encindex());
05256 for (j=0; ext[j]; j++) {
05257 rb_str_cat2(fname, ext[j]);
05258 for (i = 0; i < RARRAY_LEN(load_path); i++) {
05259 VALUE str = RARRAY_PTR(load_path)[i];
05260
05261 RB_GC_GUARD(str) = rb_get_path_check(str, safe_level);
05262 if (RSTRING_LEN(str) == 0) continue;
05263 rb_file_expand_path_internal(fname, str, 0, 0, tmp);
05264 if (rb_file_load_ok(RSTRING_PTR(tmp))) {
05265 *filep = copy_path_class(tmp, *filep);
05266 return (int)(j+1);
05267 }
05268 FL_UNSET(tmp, FL_TAINT | FL_UNTRUSTED);
05269 }
05270 rb_str_set_len(fname, fnlen);
05271 }
05272 RB_GC_GUARD(load_path);
05273 return 0;
05274 }
05275
05276 VALUE
05277 rb_find_file(VALUE path)
05278 {
05279 return rb_find_file_safe(path, rb_safe_level());
05280 }
05281
05282 VALUE
05283 rb_find_file_safe(VALUE path, int safe_level)
05284 {
05285 VALUE tmp, load_path;
05286 const char *f = StringValueCStr(path);
05287 int expanded = 0;
05288
05289 if (f[0] == '~') {
05290 tmp = file_expand_path_1(path);
05291 if (safe_level >= 1 && OBJ_TAINTED(tmp)) {
05292 rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
05293 }
05294 path = copy_path_class(tmp, path);
05295 f = RSTRING_PTR(path);
05296 expanded = 1;
05297 }
05298
05299 if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) {
05300 if (safe_level >= 1 && !fpath_check(path)) {
05301 rb_raise(rb_eSecurityError, "loading from unsafe path %s", f);
05302 }
05303 if (!rb_file_load_ok(f)) return 0;
05304 if (!expanded)
05305 path = copy_path_class(file_expand_path_1(path), path);
05306 return path;
05307 }
05308
05309 if (safe_level >= 4) {
05310 rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f);
05311 }
05312
05313 RB_GC_GUARD(load_path) = rb_get_load_path();
05314 if (load_path) {
05315 long i;
05316
05317 tmp = rb_str_tmp_new(MAXPATHLEN + 2);
05318 rb_enc_associate_index(tmp, rb_usascii_encindex());
05319 for (i = 0; i < RARRAY_LEN(load_path); i++) {
05320 VALUE str = RARRAY_PTR(load_path)[i];
05321 RB_GC_GUARD(str) = rb_get_path_check(str, safe_level);
05322 if (RSTRING_LEN(str) > 0) {
05323 rb_file_expand_path_internal(path, str, 0, 0, tmp);
05324 f = RSTRING_PTR(tmp);
05325 if (rb_file_load_ok(f)) goto found;
05326 }
05327 }
05328 return 0;
05329 }
05330 else {
05331 return 0;
05332 }
05333
05334 found:
05335 if (safe_level >= 1 && !fpath_check(tmp)) {
05336 rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
05337 }
05338
05339 return copy_path_class(tmp, path);
05340 }
05341
05342 static void
05343 define_filetest_function(const char *name, VALUE (*func)(ANYARGS), int argc)
05344 {
05345 rb_define_module_function(rb_mFileTest, name, func, argc);
05346 rb_define_singleton_method(rb_cFile, name, func, argc);
05347 }
05348
05349 static const char null_device[] =
05350 #if defined DOSISH
05351 "NUL"
05352 #elif defined AMIGA || defined __amigaos__
05353 "NIL"
05354 #elif defined __VMS
05355 "NL:"
05356 #else
05357 "/dev/null"
05358 #endif
05359 ;
05360
05361
05362
05363
05364
05365
05366
05367
05368
05369
05370
05371
05372
05373
05374
05375
05376
05377
05378
05379
05380
05381
05382
05383
05384
05385
05386
05387
05388
05389
05390
05391
05392
05393 void
05394 Init_File(void)
05395 {
05396 rb_mFileTest = rb_define_module("FileTest");
05397 rb_cFile = rb_define_class("File", rb_cIO);
05398
05399 define_filetest_function("directory?", rb_file_directory_p, 1);
05400 define_filetest_function("exist?", rb_file_exist_p, 1);
05401 define_filetest_function("exists?", rb_file_exist_p, 1);
05402 define_filetest_function("readable?", rb_file_readable_p, 1);
05403 define_filetest_function("readable_real?", rb_file_readable_real_p, 1);
05404 define_filetest_function("world_readable?", rb_file_world_readable_p, 1);
05405 define_filetest_function("writable?", rb_file_writable_p, 1);
05406 define_filetest_function("writable_real?", rb_file_writable_real_p, 1);
05407 define_filetest_function("world_writable?", rb_file_world_writable_p, 1);
05408 define_filetest_function("executable?", rb_file_executable_p, 1);
05409 define_filetest_function("executable_real?", rb_file_executable_real_p, 1);
05410 define_filetest_function("file?", rb_file_file_p, 1);
05411 define_filetest_function("zero?", rb_file_zero_p, 1);
05412 define_filetest_function("size?", rb_file_size_p, 1);
05413 define_filetest_function("size", rb_file_s_size, 1);
05414 define_filetest_function("owned?", rb_file_owned_p, 1);
05415 define_filetest_function("grpowned?", rb_file_grpowned_p, 1);
05416
05417 define_filetest_function("pipe?", rb_file_pipe_p, 1);
05418 define_filetest_function("symlink?", rb_file_symlink_p, 1);
05419 define_filetest_function("socket?", rb_file_socket_p, 1);
05420
05421 define_filetest_function("blockdev?", rb_file_blockdev_p, 1);
05422 define_filetest_function("chardev?", rb_file_chardev_p, 1);
05423
05424 define_filetest_function("setuid?", rb_file_suid_p, 1);
05425 define_filetest_function("setgid?", rb_file_sgid_p, 1);
05426 define_filetest_function("sticky?", rb_file_sticky_p, 1);
05427
05428 define_filetest_function("identical?", rb_file_identical_p, 2);
05429
05430 rb_define_singleton_method(rb_cFile, "stat", rb_file_s_stat, 1);
05431 rb_define_singleton_method(rb_cFile, "lstat", rb_file_s_lstat, 1);
05432 rb_define_singleton_method(rb_cFile, "ftype", rb_file_s_ftype, 1);
05433
05434 rb_define_singleton_method(rb_cFile, "atime", rb_file_s_atime, 1);
05435 rb_define_singleton_method(rb_cFile, "mtime", rb_file_s_mtime, 1);
05436 rb_define_singleton_method(rb_cFile, "ctime", rb_file_s_ctime, 1);
05437
05438 rb_define_singleton_method(rb_cFile, "utime", rb_file_s_utime, -1);
05439 rb_define_singleton_method(rb_cFile, "chmod", rb_file_s_chmod, -1);
05440 rb_define_singleton_method(rb_cFile, "chown", rb_file_s_chown, -1);
05441 rb_define_singleton_method(rb_cFile, "lchmod", rb_file_s_lchmod, -1);
05442 rb_define_singleton_method(rb_cFile, "lchown", rb_file_s_lchown, -1);
05443
05444 rb_define_singleton_method(rb_cFile, "link", rb_file_s_link, 2);
05445 rb_define_singleton_method(rb_cFile, "symlink", rb_file_s_symlink, 2);
05446 rb_define_singleton_method(rb_cFile, "readlink", rb_file_s_readlink, 1);
05447
05448 rb_define_singleton_method(rb_cFile, "unlink", rb_file_s_unlink, -2);
05449 rb_define_singleton_method(rb_cFile, "delete", rb_file_s_unlink, -2);
05450 rb_define_singleton_method(rb_cFile, "rename", rb_file_s_rename, 2);
05451 rb_define_singleton_method(rb_cFile, "umask", rb_file_s_umask, -1);
05452 rb_define_singleton_method(rb_cFile, "truncate", rb_file_s_truncate, 2);
05453 rb_define_singleton_method(rb_cFile, "expand_path", rb_file_s_expand_path, -1);
05454 rb_define_singleton_method(rb_cFile, "absolute_path", rb_file_s_absolute_path, -1);
05455 rb_define_singleton_method(rb_cFile, "realpath", rb_file_s_realpath, -1);
05456 rb_define_singleton_method(rb_cFile, "realdirpath", rb_file_s_realdirpath, -1);
05457 rb_define_singleton_method(rb_cFile, "basename", rb_file_s_basename, -1);
05458 rb_define_singleton_method(rb_cFile, "dirname", rb_file_s_dirname, 1);
05459 rb_define_singleton_method(rb_cFile, "extname", rb_file_s_extname, 1);
05460 rb_define_singleton_method(rb_cFile, "path", rb_file_s_path, 1);
05461
05462 separator = rb_obj_freeze(rb_usascii_str_new2("/"));
05463 rb_define_const(rb_cFile, "Separator", separator);
05464 rb_define_const(rb_cFile, "SEPARATOR", separator);
05465 rb_define_singleton_method(rb_cFile, "split", rb_file_s_split, 1);
05466 rb_define_singleton_method(rb_cFile, "join", rb_file_s_join, -2);
05467
05468 #ifdef DOSISH
05469 rb_define_const(rb_cFile, "ALT_SEPARATOR", rb_obj_freeze(rb_usascii_str_new2(file_alt_separator)));
05470 #else
05471 rb_define_const(rb_cFile, "ALT_SEPARATOR", Qnil);
05472 #endif
05473 rb_define_const(rb_cFile, "PATH_SEPARATOR", rb_obj_freeze(rb_str_new2(PATH_SEP)));
05474
05475 rb_define_method(rb_cIO, "stat", rb_io_stat, 0);
05476 rb_define_method(rb_cFile, "lstat", rb_file_lstat, 0);
05477
05478 rb_define_method(rb_cFile, "atime", rb_file_atime, 0);
05479 rb_define_method(rb_cFile, "mtime", rb_file_mtime, 0);
05480 rb_define_method(rb_cFile, "ctime", rb_file_ctime, 0);
05481 rb_define_method(rb_cFile, "size", rb_file_size, 0);
05482
05483 rb_define_method(rb_cFile, "chmod", rb_file_chmod, 1);
05484 rb_define_method(rb_cFile, "chown", rb_file_chown, 2);
05485 rb_define_method(rb_cFile, "truncate", rb_file_truncate, 1);
05486
05487 rb_define_method(rb_cFile, "flock", rb_file_flock, 1);
05488
05489 rb_mFConst = rb_define_module_under(rb_cFile, "Constants");
05490 rb_include_module(rb_cIO, rb_mFConst);
05491 rb_file_const("LOCK_SH", INT2FIX(LOCK_SH));
05492 rb_file_const("LOCK_EX", INT2FIX(LOCK_EX));
05493 rb_file_const("LOCK_UN", INT2FIX(LOCK_UN));
05494 rb_file_const("LOCK_NB", INT2FIX(LOCK_NB));
05495
05496 rb_file_const("NULL", rb_obj_freeze(rb_usascii_str_new2(null_device)));
05497
05498 rb_define_method(rb_cFile, "path", rb_file_path, 0);
05499 rb_define_method(rb_cFile, "to_path", rb_file_path, 0);
05500 rb_define_global_function("test", rb_f_test, -1);
05501
05502 rb_cStat = rb_define_class_under(rb_cFile, "Stat", rb_cObject);
05503 rb_define_alloc_func(rb_cStat, rb_stat_s_alloc);
05504 rb_define_method(rb_cStat, "initialize", rb_stat_init, 1);
05505 rb_define_method(rb_cStat, "initialize_copy", rb_stat_init_copy, 1);
05506
05507 rb_include_module(rb_cStat, rb_mComparable);
05508
05509 rb_define_method(rb_cStat, "<=>", rb_stat_cmp, 1);
05510
05511 rb_define_method(rb_cStat, "dev", rb_stat_dev, 0);
05512 rb_define_method(rb_cStat, "dev_major", rb_stat_dev_major, 0);
05513 rb_define_method(rb_cStat, "dev_minor", rb_stat_dev_minor, 0);
05514 rb_define_method(rb_cStat, "ino", rb_stat_ino, 0);
05515 rb_define_method(rb_cStat, "mode", rb_stat_mode, 0);
05516 rb_define_method(rb_cStat, "nlink", rb_stat_nlink, 0);
05517 rb_define_method(rb_cStat, "uid", rb_stat_uid, 0);
05518 rb_define_method(rb_cStat, "gid", rb_stat_gid, 0);
05519 rb_define_method(rb_cStat, "rdev", rb_stat_rdev, 0);
05520 rb_define_method(rb_cStat, "rdev_major", rb_stat_rdev_major, 0);
05521 rb_define_method(rb_cStat, "rdev_minor", rb_stat_rdev_minor, 0);
05522 rb_define_method(rb_cStat, "size", rb_stat_size, 0);
05523 rb_define_method(rb_cStat, "blksize", rb_stat_blksize, 0);
05524 rb_define_method(rb_cStat, "blocks", rb_stat_blocks, 0);
05525 rb_define_method(rb_cStat, "atime", rb_stat_atime, 0);
05526 rb_define_method(rb_cStat, "mtime", rb_stat_mtime, 0);
05527 rb_define_method(rb_cStat, "ctime", rb_stat_ctime, 0);
05528
05529 rb_define_method(rb_cStat, "inspect", rb_stat_inspect, 0);
05530
05531 rb_define_method(rb_cStat, "ftype", rb_stat_ftype, 0);
05532
05533 rb_define_method(rb_cStat, "directory?", rb_stat_d, 0);
05534 rb_define_method(rb_cStat, "readable?", rb_stat_r, 0);
05535 rb_define_method(rb_cStat, "readable_real?", rb_stat_R, 0);
05536 rb_define_method(rb_cStat, "world_readable?", rb_stat_wr, 0);
05537 rb_define_method(rb_cStat, "writable?", rb_stat_w, 0);
05538 rb_define_method(rb_cStat, "writable_real?", rb_stat_W, 0);
05539 rb_define_method(rb_cStat, "world_writable?", rb_stat_ww, 0);
05540 rb_define_method(rb_cStat, "executable?", rb_stat_x, 0);
05541 rb_define_method(rb_cStat, "executable_real?", rb_stat_X, 0);
05542 rb_define_method(rb_cStat, "file?", rb_stat_f, 0);
05543 rb_define_method(rb_cStat, "zero?", rb_stat_z, 0);
05544 rb_define_method(rb_cStat, "size?", rb_stat_s, 0);
05545 rb_define_method(rb_cStat, "owned?", rb_stat_owned, 0);
05546 rb_define_method(rb_cStat, "grpowned?", rb_stat_grpowned, 0);
05547
05548 rb_define_method(rb_cStat, "pipe?", rb_stat_p, 0);
05549 rb_define_method(rb_cStat, "symlink?", rb_stat_l, 0);
05550 rb_define_method(rb_cStat, "socket?", rb_stat_S, 0);
05551
05552 rb_define_method(rb_cStat, "blockdev?", rb_stat_b, 0);
05553 rb_define_method(rb_cStat, "chardev?", rb_stat_c, 0);
05554
05555 rb_define_method(rb_cStat, "setuid?", rb_stat_suid, 0);
05556 rb_define_method(rb_cStat, "setgid?", rb_stat_sgid, 0);
05557 rb_define_method(rb_cStat, "sticky?", rb_stat_sticky, 0);
05558
05559 #ifdef _WIN32
05560 rb_w32_init_file();
05561 #endif
05562 }
05563