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