|
Ruby
1.9.3p448(2013-06-27revision41675)
|
00001 /********************************************************************** 00002 00003 file.c - 00004 00005 $Author: usa $ 00006 created at: Mon Nov 15 12:24:34 JST 1993 00007 00008 Copyright (C) 1993-2007 Yukihiro Matsumoto 00009 Copyright (C) 2000 Network Applied Communication Laboratory, Inc. 00010 Copyright (C) 2000 Information-technology Promotion Agency, Japan 00011 00012 **********************************************************************/ 00013 00014 #ifdef _WIN32 00015 #include "missing/file.h" 00016 #endif 00017 #ifdef __CYGWIN__ 00018 #include <windows.h> 00019 #include <sys/cygwin.h> 00020 #include <wchar.h> 00021 #endif 00022 00023 #include "ruby/ruby.h" 00024 #include "ruby/io.h" 00025 #include "ruby/util.h" 00026 #include "dln.h" 00027 #include "internal.h" 00028 00029 #ifdef HAVE_UNISTD_H 00030 #include <unistd.h> 00031 #endif 00032 00033 #ifdef HAVE_SYS_FILE_H 00034 # include <sys/file.h> 00035 #else 00036 int flock(int, int); 00037 #endif 00038 00039 #ifdef HAVE_SYS_PARAM_H 00040 # include <sys/param.h> 00041 #endif 00042 #ifndef MAXPATHLEN 00043 # define MAXPATHLEN 1024 00044 #endif 00045 00046 #include <ctype.h> 00047 00048 #include <time.h> 00049 00050 #ifdef HAVE_UTIME_H 00051 #include <utime.h> 00052 #elif defined HAVE_SYS_UTIME_H 00053 #include <sys/utime.h> 00054 #endif 00055 00056 #ifdef HAVE_PWD_H 00057 #include <pwd.h> 00058 #endif 00059 00060 #include <sys/types.h> 00061 #include <sys/stat.h> 00062 00063 #ifdef HAVE_SYS_MKDEV_H 00064 #include <sys/mkdev.h> 00065 #endif 00066 00067 #if defined(HAVE_FCNTL_H) 00068 #include <fcntl.h> 00069 #endif 00070 00071 #if defined(HAVE_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 #if USE_NTFS 02788 static char * 02789 ntfs_tail(const char *path, const char *end, rb_encoding *enc) 02790 { 02791 while (path < end && *path == '.') path++; 02792 while (path < end && *path != ':') { 02793 if (istrailinggarbage(*path)) { 02794 const char *last = path++; 02795 while (path < end && istrailinggarbage(*path)) path++; 02796 if (path >= end || *path == ':') return (char *)last; 02797 } 02798 else if (isdirsep(*path)) { 02799 const char *last = path++; 02800 while (path < end && isdirsep(*path)) path++; 02801 if (path >= end) return (char *)last; 02802 if (*path == ':') path++; 02803 } 02804 else { 02805 Inc(path, end, enc); 02806 } 02807 } 02808 return (char *)path; 02809 } 02810 #endif 02811 02812 #define BUFCHECK(cond) do {\ 02813 bdiff = p - buf;\ 02814 if (cond) {\ 02815 do {buflen *= 2;} while (cond);\ 02816 rb_str_resize(result, buflen);\ 02817 buf = RSTRING_PTR(result);\ 02818 p = buf + bdiff;\ 02819 pend = buf + buflen;\ 02820 }\ 02821 } while (0) 02822 02823 #define BUFINIT() (\ 02824 p = buf = RSTRING_PTR(result),\ 02825 buflen = RSTRING_LEN(result),\ 02826 pend = p + buflen) 02827 02828 VALUE 02829 rb_home_dir(const char *user, VALUE result) 02830 { 02831 const char *dir; 02832 char *buf; 02833 #if defined DOSISH || defined __CYGWIN__ 02834 char *p, *bend; 02835 #endif 02836 long dirlen; 02837 rb_encoding *enc; 02838 02839 if (!user || !*user) { 02840 if (!(dir = getenv("HOME"))) { 02841 rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'"); 02842 } 02843 dirlen = strlen(dir); 02844 rb_str_resize(result, dirlen); 02845 memcpy(buf = RSTRING_PTR(result), dir, dirlen); 02846 } 02847 else { 02848 #ifdef HAVE_PWD_H 02849 struct passwd *pwPtr = getpwnam(user); 02850 if (!pwPtr) { 02851 endpwent(); 02852 rb_raise(rb_eArgError, "user %s doesn't exist", user); 02853 } 02854 dirlen = strlen(pwPtr->pw_dir); 02855 rb_str_resize(result, dirlen); 02856 memcpy(buf = RSTRING_PTR(result), pwPtr->pw_dir, dirlen + 1); 02857 endpwent(); 02858 #else 02859 return Qnil; 02860 #endif 02861 } 02862 enc = rb_filesystem_encoding(); 02863 rb_enc_associate(result, enc); 02864 #if defined DOSISH || defined __CYGWIN__ 02865 for (bend = (p = buf) + dirlen; p < bend; Inc(p, bend, enc)) { 02866 if (*p == '\\') { 02867 *p = '/'; 02868 } 02869 } 02870 #endif 02871 return result; 02872 } 02873 02874 #ifndef _WIN32 02875 static char * 02876 append_fspath(VALUE result, VALUE fname, char *dir, rb_encoding **enc, rb_encoding *fsenc) 02877 { 02878 char *buf, *cwdp = dir; 02879 VALUE dirname = Qnil; 02880 size_t dirlen = strlen(dir), buflen = rb_str_capacity(result); 02881 02882 *enc = fsenc; 02883 do {buflen *= 2;} while (dirlen > buflen); 02884 rb_str_resize(result, buflen); 02885 buf = RSTRING_PTR(result); 02886 memcpy(buf, cwdp, dirlen); 02887 xfree(dir); 02888 if (!NIL_P(dirname)) rb_str_resize(dirname, 0); 02889 rb_enc_associate(result, *enc); 02890 return buf + dirlen; 02891 } 02892 02893 VALUE 02894 rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_name, VALUE result) 02895 { 02896 const char *s, *b, *fend; 02897 char *buf, *p, *pend, *root; 02898 size_t buflen, bdiff; 02899 int tainted; 02900 rb_encoding *enc, *fsenc = rb_filesystem_encoding(); 02901 02902 s = StringValuePtr(fname); 02903 fend = s + RSTRING_LEN(fname); 02904 enc = rb_enc_get(fname); 02905 BUFINIT(); 02906 tainted = OBJ_TAINTED(fname); 02907 02908 if (s[0] == '~' && abs_mode == 0) { /* execute only if NOT absolute_path() */ 02909 long userlen = 0; 02910 tainted = 1; 02911 if (isdirsep(s[1]) || s[1] == '\0') { 02912 buf = 0; 02913 b = 0; 02914 rb_str_set_len(result, 0); 02915 if (*++s) ++s; 02916 } 02917 else { 02918 s = nextdirsep(b = s, fend, enc); 02919 userlen = s - b; 02920 BUFCHECK(bdiff + userlen >= buflen); 02921 memcpy(p, b, userlen); 02922 rb_str_set_len(result, userlen); 02923 buf = p + 1; 02924 p += userlen; 02925 } 02926 if (NIL_P(rb_home_dir(buf, result))) { 02927 rb_raise(rb_eArgError, "can't find user %s", buf); 02928 } 02929 if (!rb_is_absolute_path(RSTRING_PTR(result))) { 02930 if (userlen) { 02931 rb_raise(rb_eArgError, "non-absolute home of %.*s", (int)userlen, b); 02932 } 02933 else { 02934 rb_raise(rb_eArgError, "non-absolute home"); 02935 } 02936 } 02937 BUFINIT(); 02938 p = pend; 02939 } 02940 #ifdef DOSISH_DRIVE_LETTER 02941 /* skip drive letter */ 02942 else if (has_drive_letter(s)) { 02943 if (isdirsep(s[2])) { 02944 /* specified drive letter, and full path */ 02945 /* skip drive letter */ 02946 BUFCHECK(bdiff + 2 >= buflen); 02947 memcpy(p, s, 2); 02948 p += 2; 02949 s += 2; 02950 rb_enc_copy(result, fname); 02951 } 02952 else { 02953 /* specified drive, but not full path */ 02954 int same = 0; 02955 if (!NIL_P(dname) && !not_same_drive(dname, s[0])) { 02956 rb_file_expand_path_internal(dname, Qnil, abs_mode, long_name, result); 02957 BUFINIT(); 02958 if (has_drive_letter(p) && TOLOWER(p[0]) == TOLOWER(s[0])) { 02959 /* ok, same drive */ 02960 same = 1; 02961 } 02962 } 02963 if (!same) { 02964 char *e = append_fspath(result, fname, getcwdofdrv(*s), &enc, fsenc); 02965 tainted = 1; 02966 BUFINIT(); 02967 p = e; 02968 } 02969 else { 02970 rb_enc_associate(result, enc = rb_enc_check(result, fname)); 02971 p = pend; 02972 } 02973 p = chompdirsep(skiproot(buf, p, enc), p, enc); 02974 s += 2; 02975 } 02976 } 02977 #endif 02978 else if (!rb_is_absolute_path(s)) { 02979 if (!NIL_P(dname)) { 02980 rb_file_expand_path_internal(dname, Qnil, abs_mode, long_name, result); 02981 rb_enc_associate(result, rb_enc_check(result, fname)); 02982 BUFINIT(); 02983 p = pend; 02984 } 02985 else { 02986 char *e = append_fspath(result, fname, my_getcwd(), &enc, fsenc); 02987 tainted = 1; 02988 BUFINIT(); 02989 p = e; 02990 } 02991 #if defined DOSISH || defined __CYGWIN__ 02992 if (isdirsep(*s)) { 02993 /* specified full path, but not drive letter nor UNC */ 02994 /* we need to get the drive letter or UNC share name */ 02995 p = skipprefix(buf, p, enc); 02996 } 02997 else 02998 #endif 02999 p = chompdirsep(skiproot(buf, p, enc), p, enc); 03000 } 03001 else { 03002 size_t len; 03003 b = s; 03004 do s++; while (isdirsep(*s)); 03005 len = s - b; 03006 p = buf + len; 03007 BUFCHECK(bdiff >= buflen); 03008 memset(buf, '/', len); 03009 rb_str_set_len(result, len); 03010 rb_enc_associate(result, rb_enc_check(result, fname)); 03011 } 03012 if (p > buf && p[-1] == '/') 03013 --p; 03014 else { 03015 rb_str_set_len(result, p-buf); 03016 BUFCHECK(bdiff + 1 >= buflen); 03017 *p = '/'; 03018 } 03019 03020 rb_str_set_len(result, p-buf+1); 03021 BUFCHECK(bdiff + 1 >= buflen); 03022 p[1] = 0; 03023 root = skipprefix(buf, p+1, enc); 03024 03025 b = s; 03026 while (*s) { 03027 switch (*s) { 03028 case '.': 03029 if (b == s++) { /* beginning of path element */ 03030 switch (*s) { 03031 case '\0': 03032 b = s; 03033 break; 03034 case '.': 03035 if (*(s+1) == '\0' || isdirsep(*(s+1))) { 03036 /* We must go back to the parent */ 03037 char *n; 03038 *p = '\0'; 03039 if (!(n = strrdirsep(root, p, enc))) { 03040 *p = '/'; 03041 } 03042 else { 03043 p = n; 03044 } 03045 b = ++s; 03046 } 03047 #if USE_NTFS 03048 else { 03049 do ++s; while (istrailinggarbage(*s)); 03050 } 03051 #endif 03052 break; 03053 case '/': 03054 #if defined DOSISH || defined __CYGWIN__ 03055 case '\\': 03056 #endif 03057 b = ++s; 03058 break; 03059 default: 03060 /* ordinary path element, beginning don't move */ 03061 break; 03062 } 03063 } 03064 #if USE_NTFS 03065 else { 03066 --s; 03067 case ' ': { 03068 const char *e = s; 03069 while (s < fend && istrailinggarbage(*s)) s++; 03070 if (!*s) { 03071 s = e; 03072 goto endpath; 03073 } 03074 } 03075 } 03076 #endif 03077 break; 03078 case '/': 03079 #if defined DOSISH || defined __CYGWIN__ 03080 case '\\': 03081 #endif 03082 if (s > b) { 03083 long rootdiff = root - buf; 03084 rb_str_set_len(result, p-buf+1); 03085 BUFCHECK(bdiff + (s-b+1) >= buflen); 03086 root = buf + rootdiff; 03087 memcpy(++p, b, s-b); 03088 p += s-b; 03089 *p = '/'; 03090 } 03091 b = ++s; 03092 break; 03093 default: 03094 Inc(s, fend, enc); 03095 break; 03096 } 03097 } 03098 03099 if (s > b) { 03100 #if USE_NTFS 03101 static const char prime[] = ":$DATA"; 03102 enum {prime_len = sizeof(prime) -1}; 03103 endpath: 03104 if (s > b + prime_len && strncasecmp(s - prime_len, prime, prime_len) == 0) { 03105 /* alias of stream */ 03106 /* get rid of a bug of x64 VC++ */ 03107 if (*(s - (prime_len+1)) == ':') { 03108 s -= prime_len + 1; /* prime */ 03109 } 03110 else if (memchr(b, ':', s - prime_len - b)) { 03111 s -= prime_len; /* alternative */ 03112 } 03113 } 03114 #endif 03115 rb_str_set_len(result, p-buf+1); 03116 BUFCHECK(bdiff + (s-b) >= buflen); 03117 memcpy(++p, b, s-b); 03118 p += s-b; 03119 rb_str_set_len(result, p-buf); 03120 } 03121 if (p == skiproot(buf, p + !!*p, enc) - 1) p++; 03122 03123 #if USE_NTFS 03124 *p = '\0'; 03125 if ((s = strrdirsep(b = buf, p, enc)) != 0 && !strpbrk(s, "*?")) { 03126 VALUE tmp, v; 03127 size_t len; 03128 rb_encoding *enc; 03129 WCHAR *wstr; 03130 WIN32_FIND_DATAW wfd; 03131 HANDLE h; 03132 #ifdef __CYGWIN__ 03133 #ifdef HAVE_CYGWIN_CONV_PATH 03134 char *w32buf = NULL; 03135 const int flags = CCP_POSIX_TO_WIN_A | CCP_RELATIVE; 03136 #else 03137 char w32buf[MAXPATHLEN]; 03138 #endif 03139 const char *path; 03140 ssize_t bufsize; 03141 int lnk_added = 0, is_symlink = 0; 03142 struct stat st; 03143 p = (char *)s; 03144 len = strlen(p); 03145 if (lstat(buf, &st) == 0 && S_ISLNK(st.st_mode)) { 03146 is_symlink = 1; 03147 if (len > 4 && STRCASECMP(p + len - 4, ".lnk") != 0) { 03148 lnk_added = 1; 03149 } 03150 } 03151 path = *buf ? buf : "/"; 03152 #ifdef HAVE_CYGWIN_CONV_PATH 03153 bufsize = cygwin_conv_path(flags, path, NULL, 0); 03154 if (bufsize > 0) { 03155 bufsize += len; 03156 if (lnk_added) bufsize += 4; 03157 w32buf = ALLOCA_N(char, bufsize); 03158 if (cygwin_conv_path(flags, path, w32buf, bufsize) == 0) { 03159 b = w32buf; 03160 } 03161 } 03162 #else 03163 bufsize = MAXPATHLEN; 03164 if (cygwin_conv_to_win32_path(path, w32buf) == 0) { 03165 b = w32buf; 03166 } 03167 #endif 03168 if (is_symlink && b == w32buf) { 03169 *p = '\\'; 03170 strlcat(w32buf, p, bufsize); 03171 if (lnk_added) { 03172 strlcat(w32buf, ".lnk", bufsize); 03173 } 03174 } 03175 else { 03176 lnk_added = 0; 03177 } 03178 *p = '/'; 03179 #endif 03180 rb_str_set_len(result, p - buf + strlen(p)); 03181 enc = rb_enc_get(result); 03182 tmp = result; 03183 if (enc != rb_utf8_encoding() && rb_enc_str_coderange(result) != ENC_CODERANGE_7BIT) { 03184 tmp = rb_str_encode_ospath(result); 03185 } 03186 len = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, NULL, 0); 03187 wstr = ALLOCV_N(WCHAR, v, len); 03188 MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, wstr, len); 03189 if (tmp != result) rb_str_resize(tmp, 0); 03190 h = FindFirstFileW(wstr, &wfd); 03191 ALLOCV_END(v); 03192 if (h != INVALID_HANDLE_VALUE) { 03193 size_t wlen; 03194 FindClose(h); 03195 len = lstrlenW(wfd.cFileName); 03196 #ifdef __CYGWIN__ 03197 if (lnk_added && len > 4 && 03198 wcscasecmp(wfd.cFileName + len - 4, L".lnk") == 0) { 03199 wfd.cFileName[len -= 4] = L'\0'; 03200 } 03201 #else 03202 p = (char *)s; 03203 #endif 03204 ++p; 03205 wlen = (int)len; 03206 len = WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, NULL, 0, NULL, NULL); 03207 BUFCHECK(bdiff + len >= buflen); 03208 WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, p, len + 1, NULL, NULL); 03209 if (tmp != result) { 03210 rb_str_buf_cat(tmp, p, len); 03211 tmp = rb_str_encode(tmp, rb_enc_from_encoding(enc), 0, Qnil); 03212 len = RSTRING_LEN(tmp); 03213 BUFCHECK(bdiff + len >= buflen); 03214 memcpy(p, RSTRING_PTR(tmp), len); 03215 rb_str_resize(tmp, 0); 03216 } 03217 p += len; 03218 } 03219 #ifdef __CYGWIN__ 03220 else { 03221 p += strlen(p); 03222 } 03223 #endif 03224 } 03225 #endif 03226 03227 if (tainted) OBJ_TAINT(result); 03228 rb_str_set_len(result, p - buf); 03229 rb_enc_check(fname, result); 03230 ENC_CODERANGE_CLEAR(result); 03231 return result; 03232 } 03233 #endif /* _WIN32 */ 03234 03235 #define EXPAND_PATH_BUFFER() rb_enc_str_new(0, MAXPATHLEN + 2, rb_filesystem_encoding()) 03236 03237 #define check_expand_path_args(fname, dname) \ 03238 (((fname) = rb_get_path(fname)), \ 03239 (void)(NIL_P(dname) ? (dname) : ((dname) = rb_get_path(dname)))) 03240 03241 static VALUE 03242 file_expand_path_1(VALUE fname) 03243 { 03244 return rb_file_expand_path_internal(fname, Qnil, 0, 0, EXPAND_PATH_BUFFER()); 03245 } 03246 03247 VALUE 03248 rb_file_expand_path(VALUE fname, VALUE dname) 03249 { 03250 check_expand_path_args(fname, dname); 03251 return rb_file_expand_path_internal(fname, dname, 0, 1, EXPAND_PATH_BUFFER()); 03252 } 03253 03254 VALUE 03255 rb_file_expand_path_fast(VALUE fname, VALUE dname) 03256 { 03257 check_expand_path_args(fname, dname); 03258 return rb_file_expand_path_internal(fname, dname, 0, 0, EXPAND_PATH_BUFFER()); 03259 } 03260 03261 /* 03262 * call-seq: 03263 * File.expand_path(file_name [, dir_string] ) -> abs_file_name 03264 * 03265 * Converts a pathname to an absolute pathname. Relative paths are 03266 * referenced from the current working directory of the process unless 03267 * <i>dir_string</i> is given, in which case it will be used as the 03268 * starting point. The given pathname may start with a 03269 * ``<code>~</code>'', which expands to the process owner's home 03270 * directory (the environment variable <code>HOME</code> must be set 03271 * correctly). ``<code>~</code><i>user</i>'' expands to the named 03272 * user's home directory. 03273 * 03274 * File.expand_path("~oracle/bin") #=> "/home/oracle/bin" 03275 * File.expand_path("../../bin", "/tmp/x") #=> "/bin" 03276 */ 03277 03278 VALUE 03279 rb_file_s_expand_path(int argc, VALUE *argv) 03280 { 03281 VALUE fname, dname; 03282 03283 if (argc == 1) { 03284 return rb_file_expand_path(argv[0], Qnil); 03285 } 03286 rb_scan_args(argc, argv, "11", &fname, &dname); 03287 03288 return rb_file_expand_path(fname, dname); 03289 } 03290 03291 VALUE 03292 rb_file_absolute_path(VALUE fname, VALUE dname) 03293 { 03294 check_expand_path_args(fname, dname); 03295 return rb_file_expand_path_internal(fname, dname, 1, 1, EXPAND_PATH_BUFFER()); 03296 } 03297 03298 /* 03299 * call-seq: 03300 * File.absolute_path(file_name [, dir_string] ) -> abs_file_name 03301 * 03302 * Converts a pathname to an absolute pathname. Relative paths are 03303 * referenced from the current working directory of the process unless 03304 * <i>dir_string</i> is given, in which case it will be used as the 03305 * starting point. If the given pathname starts with a ``<code>~</code>'' 03306 * it is NOT expanded, it is treated as a normal directory name. 03307 * 03308 * File.absolute_path("~oracle/bin") #=> "<relative_path>/~oracle/bin" 03309 */ 03310 03311 VALUE 03312 rb_file_s_absolute_path(int argc, VALUE *argv) 03313 { 03314 VALUE fname, dname; 03315 03316 if (argc == 1) { 03317 return rb_file_absolute_path(argv[0], Qnil); 03318 } 03319 rb_scan_args(argc, argv, "11", &fname, &dname); 03320 03321 return rb_file_absolute_path(fname, dname); 03322 } 03323 03324 static void 03325 realpath_rec(long *prefixlenp, VALUE *resolvedp, const char *unresolved, VALUE loopcheck, int strict, int last) 03326 { 03327 const char *pend = unresolved + strlen(unresolved); 03328 rb_encoding *enc = rb_enc_get(*resolvedp); 03329 ID resolving; 03330 CONST_ID(resolving, "resolving"); 03331 while (unresolved < pend) { 03332 const char *testname = unresolved; 03333 const char *unresolved_firstsep = rb_enc_path_next(unresolved, pend, enc); 03334 long testnamelen = unresolved_firstsep - unresolved; 03335 const char *unresolved_nextname = unresolved_firstsep; 03336 while (unresolved_nextname < pend && isdirsep(*unresolved_nextname)) 03337 unresolved_nextname++; 03338 unresolved = unresolved_nextname; 03339 if (testnamelen == 1 && testname[0] == '.') { 03340 } 03341 else if (testnamelen == 2 && testname[0] == '.' && testname[1] == '.') { 03342 if (*prefixlenp < RSTRING_LEN(*resolvedp)) { 03343 const char *resolved_str = RSTRING_PTR(*resolvedp); 03344 const char *resolved_names = resolved_str + *prefixlenp; 03345 const char *lastsep = strrdirsep(resolved_names, resolved_str + RSTRING_LEN(*resolvedp), enc); 03346 long len = lastsep ? lastsep - resolved_names : 0; 03347 rb_str_resize(*resolvedp, *prefixlenp + len); 03348 } 03349 } 03350 else { 03351 VALUE checkval; 03352 VALUE testpath = rb_str_dup(*resolvedp); 03353 if (*prefixlenp < RSTRING_LEN(testpath)) 03354 rb_str_cat2(testpath, "/"); 03355 rb_str_cat(testpath, testname, testnamelen); 03356 checkval = rb_hash_aref(loopcheck, testpath); 03357 if (!NIL_P(checkval)) { 03358 if (checkval == ID2SYM(resolving)) { 03359 errno = ELOOP; 03360 rb_sys_fail_path(testpath); 03361 } 03362 else { 03363 *resolvedp = rb_str_dup(checkval); 03364 } 03365 } 03366 else { 03367 struct stat sbuf; 03368 int ret; 03369 VALUE testpath2 = rb_str_encode_ospath(testpath); 03370 ret = lstat(RSTRING_PTR(testpath2), &sbuf); 03371 if (ret == -1) { 03372 if (errno == ENOENT) { 03373 if (strict || !last || *unresolved_firstsep) 03374 rb_sys_fail_path(testpath); 03375 *resolvedp = testpath; 03376 break; 03377 } 03378 else { 03379 rb_sys_fail_path(testpath); 03380 } 03381 } 03382 #ifdef HAVE_READLINK 03383 if (S_ISLNK(sbuf.st_mode)) { 03384 VALUE link; 03385 volatile VALUE link_orig = Qnil; 03386 const char *link_prefix, *link_names; 03387 long link_prefixlen; 03388 rb_hash_aset(loopcheck, testpath, ID2SYM(resolving)); 03389 link = rb_readlink(testpath); 03390 link_prefix = RSTRING_PTR(link); 03391 link_names = skipprefixroot(link_prefix, link_prefix + RSTRING_LEN(link), rb_enc_get(link)); 03392 link_prefixlen = link_names - link_prefix; 03393 if (link_prefixlen > 0) { 03394 rb_encoding *enc, *linkenc = rb_enc_get(link); 03395 link_orig = link; 03396 link = rb_str_subseq(link, 0, link_prefixlen); 03397 enc = rb_enc_check(*resolvedp, link); 03398 if (enc != linkenc) link = rb_str_conv_enc(link, linkenc, enc); 03399 *resolvedp = link; 03400 *prefixlenp = link_prefixlen; 03401 } 03402 realpath_rec(prefixlenp, resolvedp, link_names, loopcheck, strict, *unresolved_firstsep == '\0'); 03403 RB_GC_GUARD(link_orig); 03404 rb_hash_aset(loopcheck, testpath, rb_str_dup_frozen(*resolvedp)); 03405 } 03406 else 03407 #endif 03408 { 03409 VALUE s = rb_str_dup_frozen(testpath); 03410 rb_hash_aset(loopcheck, s, s); 03411 *resolvedp = testpath; 03412 } 03413 } 03414 } 03415 } 03416 } 03417 03418 VALUE 03419 rb_realpath_internal(VALUE basedir, VALUE path, int strict) 03420 { 03421 long prefixlen; 03422 VALUE resolved; 03423 volatile VALUE unresolved_path; 03424 VALUE loopcheck; 03425 volatile VALUE curdir = Qnil; 03426 03427 rb_encoding *enc; 03428 char *path_names = NULL, *basedir_names = NULL, *curdir_names = NULL; 03429 char *ptr, *prefixptr = NULL, *pend; 03430 long len; 03431 03432 rb_secure(2); 03433 03434 FilePathValue(path); 03435 unresolved_path = rb_str_dup_frozen(path); 03436 03437 if (!NIL_P(basedir)) { 03438 FilePathValue(basedir); 03439 basedir = rb_str_dup_frozen(basedir); 03440 } 03441 03442 RSTRING_GETMEM(unresolved_path, ptr, len); 03443 path_names = skipprefixroot(ptr, ptr + len, rb_enc_get(unresolved_path)); 03444 if (ptr != path_names) { 03445 resolved = rb_str_subseq(unresolved_path, 0, path_names - ptr); 03446 goto root_found; 03447 } 03448 03449 if (!NIL_P(basedir)) { 03450 RSTRING_GETMEM(basedir, ptr, len); 03451 basedir_names = skipprefixroot(ptr, ptr + len, rb_enc_get(basedir)); 03452 if (ptr != basedir_names) { 03453 resolved = rb_str_subseq(basedir, 0, basedir_names - ptr); 03454 goto root_found; 03455 } 03456 } 03457 03458 curdir = rb_dir_getwd(); 03459 RSTRING_GETMEM(curdir, ptr, len); 03460 curdir_names = skipprefixroot(ptr, ptr + len, rb_enc_get(curdir)); 03461 resolved = rb_str_subseq(curdir, 0, curdir_names - ptr); 03462 03463 root_found: 03464 RSTRING_GETMEM(resolved, prefixptr, prefixlen); 03465 pend = prefixptr + prefixlen; 03466 enc = rb_enc_get(resolved); 03467 ptr = chompdirsep(prefixptr, pend, enc); 03468 if (ptr < pend) { 03469 prefixlen = ++ptr - prefixptr; 03470 rb_str_set_len(resolved, prefixlen); 03471 } 03472 #ifdef FILE_ALT_SEPARATOR 03473 while (prefixptr < ptr) { 03474 if (*prefixptr == FILE_ALT_SEPARATOR) { 03475 *prefixptr = '/'; 03476 } 03477 Inc(prefixptr, pend, enc); 03478 } 03479 #endif 03480 03481 loopcheck = rb_hash_new(); 03482 if (curdir_names) 03483 realpath_rec(&prefixlen, &resolved, curdir_names, loopcheck, 1, 0); 03484 if (basedir_names) 03485 realpath_rec(&prefixlen, &resolved, basedir_names, loopcheck, 1, 0); 03486 realpath_rec(&prefixlen, &resolved, path_names, loopcheck, strict, 1); 03487 03488 OBJ_TAINT(resolved); 03489 return resolved; 03490 } 03491 03492 /* 03493 * call-seq: 03494 * File.realpath(pathname [, dir_string]) -> real_pathname 03495 * 03496 * Returns the real (absolute) pathname of _pathname_ in the actual 03497 * filesystem not containing symlinks or useless dots. 03498 * 03499 * If _dir_string_ is given, it is used as a base directory 03500 * for interpreting relative pathname instead of the current directory. 03501 * 03502 * All components of the pathname must exist when this method is 03503 * called. 03504 */ 03505 static VALUE 03506 rb_file_s_realpath(int argc, VALUE *argv, VALUE klass) 03507 { 03508 VALUE path, basedir; 03509 rb_scan_args(argc, argv, "11", &path, &basedir); 03510 return rb_realpath_internal(basedir, path, 1); 03511 } 03512 03513 /* 03514 * call-seq: 03515 * File.realdirpath(pathname [, dir_string]) -> real_pathname 03516 * 03517 * Returns the real (absolute) pathname of _pathname_ in the actual filesystem. 03518 * The real pathname doesn't contain symlinks or useless dots. 03519 * 03520 * If _dir_string_ is given, it is used as a base directory 03521 * for interpreting relative pathname instead of the current directory. 03522 * 03523 * The last component of the real pathname can be nonexistent. 03524 */ 03525 static VALUE 03526 rb_file_s_realdirpath(int argc, VALUE *argv, VALUE klass) 03527 { 03528 VALUE path, basedir; 03529 rb_scan_args(argc, argv, "11", &path, &basedir); 03530 return rb_realpath_internal(basedir, path, 0); 03531 } 03532 03533 static size_t 03534 rmext(const char *p, long l0, long l1, const char *e, long l2, rb_encoding *enc) 03535 { 03536 int len1, len2; 03537 unsigned int c; 03538 const char *s, *last; 03539 03540 if (!e || !l2) return 0; 03541 03542 c = rb_enc_codepoint_len(e, e + l2, &len1, enc); 03543 if (rb_enc_ascget(e + len1, e + l2, &len2, enc) == '*' && len1 + len2 == l2) { 03544 if (c == '.') return l0; 03545 s = p; 03546 e = p + l1; 03547 last = e; 03548 while (s < e) { 03549 if (rb_enc_codepoint_len(s, e, &len1, enc) == c) last = s; 03550 s += len1; 03551 } 03552 return last - p; 03553 } 03554 if (l1 < l2) return l1; 03555 03556 s = p+l1-l2; 03557 if (rb_enc_left_char_head(p, s, p+l1, enc) != s) return 0; 03558 #if CASEFOLD_FILESYSTEM 03559 #define fncomp strncasecmp 03560 #else 03561 #define fncomp strncmp 03562 #endif 03563 if (fncomp(s, e, l2) == 0) { 03564 return l1-l2; 03565 } 03566 return 0; 03567 } 03568 03569 const char * 03570 ruby_enc_find_basename(const char *name, long *baselen, long *alllen, rb_encoding *enc) 03571 { 03572 const char *p, *q, *e, *end; 03573 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC 03574 const char *root; 03575 #endif 03576 long f = 0, n = -1; 03577 03578 end = name + *alllen; 03579 name = skipprefix(name, end, enc); 03580 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC 03581 root = name; 03582 #endif 03583 while (isdirsep(*name)) 03584 name++; 03585 if (!*name) { 03586 p = name - 1; 03587 f = 1; 03588 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC 03589 if (name != root) { 03590 /* has slashes */ 03591 } 03592 #ifdef DOSISH_DRIVE_LETTER 03593 else if (*p == ':') { 03594 p++; 03595 f = 0; 03596 } 03597 #endif 03598 #ifdef DOSISH_UNC 03599 else { 03600 p = "/"; 03601 } 03602 #endif 03603 #endif 03604 } 03605 else { 03606 if (!(p = strrdirsep(name, end, enc))) { 03607 p = name; 03608 } 03609 else { 03610 while (isdirsep(*p)) p++; /* skip last / */ 03611 } 03612 #if USE_NTFS 03613 n = ntfs_tail(p, end, enc) - p; 03614 #else 03615 n = chompdirsep(p, end, enc) - p; 03616 #endif 03617 for (q = p; q - p < n && *q == '.'; q++); 03618 for (e = 0; q - p < n; Inc(q, end, enc)) { 03619 if (*q == '.') e = q; 03620 } 03621 if (e) f = e - p; 03622 else f = n; 03623 } 03624 03625 if (baselen) 03626 *baselen = f; 03627 if (alllen) 03628 *alllen = n; 03629 return p; 03630 } 03631 03632 /* 03633 * call-seq: 03634 * File.basename(file_name [, suffix] ) -> base_name 03635 * 03636 * Returns the last component of the filename given in <i>file_name</i>, 03637 * which must be formed using forward slashes (``<code>/</code>'') 03638 * regardless of the separator used on the local file system. If 03639 * <i>suffix</i> is given and present at the end of <i>file_name</i>, 03640 * it is removed. 03641 * 03642 * File.basename("/home/gumby/work/ruby.rb") #=> "ruby.rb" 03643 * File.basename("/home/gumby/work/ruby.rb", ".rb") #=> "ruby" 03644 */ 03645 03646 static VALUE 03647 rb_file_s_basename(int argc, VALUE *argv) 03648 { 03649 VALUE fname, fext, basename; 03650 const char *name, *p; 03651 long f, n; 03652 rb_encoding *enc; 03653 03654 if (rb_scan_args(argc, argv, "11", &fname, &fext) == 2) { 03655 rb_encoding *enc; 03656 StringValue(fext); 03657 if (!rb_enc_asciicompat(enc = rb_enc_get(fext))) { 03658 rb_raise(rb_eEncCompatError, "ascii incompatible character encodings: %s", 03659 rb_enc_name(enc)); 03660 } 03661 } 03662 FilePathStringValue(fname); 03663 if (!NIL_P(fext)) enc = rb_enc_check(fname, fext); 03664 else enc = rb_enc_get(fname); 03665 if ((n = RSTRING_LEN(fname)) == 0 || !*(name = RSTRING_PTR(fname))) 03666 return rb_str_new_shared(fname); 03667 03668 p = ruby_enc_find_basename(name, &f, &n, enc); 03669 if (n >= 0) { 03670 if (NIL_P(fext)) { 03671 f = n; 03672 } 03673 else { 03674 rb_encoding *fenc = rb_enc_get(fext); 03675 const char *fp; 03676 if (enc != fenc && 03677 rb_enc_str_coderange(fext) != ENC_CODERANGE_7BIT) { 03678 fext = rb_str_conv_enc(fext, fenc, enc); 03679 } 03680 fp = StringValueCStr(fext); 03681 if (!(f = rmext(p, f, n, fp, RSTRING_LEN(fext), enc))) { 03682 f = n; 03683 } 03684 RB_GC_GUARD(fext); 03685 } 03686 if (f == RSTRING_LEN(fname)) return rb_str_new_shared(fname); 03687 } 03688 03689 basename = rb_str_new(p, f); 03690 rb_enc_copy(basename, fname); 03691 OBJ_INFECT(basename, fname); 03692 return basename; 03693 } 03694 03695 /* 03696 * call-seq: 03697 * File.dirname(file_name ) -> dir_name 03698 * 03699 * Returns all components of the filename given in <i>file_name</i> 03700 * except the last one. The filename must be formed using forward 03701 * slashes (``<code>/</code>'') regardless of the separator used on the 03702 * local file system. 03703 * 03704 * File.dirname("/home/gumby/work/ruby.rb") #=> "/home/gumby/work" 03705 */ 03706 03707 static VALUE 03708 rb_file_s_dirname(VALUE klass, VALUE fname) 03709 { 03710 return rb_file_dirname(fname); 03711 } 03712 03713 VALUE 03714 rb_file_dirname(VALUE fname) 03715 { 03716 const char *name, *root, *p, *end; 03717 VALUE dirname; 03718 rb_encoding *enc; 03719 03720 FilePathStringValue(fname); 03721 name = StringValueCStr(fname); 03722 end = name + RSTRING_LEN(fname); 03723 enc = rb_enc_get(fname); 03724 root = skiproot(name, end, enc); 03725 #ifdef DOSISH_UNC 03726 if (root > name + 1 && isdirsep(*name)) 03727 root = skipprefix(name = root - 2, end, enc); 03728 #else 03729 if (root > name + 1) 03730 name = root - 1; 03731 #endif 03732 p = strrdirsep(root, end, enc); 03733 if (!p) { 03734 p = root; 03735 } 03736 if (p == name) 03737 return rb_usascii_str_new2("."); 03738 #ifdef DOSISH_DRIVE_LETTER 03739 if (has_drive_letter(name) && isdirsep(*(name + 2))) { 03740 const char *top = skiproot(name + 2, end, enc); 03741 dirname = rb_str_new(name, 3); 03742 rb_str_cat(dirname, top, p - top); 03743 } 03744 else 03745 #endif 03746 dirname = rb_str_new(name, p - name); 03747 #ifdef DOSISH_DRIVE_LETTER 03748 if (has_drive_letter(name) && root == name + 2 && p - name == 2) 03749 rb_str_cat(dirname, ".", 1); 03750 #endif 03751 rb_enc_copy(dirname, fname); 03752 OBJ_INFECT(dirname, fname); 03753 return dirname; 03754 } 03755 03756 /* 03757 * accept a String, and return the pointer of the extension. 03758 * if len is passed, set the length of extension to it. 03759 * returned pointer is in ``name'' or NULL. 03760 * returns *len 03761 * no dot NULL 0 03762 * dotfile top 0 03763 * end with dot dot 1 03764 * .ext dot len of .ext 03765 * .ext:stream dot len of .ext without :stream (NT only) 03766 * 03767 */ 03768 const char * 03769 ruby_enc_find_extname(const char *name, long *len, rb_encoding *enc) 03770 { 03771 const char *p, *e, *end = name + (len ? *len : (long)strlen(name)); 03772 03773 p = strrdirsep(name, end, enc); /* get the last path component */ 03774 if (!p) 03775 p = name; 03776 else 03777 do name = ++p; while (isdirsep(*p)); 03778 03779 e = 0; 03780 while (*p && *p == '.') p++; 03781 while (*p) { 03782 if (*p == '.' || istrailinggarbage(*p)) { 03783 #if USE_NTFS 03784 const char *last = p++, *dot = last; 03785 while (istrailinggarbage(*p)) { 03786 if (*p == '.') dot = p; 03787 p++; 03788 } 03789 if (!*p || *p == ':') { 03790 p = last; 03791 break; 03792 } 03793 if (*last == '.' || dot > last) e = dot; 03794 continue; 03795 #else 03796 e = p; /* get the last dot of the last component */ 03797 #endif 03798 } 03799 #if USE_NTFS 03800 else if (*p == ':') { 03801 break; 03802 } 03803 #endif 03804 else if (isdirsep(*p)) 03805 break; 03806 Inc(p, end, enc); 03807 } 03808 03809 if (len) { 03810 /* no dot, or the only dot is first or end? */ 03811 if (!e || e == name) 03812 *len = 0; 03813 else if (e+1 == p) 03814 *len = 1; 03815 else 03816 *len = p - e; 03817 } 03818 return e; 03819 } 03820 03821 /* 03822 * call-seq: 03823 * File.extname(path) -> string 03824 * 03825 * Returns the extension (the portion of file name in <i>path</i> 03826 * after the period). 03827 * 03828 * File.extname("test.rb") #=> ".rb" 03829 * File.extname("a/b/d/test.rb") #=> ".rb" 03830 * File.extname("test") #=> "" 03831 * File.extname(".profile") #=> "" 03832 * 03833 */ 03834 03835 static VALUE 03836 rb_file_s_extname(VALUE klass, VALUE fname) 03837 { 03838 const char *name, *e; 03839 long len; 03840 VALUE extname; 03841 03842 FilePathStringValue(fname); 03843 name = StringValueCStr(fname); 03844 len = RSTRING_LEN(fname); 03845 e = ruby_enc_find_extname(name, &len, rb_enc_get(fname)); 03846 if (len <= 1) 03847 return rb_str_new(0, 0); 03848 extname = rb_str_subseq(fname, e - name, len); /* keep the dot, too! */ 03849 OBJ_INFECT(extname, fname); 03850 return extname; 03851 } 03852 03853 /* 03854 * call-seq: 03855 * File.path(path) -> string 03856 * 03857 * Returns the string representation of the path 03858 * 03859 * File.path("/dev/null") #=> "/dev/null" 03860 * File.path(Pathname.new("/tmp")) #=> "/tmp" 03861 * 03862 */ 03863 03864 static VALUE 03865 rb_file_s_path(VALUE klass, VALUE fname) 03866 { 03867 return rb_get_path(fname); 03868 } 03869 03870 /* 03871 * call-seq: 03872 * File.split(file_name) -> array 03873 * 03874 * Splits the given string into a directory and a file component and 03875 * returns them in a two-element array. See also 03876 * <code>File::dirname</code> and <code>File::basename</code>. 03877 * 03878 * File.split("/home/gumby/.profile") #=> ["/home/gumby", ".profile"] 03879 */ 03880 03881 static VALUE 03882 rb_file_s_split(VALUE klass, VALUE path) 03883 { 03884 FilePathStringValue(path); /* get rid of converting twice */ 03885 return rb_assoc_new(rb_file_s_dirname(Qnil, path), rb_file_s_basename(1,&path)); 03886 } 03887 03888 static VALUE separator; 03889 03890 static VALUE rb_file_join(VALUE ary, VALUE sep); 03891 03892 static VALUE 03893 file_inspect_join(VALUE ary, VALUE argp, int recur) 03894 { 03895 VALUE *arg = (VALUE *)argp; 03896 if (recur || ary == arg[0]) rb_raise(rb_eArgError, "recursive array"); 03897 return rb_file_join(arg[0], arg[1]); 03898 } 03899 03900 static VALUE 03901 rb_file_join(VALUE ary, VALUE sep) 03902 { 03903 long len, i; 03904 VALUE result, tmp; 03905 const char *name, *tail; 03906 03907 if (RARRAY_LEN(ary) == 0) return rb_str_new(0, 0); 03908 03909 len = 1; 03910 for (i=0; i<RARRAY_LEN(ary); i++) { 03911 tmp = RARRAY_PTR(ary)[i]; 03912 if (RB_TYPE_P(tmp, T_STRING)) { 03913 len += RSTRING_LEN(tmp); 03914 } 03915 else { 03916 len += 10; 03917 } 03918 } 03919 if (!NIL_P(sep)) { 03920 StringValue(sep); 03921 len += RSTRING_LEN(sep) * RARRAY_LEN(ary) - 1; 03922 } 03923 result = rb_str_buf_new(len); 03924 OBJ_INFECT(result, ary); 03925 for (i=0; i<RARRAY_LEN(ary); i++) { 03926 tmp = RARRAY_PTR(ary)[i]; 03927 switch (TYPE(tmp)) { 03928 case T_STRING: 03929 break; 03930 case T_ARRAY: 03931 if (ary == tmp) { 03932 rb_raise(rb_eArgError, "recursive array"); 03933 } 03934 else { 03935 VALUE args[2]; 03936 03937 args[0] = tmp; 03938 args[1] = sep; 03939 tmp = rb_exec_recursive(file_inspect_join, ary, (VALUE)args); 03940 } 03941 break; 03942 default: 03943 FilePathStringValue(tmp); 03944 } 03945 name = StringValueCStr(result); 03946 len = RSTRING_LEN(result); 03947 if (i == 0) { 03948 rb_enc_copy(result, tmp); 03949 } 03950 else if (!NIL_P(sep)) { 03951 tail = chompdirsep(name, name + len, rb_enc_get(result)); 03952 if (RSTRING_PTR(tmp) && isdirsep(RSTRING_PTR(tmp)[0])) { 03953 rb_str_set_len(result, tail - name); 03954 } 03955 else if (!*tail) { 03956 rb_str_buf_append(result, sep); 03957 } 03958 } 03959 rb_str_buf_append(result, tmp); 03960 } 03961 03962 return result; 03963 } 03964 03965 /* 03966 * call-seq: 03967 * File.join(string, ...) -> path 03968 * 03969 * Returns a new string formed by joining the strings using 03970 * <code>File::SEPARATOR</code>. 03971 * 03972 * File.join("usr", "mail", "gumby") #=> "usr/mail/gumby" 03973 * 03974 */ 03975 03976 static VALUE 03977 rb_file_s_join(VALUE klass, VALUE args) 03978 { 03979 return rb_file_join(args, separator); 03980 } 03981 03982 #if defined(HAVE_TRUNCATE) || defined(HAVE_CHSIZE) 03983 /* 03984 * call-seq: 03985 * File.truncate(file_name, integer) -> 0 03986 * 03987 * Truncates the file <i>file_name</i> to be at most <i>integer</i> 03988 * bytes long. Not available on all platforms. 03989 * 03990 * f = File.new("out", "w") 03991 * f.write("1234567890") #=> 10 03992 * f.close #=> nil 03993 * File.truncate("out", 5) #=> 0 03994 * File.size("out") #=> 5 03995 * 03996 */ 03997 03998 static VALUE 03999 rb_file_s_truncate(VALUE klass, VALUE path, VALUE len) 04000 { 04001 off_t pos; 04002 04003 rb_secure(2); 04004 pos = NUM2OFFT(len); 04005 FilePathValue(path); 04006 path = rb_str_encode_ospath(path); 04007 #ifdef HAVE_TRUNCATE 04008 if (truncate(StringValueCStr(path), pos) < 0) 04009 rb_sys_fail_path(path); 04010 #else /* defined(HAVE_CHSIZE) */ 04011 { 04012 int tmpfd; 04013 04014 if ((tmpfd = open(StringValueCStr(path), 0)) < 0) { 04015 rb_sys_fail_path(path); 04016 } 04017 rb_update_max_fd(tmpfd); 04018 if (chsize(tmpfd, pos) < 0) { 04019 close(tmpfd); 04020 rb_sys_fail_path(path); 04021 } 04022 close(tmpfd); 04023 } 04024 #endif 04025 return INT2FIX(0); 04026 } 04027 #else 04028 #define rb_file_s_truncate rb_f_notimplement 04029 #endif 04030 04031 #if defined(HAVE_FTRUNCATE) || defined(HAVE_CHSIZE) 04032 /* 04033 * call-seq: 04034 * file.truncate(integer) -> 0 04035 * 04036 * Truncates <i>file</i> to at most <i>integer</i> bytes. The file 04037 * must be opened for writing. Not available on all platforms. 04038 * 04039 * f = File.new("out", "w") 04040 * f.syswrite("1234567890") #=> 10 04041 * f.truncate(5) #=> 0 04042 * f.close() #=> nil 04043 * File.size("out") #=> 5 04044 */ 04045 04046 static VALUE 04047 rb_file_truncate(VALUE obj, VALUE len) 04048 { 04049 rb_io_t *fptr; 04050 off_t pos; 04051 04052 rb_secure(2); 04053 pos = NUM2OFFT(len); 04054 GetOpenFile(obj, fptr); 04055 if (!(fptr->mode & FMODE_WRITABLE)) { 04056 rb_raise(rb_eIOError, "not opened for writing"); 04057 } 04058 rb_io_flush(obj); 04059 #ifdef HAVE_FTRUNCATE 04060 if (ftruncate(fptr->fd, pos) < 0) 04061 rb_sys_fail_path(fptr->pathv); 04062 #else /* defined(HAVE_CHSIZE) */ 04063 if (chsize(fptr->fd, pos) < 0) 04064 rb_sys_fail_path(fptr->pathv); 04065 #endif 04066 return INT2FIX(0); 04067 } 04068 #else 04069 #define rb_file_truncate rb_f_notimplement 04070 #endif 04071 04072 # ifndef LOCK_SH 04073 # define LOCK_SH 1 04074 # endif 04075 # ifndef LOCK_EX 04076 # define LOCK_EX 2 04077 # endif 04078 # ifndef LOCK_NB 04079 # define LOCK_NB 4 04080 # endif 04081 # ifndef LOCK_UN 04082 # define LOCK_UN 8 04083 # endif 04084 04085 #ifdef __CYGWIN__ 04086 #include <winerror.h> 04087 extern unsigned long __attribute__((stdcall)) GetLastError(void); 04088 #endif 04089 04090 static VALUE 04091 rb_thread_flock(void *data) 04092 { 04093 #ifdef __CYGWIN__ 04094 int old_errno = errno; 04095 #endif 04096 int *op = data, ret = flock(op[0], op[1]); 04097 04098 #ifdef __CYGWIN__ 04099 if (GetLastError() == ERROR_NOT_LOCKED) { 04100 ret = 0; 04101 errno = old_errno; 04102 } 04103 #endif 04104 return (VALUE)ret; 04105 } 04106 04107 /* 04108 * call-seq: 04109 * file.flock (locking_constant )-> 0 or false 04110 * 04111 * Locks or unlocks a file according to <i>locking_constant</i> (a 04112 * logical <em>or</em> of the values in the table below). 04113 * Returns <code>false</code> if <code>File::LOCK_NB</code> is 04114 * specified and the operation would otherwise have blocked. Not 04115 * available on all platforms. 04116 * 04117 * Locking constants (in class File): 04118 * 04119 * LOCK_EX | Exclusive lock. Only one process may hold an 04120 * | exclusive lock for a given file at a time. 04121 * ----------+------------------------------------------------ 04122 * LOCK_NB | Don't block when locking. May be combined 04123 * | with other lock options using logical or. 04124 * ----------+------------------------------------------------ 04125 * LOCK_SH | Shared lock. Multiple processes may each hold a 04126 * | shared lock for a given file at the same time. 04127 * ----------+------------------------------------------------ 04128 * LOCK_UN | Unlock. 04129 * 04130 * Example: 04131 * 04132 * # update a counter using write lock 04133 * # don't use "w" because it truncates the file before lock. 04134 * File.open("counter", File::RDWR|File::CREAT, 0644) {|f| 04135 * f.flock(File::LOCK_EX) 04136 * value = f.read.to_i + 1 04137 * f.rewind 04138 * f.write("#{value}\n") 04139 * f.flush 04140 * f.truncate(f.pos) 04141 * } 04142 * 04143 * # read the counter using read lock 04144 * File.open("counter", "r") {|f| 04145 * f.flock(File::LOCK_SH) 04146 * p f.read 04147 * } 04148 * 04149 */ 04150 04151 static VALUE 04152 rb_file_flock(VALUE obj, VALUE operation) 04153 { 04154 rb_io_t *fptr; 04155 int op[2], op1; 04156 04157 rb_secure(2); 04158 op[1] = op1 = NUM2INT(operation); 04159 GetOpenFile(obj, fptr); 04160 op[0] = fptr->fd; 04161 04162 if (fptr->mode & FMODE_WRITABLE) { 04163 rb_io_flush(obj); 04164 } 04165 while ((int)rb_thread_io_blocking_region(rb_thread_flock, op, fptr->fd) < 0) { 04166 switch (errno) { 04167 case EAGAIN: 04168 case EACCES: 04169 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN 04170 case EWOULDBLOCK: 04171 #endif 04172 if (op1 & LOCK_NB) return Qfalse; 04173 rb_thread_polling(); 04174 rb_io_check_closed(fptr); 04175 continue; 04176 04177 case EINTR: 04178 #if defined(ERESTART) 04179 case ERESTART: 04180 #endif 04181 break; 04182 04183 default: 04184 rb_sys_fail_path(fptr->pathv); 04185 } 04186 } 04187 return INT2FIX(0); 04188 } 04189 #undef flock 04190 04191 static void 04192 test_check(int n, int argc, VALUE *argv) 04193 { 04194 int i; 04195 04196 rb_secure(2); 04197 n+=1; 04198 if (n != argc) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, n); 04199 for (i=1; i<n; i++) { 04200 switch (TYPE(argv[i])) { 04201 case T_STRING: 04202 default: 04203 FilePathValue(argv[i]); 04204 break; 04205 case T_FILE: 04206 break; 04207 } 04208 } 04209 } 04210 04211 #define CHECK(n) test_check((n), argc, argv) 04212 04213 /* 04214 * call-seq: 04215 * test(int_cmd, file1 [, file2] ) -> obj 04216 * 04217 * Uses the integer <i>aCmd</i> to perform various tests on 04218 * <i>file1</i> (first table below) or on <i>file1</i> and 04219 * <i>file2</i> (second table). 04220 * 04221 * File tests on a single file: 04222 * 04223 * Test Returns Meaning 04224 * "A" | Time | Last access time for file1 04225 * "b" | boolean | True if file1 is a block device 04226 * "c" | boolean | True if file1 is a character device 04227 * "C" | Time | Last change time for file1 04228 * "d" | boolean | True if file1 exists and is a directory 04229 * "e" | boolean | True if file1 exists 04230 * "f" | boolean | True if file1 exists and is a regular file 04231 * "g" | boolean | True if file1 has the \CF{setgid} bit 04232 * | | set (false under NT) 04233 * "G" | boolean | True if file1 exists and has a group 04234 * | | ownership equal to the caller's group 04235 * "k" | boolean | True if file1 exists and has the sticky bit set 04236 * "l" | boolean | True if file1 exists and is a symbolic link 04237 * "M" | Time | Last modification time for file1 04238 * "o" | boolean | True if file1 exists and is owned by 04239 * | | the caller's effective uid 04240 * "O" | boolean | True if file1 exists and is owned by 04241 * | | the caller's real uid 04242 * "p" | boolean | True if file1 exists and is a fifo 04243 * "r" | boolean | True if file1 is readable by the effective 04244 * | | uid/gid of the caller 04245 * "R" | boolean | True if file is readable by the real 04246 * | | uid/gid of the caller 04247 * "s" | int/nil | If file1 has nonzero size, return the size, 04248 * | | otherwise return nil 04249 * "S" | boolean | True if file1 exists and is a socket 04250 * "u" | boolean | True if file1 has the setuid bit set 04251 * "w" | boolean | True if file1 exists and is writable by 04252 * | | the effective uid/gid 04253 * "W" | boolean | True if file1 exists and is writable by 04254 * | | the real uid/gid 04255 * "x" | boolean | True if file1 exists and is executable by 04256 * | | the effective uid/gid 04257 * "X" | boolean | True if file1 exists and is executable by 04258 * | | the real uid/gid 04259 * "z" | boolean | True if file1 exists and has a zero length 04260 * 04261 * Tests that take two files: 04262 * 04263 * "-" | boolean | True if file1 and file2 are identical 04264 * "=" | boolean | True if the modification times of file1 04265 * | | and file2 are equal 04266 * "<" | boolean | True if the modification time of file1 04267 * | | is prior to that of file2 04268 * ">" | boolean | True if the modification time of file1 04269 * | | is after that of file2 04270 */ 04271 04272 static VALUE 04273 rb_f_test(int argc, VALUE *argv) 04274 { 04275 int cmd; 04276 04277 if (argc == 0) rb_raise(rb_eArgError, "wrong number of arguments (0 for 2..3)"); 04278 cmd = NUM2CHR(argv[0]); 04279 if (cmd == 0) goto unknown; 04280 if (strchr("bcdefgGkloOprRsSuwWxXz", cmd)) { 04281 CHECK(1); 04282 switch (cmd) { 04283 case 'b': 04284 return rb_file_blockdev_p(0, argv[1]); 04285 04286 case 'c': 04287 return rb_file_chardev_p(0, argv[1]); 04288 04289 case 'd': 04290 return rb_file_directory_p(0, argv[1]); 04291 04292 case 'a': 04293 case 'e': 04294 return rb_file_exist_p(0, argv[1]); 04295 04296 case 'f': 04297 return rb_file_file_p(0, argv[1]); 04298 04299 case 'g': 04300 return rb_file_sgid_p(0, argv[1]); 04301 04302 case 'G': 04303 return rb_file_grpowned_p(0, argv[1]); 04304 04305 case 'k': 04306 return rb_file_sticky_p(0, argv[1]); 04307 04308 case 'l': 04309 return rb_file_symlink_p(0, argv[1]); 04310 04311 case 'o': 04312 return rb_file_owned_p(0, argv[1]); 04313 04314 case 'O': 04315 return rb_file_rowned_p(0, argv[1]); 04316 04317 case 'p': 04318 return rb_file_pipe_p(0, argv[1]); 04319 04320 case 'r': 04321 return rb_file_readable_p(0, argv[1]); 04322 04323 case 'R': 04324 return rb_file_readable_real_p(0, argv[1]); 04325 04326 case 's': 04327 return rb_file_size_p(0, argv[1]); 04328 04329 case 'S': 04330 return rb_file_socket_p(0, argv[1]); 04331 04332 case 'u': 04333 return rb_file_suid_p(0, argv[1]); 04334 04335 case 'w': 04336 return rb_file_writable_p(0, argv[1]); 04337 04338 case 'W': 04339 return rb_file_writable_real_p(0, argv[1]); 04340 04341 case 'x': 04342 return rb_file_executable_p(0, argv[1]); 04343 04344 case 'X': 04345 return rb_file_executable_real_p(0, argv[1]); 04346 04347 case 'z': 04348 return rb_file_zero_p(0, argv[1]); 04349 } 04350 } 04351 04352 if (strchr("MAC", cmd)) { 04353 struct stat st; 04354 VALUE fname = argv[1]; 04355 04356 CHECK(1); 04357 if (rb_stat(fname, &st) == -1) { 04358 FilePathValue(fname); 04359 rb_sys_fail_path(fname); 04360 } 04361 04362 switch (cmd) { 04363 case 'A': 04364 return stat_atime(&st); 04365 case 'M': 04366 return stat_mtime(&st); 04367 case 'C': 04368 return stat_ctime(&st); 04369 } 04370 } 04371 04372 if (cmd == '-') { 04373 CHECK(2); 04374 return rb_file_identical_p(0, argv[1], argv[2]); 04375 } 04376 04377 if (strchr("=<>", cmd)) { 04378 struct stat st1, st2; 04379 04380 CHECK(2); 04381 if (rb_stat(argv[1], &st1) < 0) return Qfalse; 04382 if (rb_stat(argv[2], &st2) < 0) return Qfalse; 04383 04384 switch (cmd) { 04385 case '=': 04386 if (st1.st_mtime == st2.st_mtime) return Qtrue; 04387 return Qfalse; 04388 04389 case '>': 04390 if (st1.st_mtime > st2.st_mtime) return Qtrue; 04391 return Qfalse; 04392 04393 case '<': 04394 if (st1.st_mtime < st2.st_mtime) return Qtrue; 04395 return Qfalse; 04396 } 04397 } 04398 unknown: 04399 /* unknown command */ 04400 if (ISPRINT(cmd)) { 04401 rb_raise(rb_eArgError, "unknown command '%s%c'", cmd == '\'' || cmd == '\\' ? "\\" : "", cmd); 04402 } 04403 else { 04404 rb_raise(rb_eArgError, "unknown command \"\\x%02X\"", cmd); 04405 } 04406 return Qnil; /* not reached */ 04407 } 04408 04409 04410 /* 04411 * Document-class: File::Stat 04412 * 04413 * Objects of class <code>File::Stat</code> encapsulate common status 04414 * information for <code>File</code> objects. The information is 04415 * recorded at the moment the <code>File::Stat</code> object is 04416 * created; changes made to the file after that point will not be 04417 * reflected. <code>File::Stat</code> objects are returned by 04418 * <code>IO#stat</code>, <code>File::stat</code>, 04419 * <code>File#lstat</code>, and <code>File::lstat</code>. Many of these 04420 * methods return platform-specific values, and not all values are 04421 * meaningful on all systems. See also <code>Kernel#test</code>. 04422 */ 04423 04424 static VALUE 04425 rb_stat_s_alloc(VALUE klass) 04426 { 04427 return stat_new_0(klass, 0); 04428 } 04429 04430 /* 04431 * call-seq: 04432 * 04433 * File::Stat.new(file_name) -> stat 04434 * 04435 * Create a File::Stat object for the given file name (raising an 04436 * exception if the file doesn't exist). 04437 */ 04438 04439 static VALUE 04440 rb_stat_init(VALUE obj, VALUE fname) 04441 { 04442 struct stat st, *nst; 04443 04444 rb_secure(2); 04445 FilePathValue(fname); 04446 fname = rb_str_encode_ospath(fname); 04447 if (STAT(StringValueCStr(fname), &st) == -1) { 04448 rb_sys_fail_path(fname); 04449 } 04450 if (DATA_PTR(obj)) { 04451 xfree(DATA_PTR(obj)); 04452 DATA_PTR(obj) = NULL; 04453 } 04454 nst = ALLOC(struct stat); 04455 *nst = st; 04456 DATA_PTR(obj) = nst; 04457 04458 return Qnil; 04459 } 04460 04461 /* :nodoc: */ 04462 static VALUE 04463 rb_stat_init_copy(VALUE copy, VALUE orig) 04464 { 04465 struct stat *nst; 04466 04467 if (copy == orig) return orig; 04468 rb_check_frozen(copy); 04469 /* need better argument type check */ 04470 if (!rb_obj_is_instance_of(orig, rb_obj_class(copy))) { 04471 rb_raise(rb_eTypeError, "wrong argument class"); 04472 } 04473 if (DATA_PTR(copy)) { 04474 xfree(DATA_PTR(copy)); 04475 DATA_PTR(copy) = 0; 04476 } 04477 if (DATA_PTR(orig)) { 04478 nst = ALLOC(struct stat); 04479 *nst = *(struct stat*)DATA_PTR(orig); 04480 DATA_PTR(copy) = nst; 04481 } 04482 04483 return copy; 04484 } 04485 04486 /* 04487 * call-seq: 04488 * stat.ftype -> string 04489 * 04490 * Identifies the type of <i>stat</i>. The return string is one of: 04491 * ``<code>file</code>'', ``<code>directory</code>'', 04492 * ``<code>characterSpecial</code>'', ``<code>blockSpecial</code>'', 04493 * ``<code>fifo</code>'', ``<code>link</code>'', 04494 * ``<code>socket</code>'', or ``<code>unknown</code>''. 04495 * 04496 * File.stat("/dev/tty").ftype #=> "characterSpecial" 04497 * 04498 */ 04499 04500 static VALUE 04501 rb_stat_ftype(VALUE obj) 04502 { 04503 return rb_file_ftype(get_stat(obj)); 04504 } 04505 04506 /* 04507 * call-seq: 04508 * stat.directory? -> true or false 04509 * 04510 * Returns <code>true</code> if <i>stat</i> is a directory, 04511 * <code>false</code> otherwise. 04512 * 04513 * File.stat("testfile").directory? #=> false 04514 * File.stat(".").directory? #=> true 04515 */ 04516 04517 static VALUE 04518 rb_stat_d(VALUE obj) 04519 { 04520 if (S_ISDIR(get_stat(obj)->st_mode)) return Qtrue; 04521 return Qfalse; 04522 } 04523 04524 /* 04525 * call-seq: 04526 * stat.pipe? -> true or false 04527 * 04528 * Returns <code>true</code> if the operating system supports pipes and 04529 * <i>stat</i> is a pipe; <code>false</code> otherwise. 04530 */ 04531 04532 static VALUE 04533 rb_stat_p(VALUE obj) 04534 { 04535 #ifdef S_IFIFO 04536 if (S_ISFIFO(get_stat(obj)->st_mode)) return Qtrue; 04537 04538 #endif 04539 return Qfalse; 04540 } 04541 04542 /* 04543 * call-seq: 04544 * stat.symlink? -> true or false 04545 * 04546 * Returns <code>true</code> if <i>stat</i> is a symbolic link, 04547 * <code>false</code> if it isn't or if the operating system doesn't 04548 * support this feature. As <code>File::stat</code> automatically 04549 * follows symbolic links, <code>symlink?</code> will always be 04550 * <code>false</code> for an object returned by 04551 * <code>File::stat</code>. 04552 * 04553 * File.symlink("testfile", "alink") #=> 0 04554 * File.stat("alink").symlink? #=> false 04555 * File.lstat("alink").symlink? #=> true 04556 * 04557 */ 04558 04559 static VALUE 04560 rb_stat_l(VALUE obj) 04561 { 04562 #ifdef S_ISLNK 04563 if (S_ISLNK(get_stat(obj)->st_mode)) return Qtrue; 04564 #endif 04565 return Qfalse; 04566 } 04567 04568 /* 04569 * call-seq: 04570 * stat.socket? -> true or false 04571 * 04572 * Returns <code>true</code> if <i>stat</i> is a socket, 04573 * <code>false</code> if it isn't or if the operating system doesn't 04574 * support this feature. 04575 * 04576 * File.stat("testfile").socket? #=> false 04577 * 04578 */ 04579 04580 static VALUE 04581 rb_stat_S(VALUE obj) 04582 { 04583 #ifdef S_ISSOCK 04584 if (S_ISSOCK(get_stat(obj)->st_mode)) return Qtrue; 04585 04586 #endif 04587 return Qfalse; 04588 } 04589 04590 /* 04591 * call-seq: 04592 * stat.blockdev? -> true or false 04593 * 04594 * Returns <code>true</code> if the file is a block device, 04595 * <code>false</code> if it isn't or if the operating system doesn't 04596 * support this feature. 04597 * 04598 * File.stat("testfile").blockdev? #=> false 04599 * File.stat("/dev/hda1").blockdev? #=> true 04600 * 04601 */ 04602 04603 static VALUE 04604 rb_stat_b(VALUE obj) 04605 { 04606 #ifdef S_ISBLK 04607 if (S_ISBLK(get_stat(obj)->st_mode)) return Qtrue; 04608 04609 #endif 04610 return Qfalse; 04611 } 04612 04613 /* 04614 * call-seq: 04615 * stat.chardev? -> true or false 04616 * 04617 * Returns <code>true</code> if the file is a character device, 04618 * <code>false</code> if it isn't or if the operating system doesn't 04619 * support this feature. 04620 * 04621 * File.stat("/dev/tty").chardev? #=> true 04622 * 04623 */ 04624 04625 static VALUE 04626 rb_stat_c(VALUE obj) 04627 { 04628 if (S_ISCHR(get_stat(obj)->st_mode)) return Qtrue; 04629 04630 return Qfalse; 04631 } 04632 04633 /* 04634 * call-seq: 04635 * stat.owned? -> true or false 04636 * 04637 * Returns <code>true</code> if the effective user id of the process is 04638 * the same as the owner of <i>stat</i>. 04639 * 04640 * File.stat("testfile").owned? #=> true 04641 * File.stat("/etc/passwd").owned? #=> false 04642 * 04643 */ 04644 04645 static VALUE 04646 rb_stat_owned(VALUE obj) 04647 { 04648 if (get_stat(obj)->st_uid == geteuid()) return Qtrue; 04649 return Qfalse; 04650 } 04651 04652 static VALUE 04653 rb_stat_rowned(VALUE obj) 04654 { 04655 if (get_stat(obj)->st_uid == getuid()) return Qtrue; 04656 return Qfalse; 04657 } 04658 04659 /* 04660 * call-seq: 04661 * stat.grpowned? -> true or false 04662 * 04663 * Returns true if the effective group id of the process is the same as 04664 * the group id of <i>stat</i>. On Windows NT, returns <code>false</code>. 04665 * 04666 * File.stat("testfile").grpowned? #=> true 04667 * File.stat("/etc/passwd").grpowned? #=> false 04668 * 04669 */ 04670 04671 static VALUE 04672 rb_stat_grpowned(VALUE obj) 04673 { 04674 #ifndef _WIN32 04675 if (rb_group_member(get_stat(obj)->st_gid)) return Qtrue; 04676 #endif 04677 return Qfalse; 04678 } 04679 04680 /* 04681 * call-seq: 04682 * stat.readable? -> true or false 04683 * 04684 * Returns <code>true</code> if <i>stat</i> is readable by the 04685 * effective user id of this process. 04686 * 04687 * File.stat("testfile").readable? #=> true 04688 * 04689 */ 04690 04691 static VALUE 04692 rb_stat_r(VALUE obj) 04693 { 04694 struct stat *st = get_stat(obj); 04695 04696 #ifdef USE_GETEUID 04697 if (geteuid() == 0) return Qtrue; 04698 #endif 04699 #ifdef S_IRUSR 04700 if (rb_stat_owned(obj)) 04701 return st->st_mode & S_IRUSR ? Qtrue : Qfalse; 04702 #endif 04703 #ifdef S_IRGRP 04704 if (rb_stat_grpowned(obj)) 04705 return st->st_mode & S_IRGRP ? Qtrue : Qfalse; 04706 #endif 04707 #ifdef S_IROTH 04708 if (!(st->st_mode & S_IROTH)) return Qfalse; 04709 #endif 04710 return Qtrue; 04711 } 04712 04713 /* 04714 * call-seq: 04715 * stat.readable_real? -> true or false 04716 * 04717 * Returns <code>true</code> if <i>stat</i> is readable by the real 04718 * user id of this process. 04719 * 04720 * File.stat("testfile").readable_real? #=> true 04721 * 04722 */ 04723 04724 static VALUE 04725 rb_stat_R(VALUE obj) 04726 { 04727 struct stat *st = get_stat(obj); 04728 04729 #ifdef USE_GETEUID 04730 if (getuid() == 0) return Qtrue; 04731 #endif 04732 #ifdef S_IRUSR 04733 if (rb_stat_rowned(obj)) 04734 return st->st_mode & S_IRUSR ? Qtrue : Qfalse; 04735 #endif 04736 #ifdef S_IRGRP 04737 if (rb_group_member(get_stat(obj)->st_gid)) 04738 return st->st_mode & S_IRGRP ? Qtrue : Qfalse; 04739 #endif 04740 #ifdef S_IROTH 04741 if (!(st->st_mode & S_IROTH)) return Qfalse; 04742 #endif 04743 return Qtrue; 04744 } 04745 04746 /* 04747 * call-seq: 04748 * stat.world_readable? -> fixnum or nil 04749 * 04750 * If <i>stat</i> is readable by others, returns an integer 04751 * representing the file permission bits of <i>stat</i>. Returns 04752 * <code>nil</code> otherwise. The meaning of the bits is platform 04753 * dependent; on Unix systems, see <code>stat(2)</code>. 04754 * 04755 * m = File.stat("/etc/passwd").world_readable? #=> 420 04756 * sprintf("%o", m) #=> "644" 04757 */ 04758 04759 static VALUE 04760 rb_stat_wr(VALUE obj) 04761 { 04762 #ifdef S_IROTH 04763 if ((get_stat(obj)->st_mode & (S_IROTH)) == S_IROTH) { 04764 return UINT2NUM(get_stat(obj)->st_mode & (S_IRUGO|S_IWUGO|S_IXUGO)); 04765 } 04766 else { 04767 return Qnil; 04768 } 04769 #endif 04770 } 04771 04772 /* 04773 * call-seq: 04774 * stat.writable? -> true or false 04775 * 04776 * Returns <code>true</code> if <i>stat</i> is writable by the 04777 * effective user id of this process. 04778 * 04779 * File.stat("testfile").writable? #=> true 04780 * 04781 */ 04782 04783 static VALUE 04784 rb_stat_w(VALUE obj) 04785 { 04786 struct stat *st = get_stat(obj); 04787 04788 #ifdef USE_GETEUID 04789 if (geteuid() == 0) return Qtrue; 04790 #endif 04791 #ifdef S_IWUSR 04792 if (rb_stat_owned(obj)) 04793 return st->st_mode & S_IWUSR ? Qtrue : Qfalse; 04794 #endif 04795 #ifdef S_IWGRP 04796 if (rb_stat_grpowned(obj)) 04797 return st->st_mode & S_IWGRP ? Qtrue : Qfalse; 04798 #endif 04799 #ifdef S_IWOTH 04800 if (!(st->st_mode & S_IWOTH)) return Qfalse; 04801 #endif 04802 return Qtrue; 04803 } 04804 04805 /* 04806 * call-seq: 04807 * stat.writable_real? -> true or false 04808 * 04809 * Returns <code>true</code> if <i>stat</i> is writable by the real 04810 * user id of this process. 04811 * 04812 * File.stat("testfile").writable_real? #=> true 04813 * 04814 */ 04815 04816 static VALUE 04817 rb_stat_W(VALUE obj) 04818 { 04819 struct stat *st = get_stat(obj); 04820 04821 #ifdef USE_GETEUID 04822 if (getuid() == 0) return Qtrue; 04823 #endif 04824 #ifdef S_IWUSR 04825 if (rb_stat_rowned(obj)) 04826 return st->st_mode & S_IWUSR ? Qtrue : Qfalse; 04827 #endif 04828 #ifdef S_IWGRP 04829 if (rb_group_member(get_stat(obj)->st_gid)) 04830 return st->st_mode & S_IWGRP ? Qtrue : Qfalse; 04831 #endif 04832 #ifdef S_IWOTH 04833 if (!(st->st_mode & S_IWOTH)) return Qfalse; 04834 #endif 04835 return Qtrue; 04836 } 04837 04838 /* 04839 * call-seq: 04840 * stat.world_writable? -> fixnum or nil 04841 * 04842 * If <i>stat</i> is writable by others, returns an integer 04843 * representing the file permission bits of <i>stat</i>. Returns 04844 * <code>nil</code> otherwise. The meaning of the bits is platform 04845 * dependent; on Unix systems, see <code>stat(2)</code>. 04846 * 04847 * m = File.stat("/tmp").world_writable? #=> 511 04848 * sprintf("%o", m) #=> "777" 04849 */ 04850 04851 static VALUE 04852 rb_stat_ww(VALUE obj) 04853 { 04854 #ifdef S_IROTH 04855 if ((get_stat(obj)->st_mode & (S_IWOTH)) == S_IWOTH) { 04856 return UINT2NUM(get_stat(obj)->st_mode & (S_IRUGO|S_IWUGO|S_IXUGO)); 04857 } 04858 else { 04859 return Qnil; 04860 } 04861 #endif 04862 } 04863 04864 /* 04865 * call-seq: 04866 * stat.executable? -> true or false 04867 * 04868 * Returns <code>true</code> if <i>stat</i> is executable or if the 04869 * operating system doesn't distinguish executable files from 04870 * nonexecutable files. The tests are made using the effective owner of 04871 * the process. 04872 * 04873 * File.stat("testfile").executable? #=> false 04874 * 04875 */ 04876 04877 static VALUE 04878 rb_stat_x(VALUE obj) 04879 { 04880 struct stat *st = get_stat(obj); 04881 04882 #ifdef USE_GETEUID 04883 if (geteuid() == 0) { 04884 return st->st_mode & S_IXUGO ? Qtrue : Qfalse; 04885 } 04886 #endif 04887 #ifdef S_IXUSR 04888 if (rb_stat_owned(obj)) 04889 return st->st_mode & S_IXUSR ? Qtrue : Qfalse; 04890 #endif 04891 #ifdef S_IXGRP 04892 if (rb_stat_grpowned(obj)) 04893 return st->st_mode & S_IXGRP ? Qtrue : Qfalse; 04894 #endif 04895 #ifdef S_IXOTH 04896 if (!(st->st_mode & S_IXOTH)) return Qfalse; 04897 #endif 04898 return Qtrue; 04899 } 04900 04901 /* 04902 * call-seq: 04903 * stat.executable_real? -> true or false 04904 * 04905 * Same as <code>executable?</code>, but tests using the real owner of 04906 * the process. 04907 */ 04908 04909 static VALUE 04910 rb_stat_X(VALUE obj) 04911 { 04912 struct stat *st = get_stat(obj); 04913 04914 #ifdef USE_GETEUID 04915 if (getuid() == 0) { 04916 return st->st_mode & S_IXUGO ? Qtrue : Qfalse; 04917 } 04918 #endif 04919 #ifdef S_IXUSR 04920 if (rb_stat_rowned(obj)) 04921 return st->st_mode & S_IXUSR ? Qtrue : Qfalse; 04922 #endif 04923 #ifdef S_IXGRP 04924 if (rb_group_member(get_stat(obj)->st_gid)) 04925 return st->st_mode & S_IXGRP ? Qtrue : Qfalse; 04926 #endif 04927 #ifdef S_IXOTH 04928 if (!(st->st_mode & S_IXOTH)) return Qfalse; 04929 #endif 04930 return Qtrue; 04931 } 04932 04933 /* 04934 * call-seq: 04935 * stat.file? -> true or false 04936 * 04937 * Returns <code>true</code> if <i>stat</i> is a regular file (not 04938 * a device file, pipe, socket, etc.). 04939 * 04940 * File.stat("testfile").file? #=> true 04941 * 04942 */ 04943 04944 static VALUE 04945 rb_stat_f(VALUE obj) 04946 { 04947 if (S_ISREG(get_stat(obj)->st_mode)) return Qtrue; 04948 return Qfalse; 04949 } 04950 04951 /* 04952 * call-seq: 04953 * stat.zero? -> true or false 04954 * 04955 * Returns <code>true</code> if <i>stat</i> is a zero-length file; 04956 * <code>false</code> otherwise. 04957 * 04958 * File.stat("testfile").zero? #=> false 04959 * 04960 */ 04961 04962 static VALUE 04963 rb_stat_z(VALUE obj) 04964 { 04965 if (get_stat(obj)->st_size == 0) return Qtrue; 04966 return Qfalse; 04967 } 04968 04969 /* 04970 * call-seq: 04971 * state.size -> integer 04972 * 04973 * Returns the size of <i>stat</i> in bytes. 04974 * 04975 * File.stat("testfile").size #=> 66 04976 * 04977 */ 04978 04979 static VALUE 04980 rb_stat_s(VALUE obj) 04981 { 04982 off_t size = get_stat(obj)->st_size; 04983 04984 if (size == 0) return Qnil; 04985 return OFFT2NUM(size); 04986 } 04987 04988 /* 04989 * call-seq: 04990 * stat.setuid? -> true or false 04991 * 04992 * Returns <code>true</code> if <i>stat</i> has the set-user-id 04993 * permission bit set, <code>false</code> if it doesn't or if the 04994 * operating system doesn't support this feature. 04995 * 04996 * File.stat("/bin/su").setuid? #=> true 04997 */ 04998 04999 static VALUE 05000 rb_stat_suid(VALUE obj) 05001 { 05002 #ifdef S_ISUID 05003 if (get_stat(obj)->st_mode & S_ISUID) return Qtrue; 05004 #endif 05005 return Qfalse; 05006 } 05007 05008 /* 05009 * call-seq: 05010 * stat.setgid? -> true or false 05011 * 05012 * Returns <code>true</code> if <i>stat</i> has the set-group-id 05013 * permission bit set, <code>false</code> if it doesn't or if the 05014 * operating system doesn't support this feature. 05015 * 05016 * File.stat("/usr/sbin/lpc").setgid? #=> true 05017 * 05018 */ 05019 05020 static VALUE 05021 rb_stat_sgid(VALUE obj) 05022 { 05023 #ifdef S_ISGID 05024 if (get_stat(obj)->st_mode & S_ISGID) return Qtrue; 05025 #endif 05026 return Qfalse; 05027 } 05028 05029 /* 05030 * call-seq: 05031 * stat.sticky? -> true or false 05032 * 05033 * Returns <code>true</code> if <i>stat</i> has its sticky bit set, 05034 * <code>false</code> if it doesn't or if the operating system doesn't 05035 * support this feature. 05036 * 05037 * File.stat("testfile").sticky? #=> false 05038 * 05039 */ 05040 05041 static VALUE 05042 rb_stat_sticky(VALUE obj) 05043 { 05044 #ifdef S_ISVTX 05045 if (get_stat(obj)->st_mode & S_ISVTX) return Qtrue; 05046 #endif 05047 return Qfalse; 05048 } 05049 05050 VALUE rb_mFConst; 05051 05052 void 05053 rb_file_const(const char *name, VALUE value) 05054 { 05055 rb_define_const(rb_mFConst, name, value); 05056 } 05057 05058 int 05059 rb_is_absolute_path(const char *path) 05060 { 05061 #ifdef DOSISH_DRIVE_LETTER 05062 if (has_drive_letter(path) && isdirsep(path[2])) return 1; 05063 #endif 05064 #ifdef DOSISH_UNC 05065 if (isdirsep(path[0]) && isdirsep(path[1])) return 1; 05066 #endif 05067 #ifndef DOSISH 05068 if (path[0] == '/') return 1; 05069 #endif 05070 return 0; 05071 } 05072 05073 #ifndef ENABLE_PATH_CHECK 05074 # if defined DOSISH || defined __CYGWIN__ 05075 # define ENABLE_PATH_CHECK 0 05076 # else 05077 # define ENABLE_PATH_CHECK 1 05078 # endif 05079 #endif 05080 05081 #if ENABLE_PATH_CHECK 05082 static int 05083 path_check_0(VALUE path, int execpath) 05084 { 05085 struct stat st; 05086 const char *p0 = StringValueCStr(path); 05087 const char *e0; 05088 rb_encoding *enc; 05089 char *p = 0, *s; 05090 05091 if (!rb_is_absolute_path(p0)) { 05092 char *buf = my_getcwd(); 05093 VALUE newpath; 05094 05095 newpath = rb_str_new2(buf); 05096 xfree(buf); 05097 05098 rb_str_cat2(newpath, "/"); 05099 rb_str_cat2(newpath, p0); 05100 path = newpath; 05101 p0 = RSTRING_PTR(path); 05102 } 05103 e0 = p0 + RSTRING_LEN(path); 05104 enc = rb_enc_get(path); 05105 for (;;) { 05106 #ifndef S_IWOTH 05107 # define S_IWOTH 002 05108 #endif 05109 if (STAT(p0, &st) == 0 && S_ISDIR(st.st_mode) && (st.st_mode & S_IWOTH) 05110 #ifdef S_ISVTX 05111 && !(p && execpath && (st.st_mode & S_ISVTX)) 05112 #endif 05113 && !access(p0, W_OK)) { 05114 rb_warn("Insecure world writable dir %s in %sPATH, mode 0%" 05115 PRI_MODET_PREFIX"o", 05116 p0, (execpath ? "" : "LOAD_"), st.st_mode); 05117 if (p) *p = '/'; 05118 RB_GC_GUARD(path); 05119 return 0; 05120 } 05121 s = strrdirsep(p0, e0, enc); 05122 if (p) *p = '/'; 05123 if (!s || s == p0) return 1; 05124 p = s; 05125 e0 = p; 05126 *p = '\0'; 05127 } 05128 } 05129 #endif 05130 05131 #if ENABLE_PATH_CHECK 05132 #define fpath_check(path) path_check_0((path), FALSE) 05133 #else 05134 #define fpath_check(path) 1 05135 #endif 05136 05137 int 05138 rb_path_check(const char *path) 05139 { 05140 #if ENABLE_PATH_CHECK 05141 const char *p0, *p, *pend; 05142 const char sep = PATH_SEP_CHAR; 05143 05144 if (!path) return 1; 05145 05146 pend = path + strlen(path); 05147 p0 = path; 05148 p = strchr(path, sep); 05149 if (!p) p = pend; 05150 05151 for (;;) { 05152 if (!path_check_0(rb_str_new(p0, p - p0), TRUE)) { 05153 return 0; /* not safe */ 05154 } 05155 p0 = p + 1; 05156 if (p0 > pend) break; 05157 p = strchr(p0, sep); 05158 if (!p) p = pend; 05159 } 05160 #endif 05161 return 1; 05162 } 05163 05164 #ifndef _WIN32 05165 int 05166 rb_file_load_ok(const char *path) 05167 { 05168 int ret = 1; 05169 int fd = open(path, O_RDONLY); 05170 if (fd == -1) return 0; 05171 rb_update_max_fd(fd); 05172 #if !defined DOSISH 05173 { 05174 struct stat st; 05175 if (fstat(fd, &st) || !S_ISREG(st.st_mode)) { 05176 ret = 0; 05177 } 05178 } 05179 #endif 05180 (void)close(fd); 05181 return ret; 05182 } 05183 #endif 05184 05185 static int 05186 is_explicit_relative(const char *path) 05187 { 05188 if (*path++ != '.') return 0; 05189 if (*path == '.') path++; 05190 return isdirsep(*path); 05191 } 05192 05193 static VALUE 05194 copy_path_class(VALUE path, VALUE orig) 05195 { 05196 RBASIC(path)->klass = rb_obj_class(orig); 05197 OBJ_FREEZE(path); 05198 return path; 05199 } 05200 05201 int 05202 rb_find_file_ext(VALUE *filep, const char *const *ext) 05203 { 05204 return rb_find_file_ext_safe(filep, ext, rb_safe_level()); 05205 } 05206 05207 int 05208 rb_find_file_ext_safe(VALUE *filep, const char *const *ext, int safe_level) 05209 { 05210 const char *f = StringValueCStr(*filep); 05211 VALUE fname = *filep, load_path, tmp; 05212 long i, j, fnlen; 05213 int expanded = 0; 05214 05215 if (!ext[0]) return 0; 05216 05217 if (f[0] == '~') { 05218 fname = file_expand_path_1(fname); 05219 if (safe_level >= 1 && OBJ_TAINTED(fname)) { 05220 rb_raise(rb_eSecurityError, "loading from unsafe file %s", f); 05221 } 05222 f = RSTRING_PTR(fname); 05223 *filep = fname; 05224 expanded = 1; 05225 } 05226 05227 if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) { 05228 if (safe_level >= 1 && !fpath_check(fname)) { 05229 rb_raise(rb_eSecurityError, "loading from unsafe path %s", f); 05230 } 05231 if (!expanded) fname = file_expand_path_1(fname); 05232 fnlen = RSTRING_LEN(fname); 05233 for (i=0; ext[i]; i++) { 05234 rb_str_cat2(fname, ext[i]); 05235 if (rb_file_load_ok(RSTRING_PTR(fname))) { 05236 *filep = copy_path_class(fname, *filep); 05237 return (int)(i+1); 05238 } 05239 rb_str_set_len(fname, fnlen); 05240 } 05241 return 0; 05242 } 05243 05244 if (safe_level >= 4) { 05245 rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f); 05246 } 05247 05248 RB_GC_GUARD(load_path) = rb_get_load_path(); 05249 if (!load_path) return 0; 05250 05251 fname = rb_str_dup(*filep); 05252 RBASIC(fname)->klass = 0; 05253 fnlen = RSTRING_LEN(fname); 05254 tmp = rb_str_tmp_new(MAXPATHLEN + 2); 05255 rb_enc_associate_index(tmp, rb_usascii_encindex()); 05256 for (j=0; ext[j]; j++) { 05257 rb_str_cat2(fname, ext[j]); 05258 for (i = 0; i < RARRAY_LEN(load_path); i++) { 05259 VALUE str = RARRAY_PTR(load_path)[i]; 05260 05261 RB_GC_GUARD(str) = rb_get_path_check(str, safe_level); 05262 if (RSTRING_LEN(str) == 0) continue; 05263 rb_file_expand_path_internal(fname, str, 0, 0, tmp); 05264 if (rb_file_load_ok(RSTRING_PTR(tmp))) { 05265 *filep = copy_path_class(tmp, *filep); 05266 return (int)(j+1); 05267 } 05268 FL_UNSET(tmp, FL_TAINT | FL_UNTRUSTED); 05269 } 05270 rb_str_set_len(fname, fnlen); 05271 } 05272 RB_GC_GUARD(load_path); 05273 return 0; 05274 } 05275 05276 VALUE 05277 rb_find_file(VALUE path) 05278 { 05279 return rb_find_file_safe(path, rb_safe_level()); 05280 } 05281 05282 VALUE 05283 rb_find_file_safe(VALUE path, int safe_level) 05284 { 05285 VALUE tmp, load_path; 05286 const char *f = StringValueCStr(path); 05287 int expanded = 0; 05288 05289 if (f[0] == '~') { 05290 tmp = file_expand_path_1(path); 05291 if (safe_level >= 1 && OBJ_TAINTED(tmp)) { 05292 rb_raise(rb_eSecurityError, "loading from unsafe file %s", f); 05293 } 05294 path = copy_path_class(tmp, path); 05295 f = RSTRING_PTR(path); 05296 expanded = 1; 05297 } 05298 05299 if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) { 05300 if (safe_level >= 1 && !fpath_check(path)) { 05301 rb_raise(rb_eSecurityError, "loading from unsafe path %s", f); 05302 } 05303 if (!rb_file_load_ok(f)) return 0; 05304 if (!expanded) 05305 path = copy_path_class(file_expand_path_1(path), path); 05306 return path; 05307 } 05308 05309 if (safe_level >= 4) { 05310 rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f); 05311 } 05312 05313 RB_GC_GUARD(load_path) = rb_get_load_path(); 05314 if (load_path) { 05315 long i; 05316 05317 tmp = rb_str_tmp_new(MAXPATHLEN + 2); 05318 rb_enc_associate_index(tmp, rb_usascii_encindex()); 05319 for (i = 0; i < RARRAY_LEN(load_path); i++) { 05320 VALUE str = RARRAY_PTR(load_path)[i]; 05321 RB_GC_GUARD(str) = rb_get_path_check(str, safe_level); 05322 if (RSTRING_LEN(str) > 0) { 05323 rb_file_expand_path_internal(path, str, 0, 0, tmp); 05324 f = RSTRING_PTR(tmp); 05325 if (rb_file_load_ok(f)) goto found; 05326 } 05327 } 05328 return 0; 05329 } 05330 else { 05331 return 0; /* no path, no load */ 05332 } 05333 05334 found: 05335 if (safe_level >= 1 && !fpath_check(tmp)) { 05336 rb_raise(rb_eSecurityError, "loading from unsafe file %s", f); 05337 } 05338 05339 return copy_path_class(tmp, path); 05340 } 05341 05342 static void 05343 define_filetest_function(const char *name, VALUE (*func)(ANYARGS), int argc) 05344 { 05345 rb_define_module_function(rb_mFileTest, name, func, argc); 05346 rb_define_singleton_method(rb_cFile, name, func, argc); 05347 } 05348 05349 static const char null_device[] = 05350 #if defined DOSISH 05351 "NUL" 05352 #elif defined AMIGA || defined __amigaos__ 05353 "NIL" 05354 #elif defined __VMS 05355 "NL:" 05356 #else 05357 "/dev/null" 05358 #endif 05359 ; 05360 05361 /* 05362 * A <code>File</code> is an abstraction of any file object accessible 05363 * by the program and is closely associated with class <code>IO</code> 05364 * <code>File</code> includes the methods of module 05365 * <code>FileTest</code> as class methods, allowing you to write (for 05366 * example) <code>File.exist?("foo")</code>. 05367 * 05368 * In the description of File methods, 05369 * <em>permission bits</em> are a platform-specific 05370 * set of bits that indicate permissions of a file. On Unix-based 05371 * systems, permissions are viewed as a set of three octets, for the 05372 * owner, the group, and the rest of the world. For each of these 05373 * entities, permissions may be set to read, write, or execute the 05374 * file: 05375 * 05376 * The permission bits <code>0644</code> (in octal) would thus be 05377 * interpreted as read/write for owner, and read-only for group and 05378 * other. Higher-order bits may also be used to indicate the type of 05379 * file (plain, directory, pipe, socket, and so on) and various other 05380 * special features. If the permissions are for a directory, the 05381 * meaning of the execute bit changes; when set the directory can be 05382 * searched. 05383 * 05384 * On non-Posix operating systems, there may be only the ability to 05385 * make a file read-only or read-write. In this case, the remaining 05386 * permission bits will be synthesized to resemble typical values. For 05387 * instance, on Windows NT the default permission bits are 05388 * <code>0644</code>, which means read/write for owner, read-only for 05389 * all others. The only change that can be made is to make the file 05390 * read-only, which is reported as <code>0444</code>. 05391 */ 05392 05393 void 05394 Init_File(void) 05395 { 05396 rb_mFileTest = rb_define_module("FileTest"); 05397 rb_cFile = rb_define_class("File", rb_cIO); 05398 05399 define_filetest_function("directory?", rb_file_directory_p, 1); 05400 define_filetest_function("exist?", rb_file_exist_p, 1); 05401 define_filetest_function("exists?", rb_file_exist_p, 1); 05402 define_filetest_function("readable?", rb_file_readable_p, 1); 05403 define_filetest_function("readable_real?", rb_file_readable_real_p, 1); 05404 define_filetest_function("world_readable?", rb_file_world_readable_p, 1); 05405 define_filetest_function("writable?", rb_file_writable_p, 1); 05406 define_filetest_function("writable_real?", rb_file_writable_real_p, 1); 05407 define_filetest_function("world_writable?", rb_file_world_writable_p, 1); 05408 define_filetest_function("executable?", rb_file_executable_p, 1); 05409 define_filetest_function("executable_real?", rb_file_executable_real_p, 1); 05410 define_filetest_function("file?", rb_file_file_p, 1); 05411 define_filetest_function("zero?", rb_file_zero_p, 1); 05412 define_filetest_function("size?", rb_file_size_p, 1); 05413 define_filetest_function("size", rb_file_s_size, 1); 05414 define_filetest_function("owned?", rb_file_owned_p, 1); 05415 define_filetest_function("grpowned?", rb_file_grpowned_p, 1); 05416 05417 define_filetest_function("pipe?", rb_file_pipe_p, 1); 05418 define_filetest_function("symlink?", rb_file_symlink_p, 1); 05419 define_filetest_function("socket?", rb_file_socket_p, 1); 05420 05421 define_filetest_function("blockdev?", rb_file_blockdev_p, 1); 05422 define_filetest_function("chardev?", rb_file_chardev_p, 1); 05423 05424 define_filetest_function("setuid?", rb_file_suid_p, 1); 05425 define_filetest_function("setgid?", rb_file_sgid_p, 1); 05426 define_filetest_function("sticky?", rb_file_sticky_p, 1); 05427 05428 define_filetest_function("identical?", rb_file_identical_p, 2); 05429 05430 rb_define_singleton_method(rb_cFile, "stat", rb_file_s_stat, 1); 05431 rb_define_singleton_method(rb_cFile, "lstat", rb_file_s_lstat, 1); 05432 rb_define_singleton_method(rb_cFile, "ftype", rb_file_s_ftype, 1); 05433 05434 rb_define_singleton_method(rb_cFile, "atime", rb_file_s_atime, 1); 05435 rb_define_singleton_method(rb_cFile, "mtime", rb_file_s_mtime, 1); 05436 rb_define_singleton_method(rb_cFile, "ctime", rb_file_s_ctime, 1); 05437 05438 rb_define_singleton_method(rb_cFile, "utime", rb_file_s_utime, -1); 05439 rb_define_singleton_method(rb_cFile, "chmod", rb_file_s_chmod, -1); 05440 rb_define_singleton_method(rb_cFile, "chown", rb_file_s_chown, -1); 05441 rb_define_singleton_method(rb_cFile, "lchmod", rb_file_s_lchmod, -1); 05442 rb_define_singleton_method(rb_cFile, "lchown", rb_file_s_lchown, -1); 05443 05444 rb_define_singleton_method(rb_cFile, "link", rb_file_s_link, 2); 05445 rb_define_singleton_method(rb_cFile, "symlink", rb_file_s_symlink, 2); 05446 rb_define_singleton_method(rb_cFile, "readlink", rb_file_s_readlink, 1); 05447 05448 rb_define_singleton_method(rb_cFile, "unlink", rb_file_s_unlink, -2); 05449 rb_define_singleton_method(rb_cFile, "delete", rb_file_s_unlink, -2); 05450 rb_define_singleton_method(rb_cFile, "rename", rb_file_s_rename, 2); 05451 rb_define_singleton_method(rb_cFile, "umask", rb_file_s_umask, -1); 05452 rb_define_singleton_method(rb_cFile, "truncate", rb_file_s_truncate, 2); 05453 rb_define_singleton_method(rb_cFile, "expand_path", rb_file_s_expand_path, -1); 05454 rb_define_singleton_method(rb_cFile, "absolute_path", rb_file_s_absolute_path, -1); 05455 rb_define_singleton_method(rb_cFile, "realpath", rb_file_s_realpath, -1); 05456 rb_define_singleton_method(rb_cFile, "realdirpath", rb_file_s_realdirpath, -1); 05457 rb_define_singleton_method(rb_cFile, "basename", rb_file_s_basename, -1); 05458 rb_define_singleton_method(rb_cFile, "dirname", rb_file_s_dirname, 1); 05459 rb_define_singleton_method(rb_cFile, "extname", rb_file_s_extname, 1); 05460 rb_define_singleton_method(rb_cFile, "path", rb_file_s_path, 1); 05461 05462 separator = rb_obj_freeze(rb_usascii_str_new2("/")); 05463 rb_define_const(rb_cFile, "Separator", separator); 05464 rb_define_const(rb_cFile, "SEPARATOR", separator); 05465 rb_define_singleton_method(rb_cFile, "split", rb_file_s_split, 1); 05466 rb_define_singleton_method(rb_cFile, "join", rb_file_s_join, -2); 05467 05468 #ifdef DOSISH 05469 rb_define_const(rb_cFile, "ALT_SEPARATOR", rb_obj_freeze(rb_usascii_str_new2(file_alt_separator))); 05470 #else 05471 rb_define_const(rb_cFile, "ALT_SEPARATOR", Qnil); 05472 #endif 05473 rb_define_const(rb_cFile, "PATH_SEPARATOR", rb_obj_freeze(rb_str_new2(PATH_SEP))); 05474 05475 rb_define_method(rb_cIO, "stat", rb_io_stat, 0); /* this is IO's method */ 05476 rb_define_method(rb_cFile, "lstat", rb_file_lstat, 0); 05477 05478 rb_define_method(rb_cFile, "atime", rb_file_atime, 0); 05479 rb_define_method(rb_cFile, "mtime", rb_file_mtime, 0); 05480 rb_define_method(rb_cFile, "ctime", rb_file_ctime, 0); 05481 rb_define_method(rb_cFile, "size", rb_file_size, 0); 05482 05483 rb_define_method(rb_cFile, "chmod", rb_file_chmod, 1); 05484 rb_define_method(rb_cFile, "chown", rb_file_chown, 2); 05485 rb_define_method(rb_cFile, "truncate", rb_file_truncate, 1); 05486 05487 rb_define_method(rb_cFile, "flock", rb_file_flock, 1); 05488 05489 rb_mFConst = rb_define_module_under(rb_cFile, "Constants"); 05490 rb_include_module(rb_cIO, rb_mFConst); 05491 rb_file_const("LOCK_SH", INT2FIX(LOCK_SH)); 05492 rb_file_const("LOCK_EX", INT2FIX(LOCK_EX)); 05493 rb_file_const("LOCK_UN", INT2FIX(LOCK_UN)); 05494 rb_file_const("LOCK_NB", INT2FIX(LOCK_NB)); 05495 05496 rb_file_const("NULL", rb_obj_freeze(rb_usascii_str_new2(null_device))); 05497 05498 rb_define_method(rb_cFile, "path", rb_file_path, 0); 05499 rb_define_method(rb_cFile, "to_path", rb_file_path, 0); 05500 rb_define_global_function("test", rb_f_test, -1); 05501 05502 rb_cStat = rb_define_class_under(rb_cFile, "Stat", rb_cObject); 05503 rb_define_alloc_func(rb_cStat, rb_stat_s_alloc); 05504 rb_define_method(rb_cStat, "initialize", rb_stat_init, 1); 05505 rb_define_method(rb_cStat, "initialize_copy", rb_stat_init_copy, 1); 05506 05507 rb_include_module(rb_cStat, rb_mComparable); 05508 05509 rb_define_method(rb_cStat, "<=>", rb_stat_cmp, 1); 05510 05511 rb_define_method(rb_cStat, "dev", rb_stat_dev, 0); 05512 rb_define_method(rb_cStat, "dev_major", rb_stat_dev_major, 0); 05513 rb_define_method(rb_cStat, "dev_minor", rb_stat_dev_minor, 0); 05514 rb_define_method(rb_cStat, "ino", rb_stat_ino, 0); 05515 rb_define_method(rb_cStat, "mode", rb_stat_mode, 0); 05516 rb_define_method(rb_cStat, "nlink", rb_stat_nlink, 0); 05517 rb_define_method(rb_cStat, "uid", rb_stat_uid, 0); 05518 rb_define_method(rb_cStat, "gid", rb_stat_gid, 0); 05519 rb_define_method(rb_cStat, "rdev", rb_stat_rdev, 0); 05520 rb_define_method(rb_cStat, "rdev_major", rb_stat_rdev_major, 0); 05521 rb_define_method(rb_cStat, "rdev_minor", rb_stat_rdev_minor, 0); 05522 rb_define_method(rb_cStat, "size", rb_stat_size, 0); 05523 rb_define_method(rb_cStat, "blksize", rb_stat_blksize, 0); 05524 rb_define_method(rb_cStat, "blocks", rb_stat_blocks, 0); 05525 rb_define_method(rb_cStat, "atime", rb_stat_atime, 0); 05526 rb_define_method(rb_cStat, "mtime", rb_stat_mtime, 0); 05527 rb_define_method(rb_cStat, "ctime", rb_stat_ctime, 0); 05528 05529 rb_define_method(rb_cStat, "inspect", rb_stat_inspect, 0); 05530 05531 rb_define_method(rb_cStat, "ftype", rb_stat_ftype, 0); 05532 05533 rb_define_method(rb_cStat, "directory?", rb_stat_d, 0); 05534 rb_define_method(rb_cStat, "readable?", rb_stat_r, 0); 05535 rb_define_method(rb_cStat, "readable_real?", rb_stat_R, 0); 05536 rb_define_method(rb_cStat, "world_readable?", rb_stat_wr, 0); 05537 rb_define_method(rb_cStat, "writable?", rb_stat_w, 0); 05538 rb_define_method(rb_cStat, "writable_real?", rb_stat_W, 0); 05539 rb_define_method(rb_cStat, "world_writable?", rb_stat_ww, 0); 05540 rb_define_method(rb_cStat, "executable?", rb_stat_x, 0); 05541 rb_define_method(rb_cStat, "executable_real?", rb_stat_X, 0); 05542 rb_define_method(rb_cStat, "file?", rb_stat_f, 0); 05543 rb_define_method(rb_cStat, "zero?", rb_stat_z, 0); 05544 rb_define_method(rb_cStat, "size?", rb_stat_s, 0); 05545 rb_define_method(rb_cStat, "owned?", rb_stat_owned, 0); 05546 rb_define_method(rb_cStat, "grpowned?", rb_stat_grpowned, 0); 05547 05548 rb_define_method(rb_cStat, "pipe?", rb_stat_p, 0); 05549 rb_define_method(rb_cStat, "symlink?", rb_stat_l, 0); 05550 rb_define_method(rb_cStat, "socket?", rb_stat_S, 0); 05551 05552 rb_define_method(rb_cStat, "blockdev?", rb_stat_b, 0); 05553 rb_define_method(rb_cStat, "chardev?", rb_stat_c, 0); 05554 05555 rb_define_method(rb_cStat, "setuid?", rb_stat_suid, 0); 05556 rb_define_method(rb_cStat, "setgid?", rb_stat_sgid, 0); 05557 rb_define_method(rb_cStat, "sticky?", rb_stat_sticky, 0); 05558 05559 #ifdef _WIN32 05560 rb_w32_init_file(); 05561 #endif 05562 } 05563
1.7.6.1