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