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