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