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