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