1 /**********************************************************************
2
3 file.c -
4
5 $Author: usa $
6 created at: Mon Nov 15 12:24:34 JST 1993
7
8 Copyright (C) 1993-2007 Yukihiro Matsumoto
9 Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
10 Copyright (C) 2000 Information-technology Promotion Agency, Japan
11
12 **********************************************************************/
13
14 #ifdef _WIN32
15 #include "missing/file.h"
16 #endif
17 #ifdef __CYGWIN__
18 #include <windows.h>
19 #include <sys/cygwin.h>
20 #include <wchar.h>
21 #endif
22 #ifdef __APPLE__
23 #include <CoreFoundation/CFString.h>
24 #endif
25
26 #include "id.h"
27 #include "ruby/encoding.h"
28 #include "ruby/io.h"
29 #include "ruby/util.h"
30 #include "ruby/thread.h"
31 #include "internal.h"
32 #include "dln.h"
33 #include "encindex.h"
34
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38 #ifdef HAVE_SYS_TIME_H
39 # include <sys/time.h>
40 #endif
41
42 #ifdef HAVE_SYS_FILE_H
43 # include <sys/file.h>
44 #else
45 int flock(int, int);
46 #endif
47
48 #ifdef HAVE_SYS_PARAM_H
49 # include <sys/param.h>
50 #endif
51 #ifndef MAXPATHLEN
52 # define MAXPATHLEN 1024
53 #endif
54
55 #include <ctype.h>
56
57 #include <time.h>
58
59 #ifdef HAVE_UTIME_H
60 #include <utime.h>
61 #elif defined HAVE_SYS_UTIME_H
62 #include <sys/utime.h>
63 #endif
64
65 #ifdef HAVE_PWD_H
66 #include <pwd.h>
67 #endif
68
69 #ifdef HAVE_SYS_SYSMACROS_H
70 #include <sys/sysmacros.h>
71 #endif
72
73 #include <sys/types.h>
74 #include <sys/stat.h>
75
76 #ifdef HAVE_SYS_MKDEV_H
77 #include <sys/mkdev.h>
78 #endif
79
80 #if defined(HAVE_FCNTL_H)
81 #include <fcntl.h>
82 #endif
83
84 #if defined(HAVE_SYS_TIME_H)
85 #include <sys/time.h>
86 #endif
87
88 #if !defined HAVE_LSTAT && !defined lstat
89 #define lstat stat
90 #endif
91
92 /* define system APIs */
93 #ifdef _WIN32
94 #include "win32/file.h"
95 #define STAT(p, s) rb_w32_ustati128((p), (s))
96 #undef lstat
97 #define lstat(p, s) rb_w32_ulstati128((p), (s))
98 #undef access
99 #define access(p, m) rb_w32_uaccess((p), (m))
100 #undef truncate
101 #define truncate(p, n) rb_w32_utruncate((p), (n))
102 #undef chmod
103 #define chmod(p, m) rb_w32_uchmod((p), (m))
104 #undef chown
105 #define chown(p, o, g) rb_w32_uchown((p), (o), (g))
106 #undef lchown
107 #define lchown(p, o, g) rb_w32_ulchown((p), (o), (g))
108 #undef utimensat
109 #define utimensat(s, p, t, f) rb_w32_uutimensat((s), (p), (t), (f))
110 #undef link
111 #define link(f, t) rb_w32_ulink((f), (t))
112 #undef unlink
113 #define unlink(p) rb_w32_uunlink(p)
114 #undef rename
115 #define rename(f, t) rb_w32_urename((f), (t))
116 #undef symlink
117 #define symlink(s, l) rb_w32_usymlink((s), (l))
118 #else
119 #define STAT(p, s) stat((p), (s))
120 #endif
121
122 #if defined _WIN32 || defined __APPLE__
123 # define USE_OSPATH 1
124 # define TO_OSPATH(str) rb_str_encode_ospath(str)
125 #else
126 # define USE_OSPATH 0
127 # define TO_OSPATH(str) (str)
128 #endif
129
130 /* utime may fail if time is out-of-range for the FS [ruby-dev:38277] */
131 #if defined DOSISH || defined __CYGWIN__
132 # define UTIME_EINVAL
133 #endif
134
135 VALUE rb_cFile;
136 VALUE rb_mFileTest;
137 VALUE rb_cStat;
138
139 #define insecure_obj_p(obj, level) ((level) > 0 && OBJ_TAINTED(obj))
140
141 static VALUE
file_path_convert(VALUE name)142 file_path_convert(VALUE name)
143 {
144 #ifndef _WIN32 /* non Windows == Unix */
145 int fname_encidx = ENCODING_GET(name);
146 int fs_encidx;
147 if (ENCINDEX_US_ASCII != fname_encidx &&
148 ENCINDEX_ASCII != fname_encidx &&
149 (fs_encidx = rb_filesystem_encindex()) != fname_encidx &&
150 rb_default_internal_encoding() &&
151 !rb_enc_str_asciionly_p(name)) {
152 /* Don't call rb_filesystem_encoding() before US-ASCII and ASCII-8BIT */
153 /* fs_encoding should be ascii compatible */
154 rb_encoding *fname_encoding = rb_enc_from_index(fname_encidx);
155 rb_encoding *fs_encoding = rb_enc_from_index(fs_encidx);
156 name = rb_str_conv_enc(name, fname_encoding, fs_encoding);
157 }
158 #endif
159 return name;
160 }
161
162 static rb_encoding *
check_path_encoding(VALUE str)163 check_path_encoding(VALUE str)
164 {
165 rb_encoding *enc = rb_enc_get(str);
166 if (!rb_enc_asciicompat(enc)) {
167 rb_raise(rb_eEncCompatError, "path name must be ASCII-compatible (%s): %"PRIsVALUE,
168 rb_enc_name(enc), rb_str_inspect(str));
169 }
170 return enc;
171 }
172
173 VALUE
rb_get_path_check_to_string(VALUE obj,int level)174 rb_get_path_check_to_string(VALUE obj, int level)
175 {
176 VALUE tmp;
177 ID to_path;
178
179 if (insecure_obj_p(obj, level)) {
180 rb_insecure_operation();
181 }
182
183 if (RB_TYPE_P(obj, T_STRING)) {
184 return obj;
185 }
186 CONST_ID(to_path, "to_path");
187 tmp = rb_check_funcall_default(obj, to_path, 0, 0, obj);
188 StringValue(tmp);
189 return tmp;
190 }
191
192 VALUE
rb_get_path_check_convert(VALUE obj,VALUE tmp,int level)193 rb_get_path_check_convert(VALUE obj, VALUE tmp, int level)
194 {
195 tmp = file_path_convert(tmp);
196 if (obj != tmp && insecure_obj_p(tmp, level)) {
197 rb_insecure_operation();
198 }
199
200 check_path_encoding(tmp);
201 if (!rb_str_to_cstr(tmp)) {
202 rb_raise(rb_eArgError, "path name contains null byte");
203 }
204
205 return rb_str_new4(tmp);
206 }
207
208 VALUE
rb_get_path_check(VALUE obj,int level)209 rb_get_path_check(VALUE obj, int level)
210 {
211 VALUE tmp = rb_get_path_check_to_string(obj, level);
212 return rb_get_path_check_convert(obj, tmp, level);
213 }
214
215 VALUE
rb_get_path_no_checksafe(VALUE obj)216 rb_get_path_no_checksafe(VALUE obj)
217 {
218 return rb_get_path_check(obj, 0);
219 }
220
221 VALUE
rb_get_path(VALUE obj)222 rb_get_path(VALUE obj)
223 {
224 return rb_get_path_check(obj, rb_safe_level());
225 }
226
227 VALUE
rb_str_encode_ospath(VALUE path)228 rb_str_encode_ospath(VALUE path)
229 {
230 #if USE_OSPATH
231 int encidx = ENCODING_GET(path);
232 #ifdef _WIN32
233 if (encidx == ENCINDEX_ASCII) {
234 encidx = rb_filesystem_encindex();
235 }
236 #endif
237 if (encidx != ENCINDEX_UTF_8) {
238 rb_encoding *enc = rb_enc_from_index(encidx);
239 rb_encoding *utf8 = rb_utf8_encoding();
240 path = rb_str_conv_enc(path, enc, utf8);
241 }
242 #endif
243 return path;
244 }
245
246 #ifdef __APPLE__
247 # define NORMALIZE_UTF8PATH 1
248 static VALUE
rb_str_append_normalized_ospath(VALUE str,const char * ptr,long len)249 rb_str_append_normalized_ospath(VALUE str, const char *ptr, long len)
250 {
251 CFIndex buflen = 0;
252 CFRange all;
253 CFStringRef s = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault,
254 (const UInt8 *)ptr, len,
255 kCFStringEncodingUTF8, FALSE,
256 kCFAllocatorNull);
257 CFMutableStringRef m = CFStringCreateMutableCopy(kCFAllocatorDefault, len, s);
258 long oldlen = RSTRING_LEN(str);
259
260 CFStringNormalize(m, kCFStringNormalizationFormC);
261 all = CFRangeMake(0, CFStringGetLength(m));
262 CFStringGetBytes(m, all, kCFStringEncodingUTF8, '?', FALSE, NULL, 0, &buflen);
263 rb_str_modify_expand(str, buflen);
264 CFStringGetBytes(m, all, kCFStringEncodingUTF8, '?', FALSE,
265 (UInt8 *)(RSTRING_PTR(str) + oldlen), buflen, &buflen);
266 rb_str_set_len(str, oldlen + buflen);
267 CFRelease(m);
268 CFRelease(s);
269 return str;
270 }
271
272 VALUE
rb_str_normalize_ospath(const char * ptr,long len)273 rb_str_normalize_ospath(const char *ptr, long len)
274 {
275 const char *p = ptr;
276 const char *e = ptr + len;
277 const char *p1 = p;
278 VALUE str = rb_str_buf_new(len);
279 rb_encoding *enc = rb_utf8_encoding();
280 rb_enc_associate(str, enc);
281
282 while (p < e) {
283 int l, c;
284 int r = rb_enc_precise_mbclen(p, e, enc);
285 if (!MBCLEN_CHARFOUND_P(r)) {
286 /* invalid byte shall not happen but */
287 static const char invalid[3] = "\xEF\xBF\xBD";
288 rb_str_append_normalized_ospath(str, p1, p-p1);
289 rb_str_cat(str, invalid, sizeof(invalid));
290 p += 1;
291 p1 = p;
292 continue;
293 }
294 l = MBCLEN_CHARFOUND_LEN(r);
295 c = rb_enc_mbc_to_codepoint(p, e, enc);
296 if ((0x2000 <= c && c <= 0x2FFF) || (0xF900 <= c && c <= 0xFAFF) ||
297 (0x2F800 <= c && c <= 0x2FAFF)) {
298 if (p - p1 > 0) {
299 rb_str_append_normalized_ospath(str, p1, p-p1);
300 }
301 rb_str_cat(str, p, l);
302 p += l;
303 p1 = p;
304 }
305 else {
306 p += l;
307 }
308 }
309 if (p - p1 > 0) {
310 rb_str_append_normalized_ospath(str, p1, p-p1);
311 }
312
313 return str;
314 }
315
316 static int
ignored_char_p(const char * p,const char * e,rb_encoding * enc)317 ignored_char_p(const char *p, const char *e, rb_encoding *enc)
318 {
319 unsigned char c;
320 if (p+3 > e) return 0;
321 switch ((unsigned char)*p) {
322 case 0xe2:
323 switch ((unsigned char)p[1]) {
324 case 0x80:
325 c = (unsigned char)p[2];
326 /* c >= 0x200c && c <= 0x200f */
327 if (c >= 0x8c && c <= 0x8f) return 3;
328 /* c >= 0x202a && c <= 0x202e */
329 if (c >= 0xaa && c <= 0xae) return 3;
330 return 0;
331 case 0x81:
332 c = (unsigned char)p[2];
333 /* c >= 0x206a && c <= 0x206f */
334 if (c >= 0xaa && c <= 0xaf) return 3;
335 return 0;
336 }
337 break;
338 case 0xef:
339 /* c == 0xfeff */
340 if ((unsigned char)p[1] == 0xbb &&
341 (unsigned char)p[2] == 0xbf)
342 return 3;
343 break;
344 }
345 return 0;
346 }
347 #else
348 # define NORMALIZE_UTF8PATH 0
349 #endif
350
351 #define apply2args(n) (rb_check_arity(argc, n, UNLIMITED_ARGUMENTS), argc-=n)
352
353 struct apply_filename {
354 const char *ptr;
355 VALUE path;
356 };
357
358 struct apply_arg {
359 int i;
360 int argc;
361 int errnum;
362 int (*func)(const char *, void *);
363 void *arg;
364 struct apply_filename fn[FLEX_ARY_LEN];
365 };
366
367 static void *
no_gvl_apply2files(void * ptr)368 no_gvl_apply2files(void *ptr)
369 {
370 struct apply_arg *aa = ptr;
371
372 for (aa->i = 0; aa->i < aa->argc; aa->i++) {
373 if (aa->func(aa->fn[aa->i].ptr, aa->arg) < 0) {
374 aa->errnum = errno;
375 break;
376 }
377 }
378 return 0;
379 }
380
381 #ifdef UTIME_EINVAL
382 NORETURN(static void utime_failed(struct apply_arg *));
383 static int utime_internal(const char *, void *);
384 #endif
385
386 static VALUE
apply2files(int (* func)(const char *,void *),int argc,VALUE * argv,void * arg)387 apply2files(int (*func)(const char *, void *), int argc, VALUE *argv, void *arg)
388 {
389 VALUE v;
390 const size_t size = sizeof(struct apply_filename);
391 const long len = (long)(offsetof(struct apply_arg, fn) + (size * argc));
392 struct apply_arg *aa = ALLOCV(v, len);
393
394 aa->errnum = 0;
395 aa->argc = argc;
396 aa->arg = arg;
397 aa->func = func;
398
399 for (aa->i = 0; aa->i < argc; aa->i++) {
400 VALUE path = rb_get_path(argv[aa->i]);
401
402 path = rb_str_encode_ospath(path);
403 aa->fn[aa->i].ptr = RSTRING_PTR(path);
404 aa->fn[aa->i].path = path;
405 }
406
407 rb_thread_call_without_gvl(no_gvl_apply2files, aa, RUBY_UBF_IO, 0);
408 if (aa->errnum) {
409 #ifdef UTIME_EINVAL
410 if (func == utime_internal) {
411 utime_failed(aa);
412 }
413 #endif
414 rb_syserr_fail_path(aa->errnum, aa->fn[aa->i].path);
415 }
416 if (v) {
417 ALLOCV_END(v);
418 }
419 return LONG2FIX(argc);
420 }
421
422 /*
423 * call-seq:
424 * file.path -> filename
425 * file.to_path -> filename
426 *
427 * Returns the pathname used to create <i>file</i> as a string. Does
428 * not normalize the name.
429 *
430 * The pathname may not point to the file corresponding to <i>file</i>.
431 * For instance, the pathname becomes void when the file has been
432 * moved or deleted.
433 *
434 * This method raises <code>IOError</code> for a <i>file</i> created using
435 * <code>File::Constants::TMPFILE</code> because they don't have a pathname.
436 *
437 * File.new("testfile").path #=> "testfile"
438 * File.new("/tmp/../tmp/xxx", "w").path #=> "/tmp/../tmp/xxx"
439 *
440 */
441
442 static VALUE
rb_file_path(VALUE obj)443 rb_file_path(VALUE obj)
444 {
445 rb_io_t *fptr;
446
447 fptr = RFILE(rb_io_taint_check(obj))->fptr;
448 rb_io_check_initialized(fptr);
449
450 if (NIL_P(fptr->pathv)) {
451 rb_raise(rb_eIOError, "File is unnamed (TMPFILE?)");
452 }
453
454 return rb_obj_taint(rb_str_dup(fptr->pathv));
455 }
456
457 static size_t
stat_memsize(const void * p)458 stat_memsize(const void *p)
459 {
460 return sizeof(struct stat);
461 }
462
463 static const rb_data_type_t stat_data_type = {
464 "stat",
465 {NULL, RUBY_TYPED_DEFAULT_FREE, stat_memsize,},
466 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
467 };
468
469 static VALUE
stat_new_0(VALUE klass,const struct stat * st)470 stat_new_0(VALUE klass, const struct stat *st)
471 {
472 struct stat *nst = 0;
473 VALUE obj = TypedData_Wrap_Struct(klass, &stat_data_type, 0);
474
475 if (st) {
476 nst = ALLOC(struct stat);
477 *nst = *st;
478 RTYPEDDATA_DATA(obj) = nst;
479 }
480 return obj;
481 }
482
483 VALUE
rb_stat_new(const struct stat * st)484 rb_stat_new(const struct stat *st)
485 {
486 return stat_new_0(rb_cStat, st);
487 }
488
489 static struct stat*
get_stat(VALUE self)490 get_stat(VALUE self)
491 {
492 struct stat* st;
493 TypedData_Get_Struct(self, struct stat, &stat_data_type, st);
494 if (!st) rb_raise(rb_eTypeError, "uninitialized File::Stat");
495 return st;
496 }
497
498 static struct timespec stat_mtimespec(struct stat *st);
499
500 /*
501 * call-seq:
502 * stat <=> other_stat -> -1, 0, 1, nil
503 *
504 * Compares File::Stat objects by comparing their respective modification
505 * times.
506 *
507 * +nil+ is returned if +other_stat+ is not a File::Stat object
508 *
509 * f1 = File.new("f1", "w")
510 * sleep 1
511 * f2 = File.new("f2", "w")
512 * f1.stat <=> f2.stat #=> -1
513 */
514
515 static VALUE
rb_stat_cmp(VALUE self,VALUE other)516 rb_stat_cmp(VALUE self, VALUE other)
517 {
518 if (rb_obj_is_kind_of(other, rb_obj_class(self))) {
519 struct timespec ts1 = stat_mtimespec(get_stat(self));
520 struct timespec ts2 = stat_mtimespec(get_stat(other));
521 if (ts1.tv_sec == ts2.tv_sec) {
522 if (ts1.tv_nsec == ts2.tv_nsec) return INT2FIX(0);
523 if (ts1.tv_nsec < ts2.tv_nsec) return INT2FIX(-1);
524 return INT2FIX(1);
525 }
526 if (ts1.tv_sec < ts2.tv_sec) return INT2FIX(-1);
527 return INT2FIX(1);
528 }
529 return Qnil;
530 }
531
532 #define ST2UINT(val) ((val) & ~(~1UL << (sizeof(val) * CHAR_BIT - 1)))
533
534 #ifndef NUM2DEVT
535 # define NUM2DEVT(v) NUM2UINT(v)
536 #endif
537 #ifndef DEVT2NUM
538 # define DEVT2NUM(v) UINT2NUM(v)
539 #endif
540 #ifndef PRI_DEVT_PREFIX
541 # define PRI_DEVT_PREFIX ""
542 #endif
543
544 /*
545 * call-seq:
546 * stat.dev -> integer
547 *
548 * Returns an integer representing the device on which <i>stat</i>
549 * resides.
550 *
551 * File.stat("testfile").dev #=> 774
552 */
553
554 static VALUE
rb_stat_dev(VALUE self)555 rb_stat_dev(VALUE self)
556 {
557 return DEVT2NUM(get_stat(self)->st_dev);
558 }
559
560 /*
561 * call-seq:
562 * stat.dev_major -> integer
563 *
564 * Returns the major part of <code>File_Stat#dev</code> or
565 * <code>nil</code>.
566 *
567 * File.stat("/dev/fd1").dev_major #=> 2
568 * File.stat("/dev/tty").dev_major #=> 5
569 */
570
571 static VALUE
rb_stat_dev_major(VALUE self)572 rb_stat_dev_major(VALUE self)
573 {
574 #if defined(major)
575 return UINT2NUM(major(get_stat(self)->st_dev));
576 #else
577 return Qnil;
578 #endif
579 }
580
581 /*
582 * call-seq:
583 * stat.dev_minor -> integer
584 *
585 * Returns the minor part of <code>File_Stat#dev</code> or
586 * <code>nil</code>.
587 *
588 * File.stat("/dev/fd1").dev_minor #=> 1
589 * File.stat("/dev/tty").dev_minor #=> 0
590 */
591
592 static VALUE
rb_stat_dev_minor(VALUE self)593 rb_stat_dev_minor(VALUE self)
594 {
595 #if defined(minor)
596 return UINT2NUM(minor(get_stat(self)->st_dev));
597 #else
598 return Qnil;
599 #endif
600 }
601
602 /*
603 * call-seq:
604 * stat.ino -> integer
605 *
606 * Returns the inode number for <i>stat</i>.
607 *
608 * File.stat("testfile").ino #=> 1083669
609 *
610 */
611
612 static VALUE
rb_stat_ino(VALUE self)613 rb_stat_ino(VALUE self)
614 {
615 #ifdef HAVE_STRUCT_STAT_ST_INOHIGH
616 /* assume INTEGER_PACK_LSWORD_FIRST and st_inohigh is just next of st_ino */
617 return rb_integer_unpack(&get_stat(self)->st_ino, 2,
618 SIZEOF_STRUCT_STAT_ST_INO, 0,
619 INTEGER_PACK_LSWORD_FIRST|INTEGER_PACK_NATIVE_BYTE_ORDER|
620 INTEGER_PACK_2COMP);
621 #elif SIZEOF_STRUCT_STAT_ST_INO > SIZEOF_LONG
622 return ULL2NUM(get_stat(self)->st_ino);
623 #else
624 return ULONG2NUM(get_stat(self)->st_ino);
625 #endif
626 }
627
628 /*
629 * call-seq:
630 * stat.mode -> integer
631 *
632 * Returns an integer representing the permission bits of
633 * <i>stat</i>. The meaning of the bits is platform dependent; on
634 * Unix systems, see <code>stat(2)</code>.
635 *
636 * File.chmod(0644, "testfile") #=> 1
637 * s = File.stat("testfile")
638 * sprintf("%o", s.mode) #=> "100644"
639 */
640
641 static VALUE
rb_stat_mode(VALUE self)642 rb_stat_mode(VALUE self)
643 {
644 return UINT2NUM(ST2UINT(get_stat(self)->st_mode));
645 }
646
647 /*
648 * call-seq:
649 * stat.nlink -> integer
650 *
651 * Returns the number of hard links to <i>stat</i>.
652 *
653 * File.stat("testfile").nlink #=> 1
654 * File.link("testfile", "testfile.bak") #=> 0
655 * File.stat("testfile").nlink #=> 2
656 *
657 */
658
659 static VALUE
rb_stat_nlink(VALUE self)660 rb_stat_nlink(VALUE self)
661 {
662 return UINT2NUM(get_stat(self)->st_nlink);
663 }
664
665 /*
666 * call-seq:
667 * stat.uid -> integer
668 *
669 * Returns the numeric user id of the owner of <i>stat</i>.
670 *
671 * File.stat("testfile").uid #=> 501
672 *
673 */
674
675 static VALUE
rb_stat_uid(VALUE self)676 rb_stat_uid(VALUE self)
677 {
678 return UIDT2NUM(get_stat(self)->st_uid);
679 }
680
681 /*
682 * call-seq:
683 * stat.gid -> integer
684 *
685 * Returns the numeric group id of the owner of <i>stat</i>.
686 *
687 * File.stat("testfile").gid #=> 500
688 *
689 */
690
691 static VALUE
rb_stat_gid(VALUE self)692 rb_stat_gid(VALUE self)
693 {
694 return GIDT2NUM(get_stat(self)->st_gid);
695 }
696
697 /*
698 * call-seq:
699 * stat.rdev -> integer or nil
700 *
701 * Returns an integer representing the device type on which
702 * <i>stat</i> resides. Returns <code>nil</code> if the operating
703 * system doesn't support this feature.
704 *
705 * File.stat("/dev/fd1").rdev #=> 513
706 * File.stat("/dev/tty").rdev #=> 1280
707 */
708
709 static VALUE
rb_stat_rdev(VALUE self)710 rb_stat_rdev(VALUE self)
711 {
712 #ifdef HAVE_STRUCT_STAT_ST_RDEV
713 return DEVT2NUM(get_stat(self)->st_rdev);
714 #else
715 return Qnil;
716 #endif
717 }
718
719 /*
720 * call-seq:
721 * stat.rdev_major -> integer
722 *
723 * Returns the major part of <code>File_Stat#rdev</code> or
724 * <code>nil</code>.
725 *
726 * File.stat("/dev/fd1").rdev_major #=> 2
727 * File.stat("/dev/tty").rdev_major #=> 5
728 */
729
730 static VALUE
rb_stat_rdev_major(VALUE self)731 rb_stat_rdev_major(VALUE self)
732 {
733 #if defined(HAVE_STRUCT_STAT_ST_RDEV) && defined(major)
734 return UINT2NUM(major(get_stat(self)->st_rdev));
735 #else
736 return Qnil;
737 #endif
738 }
739
740 /*
741 * call-seq:
742 * stat.rdev_minor -> integer
743 *
744 * Returns the minor part of <code>File_Stat#rdev</code> or
745 * <code>nil</code>.
746 *
747 * File.stat("/dev/fd1").rdev_minor #=> 1
748 * File.stat("/dev/tty").rdev_minor #=> 0
749 */
750
751 static VALUE
rb_stat_rdev_minor(VALUE self)752 rb_stat_rdev_minor(VALUE self)
753 {
754 #if defined(HAVE_STRUCT_STAT_ST_RDEV) && defined(minor)
755 return UINT2NUM(minor(get_stat(self)->st_rdev));
756 #else
757 return Qnil;
758 #endif
759 }
760
761 /*
762 * call-seq:
763 * stat.size -> integer
764 *
765 * Returns the size of <i>stat</i> in bytes.
766 *
767 * File.stat("testfile").size #=> 66
768 */
769
770 static VALUE
rb_stat_size(VALUE self)771 rb_stat_size(VALUE self)
772 {
773 return OFFT2NUM(get_stat(self)->st_size);
774 }
775
776 /*
777 * call-seq:
778 * stat.blksize -> integer or nil
779 *
780 * Returns the native file system's block size. Will return <code>nil</code>
781 * on platforms that don't support this information.
782 *
783 * File.stat("testfile").blksize #=> 4096
784 *
785 */
786
787 static VALUE
rb_stat_blksize(VALUE self)788 rb_stat_blksize(VALUE self)
789 {
790 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
791 return ULONG2NUM(get_stat(self)->st_blksize);
792 #else
793 return Qnil;
794 #endif
795 }
796
797 /*
798 * call-seq:
799 * stat.blocks -> integer or nil
800 *
801 * Returns the number of native file system blocks allocated for this
802 * file, or <code>nil</code> if the operating system doesn't
803 * support this feature.
804 *
805 * File.stat("testfile").blocks #=> 2
806 */
807
808 static VALUE
rb_stat_blocks(VALUE self)809 rb_stat_blocks(VALUE self)
810 {
811 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
812 # if SIZEOF_STRUCT_STAT_ST_BLOCKS > SIZEOF_LONG
813 return ULL2NUM(get_stat(self)->st_blocks);
814 # else
815 return ULONG2NUM(get_stat(self)->st_blocks);
816 # endif
817 #else
818 return Qnil;
819 #endif
820 }
821
822 static struct timespec
stat_atimespec(struct stat * st)823 stat_atimespec(struct stat *st)
824 {
825 struct timespec ts;
826 ts.tv_sec = st->st_atime;
827 #if defined(HAVE_STRUCT_STAT_ST_ATIM)
828 ts.tv_nsec = st->st_atim.tv_nsec;
829 #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
830 ts.tv_nsec = st->st_atimespec.tv_nsec;
831 #elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC)
832 ts.tv_nsec = (long)st->st_atimensec;
833 #else
834 ts.tv_nsec = 0;
835 #endif
836 return ts;
837 }
838
839 static VALUE
stat_atime(struct stat * st)840 stat_atime(struct stat *st)
841 {
842 struct timespec ts = stat_atimespec(st);
843 return rb_time_nano_new(ts.tv_sec, ts.tv_nsec);
844 }
845
846 static struct timespec
stat_mtimespec(struct stat * st)847 stat_mtimespec(struct stat *st)
848 {
849 struct timespec ts;
850 ts.tv_sec = st->st_mtime;
851 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
852 ts.tv_nsec = st->st_mtim.tv_nsec;
853 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
854 ts.tv_nsec = st->st_mtimespec.tv_nsec;
855 #elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
856 ts.tv_nsec = (long)st->st_mtimensec;
857 #else
858 ts.tv_nsec = 0;
859 #endif
860 return ts;
861 }
862
863 static VALUE
stat_mtime(struct stat * st)864 stat_mtime(struct stat *st)
865 {
866 struct timespec ts = stat_mtimespec(st);
867 return rb_time_nano_new(ts.tv_sec, ts.tv_nsec);
868 }
869
870 static struct timespec
stat_ctimespec(struct stat * st)871 stat_ctimespec(struct stat *st)
872 {
873 struct timespec ts;
874 ts.tv_sec = st->st_ctime;
875 #if defined(HAVE_STRUCT_STAT_ST_CTIM)
876 ts.tv_nsec = st->st_ctim.tv_nsec;
877 #elif defined(HAVE_STRUCT_STAT_ST_CTIMESPEC)
878 ts.tv_nsec = st->st_ctimespec.tv_nsec;
879 #elif defined(HAVE_STRUCT_STAT_ST_CTIMENSEC)
880 ts.tv_nsec = (long)st->st_ctimensec;
881 #else
882 ts.tv_nsec = 0;
883 #endif
884 return ts;
885 }
886
887 static VALUE
stat_ctime(struct stat * st)888 stat_ctime(struct stat *st)
889 {
890 struct timespec ts = stat_ctimespec(st);
891 return rb_time_nano_new(ts.tv_sec, ts.tv_nsec);
892 }
893
894 #define HAVE_STAT_BIRTHTIME
895 #if defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC)
896 static VALUE
stat_birthtime(struct stat * st)897 stat_birthtime(struct stat *st)
898 {
899 struct timespec *ts = &st->st_birthtimespec;
900 return rb_time_nano_new(ts->tv_sec, ts->tv_nsec);
901 }
902 #elif defined(_WIN32)
903 # define stat_birthtime stat_ctime
904 #else
905 # undef HAVE_STAT_BIRTHTIME
906 #endif
907
908 /*
909 * call-seq:
910 * stat.atime -> time
911 *
912 * Returns the last access time for this file as an object of class
913 * <code>Time</code>.
914 *
915 * File.stat("testfile").atime #=> Wed Dec 31 18:00:00 CST 1969
916 *
917 */
918
919 static VALUE
rb_stat_atime(VALUE self)920 rb_stat_atime(VALUE self)
921 {
922 return stat_atime(get_stat(self));
923 }
924
925 /*
926 * call-seq:
927 * stat.mtime -> aTime
928 *
929 * Returns the modification time of <i>stat</i>.
930 *
931 * File.stat("testfile").mtime #=> Wed Apr 09 08:53:14 CDT 2003
932 *
933 */
934
935 static VALUE
rb_stat_mtime(VALUE self)936 rb_stat_mtime(VALUE self)
937 {
938 return stat_mtime(get_stat(self));
939 }
940
941 /*
942 * call-seq:
943 * stat.ctime -> aTime
944 *
945 * Returns the change time for <i>stat</i> (that is, the time
946 * directory information about the file was changed, not the file
947 * itself).
948 *
949 * Note that on Windows (NTFS), returns creation time (birth time).
950 *
951 * File.stat("testfile").ctime #=> Wed Apr 09 08:53:14 CDT 2003
952 *
953 */
954
955 static VALUE
rb_stat_ctime(VALUE self)956 rb_stat_ctime(VALUE self)
957 {
958 return stat_ctime(get_stat(self));
959 }
960
961 #if defined(HAVE_STAT_BIRTHTIME)
962 /*
963 * call-seq:
964 * stat.birthtime -> aTime
965 *
966 * Returns the birth time for <i>stat</i>.
967 *
968 * If the platform doesn't have birthtime, raises NotImplementedError.
969 *
970 * File.write("testfile", "foo")
971 * sleep 10
972 * File.write("testfile", "bar")
973 * sleep 10
974 * File.chmod(0644, "testfile")
975 * sleep 10
976 * File.read("testfile")
977 * File.stat("testfile").birthtime #=> 2014-02-24 11:19:17 +0900
978 * File.stat("testfile").mtime #=> 2014-02-24 11:19:27 +0900
979 * File.stat("testfile").ctime #=> 2014-02-24 11:19:37 +0900
980 * File.stat("testfile").atime #=> 2014-02-24 11:19:47 +0900
981 *
982 */
983
984 static VALUE
rb_stat_birthtime(VALUE self)985 rb_stat_birthtime(VALUE self)
986 {
987 return stat_birthtime(get_stat(self));
988 }
989 #else
990 # define rb_stat_birthtime rb_f_notimplement
991 #endif
992
993 /*
994 * call-seq:
995 * stat.inspect -> string
996 *
997 * Produce a nicely formatted description of <i>stat</i>.
998 *
999 * File.stat("/etc/passwd").inspect
1000 * #=> "#<File::Stat dev=0xe000005, ino=1078078, mode=0100644,
1001 * # nlink=1, uid=0, gid=0, rdev=0x0, size=1374, blksize=4096,
1002 * # blocks=8, atime=Wed Dec 10 10:16:12 CST 2003,
1003 * # mtime=Fri Sep 12 15:41:41 CDT 2003,
1004 * # ctime=Mon Oct 27 11:20:27 CST 2003,
1005 * # birthtime=Mon Aug 04 08:13:49 CDT 2003>"
1006 */
1007
1008 static VALUE
rb_stat_inspect(VALUE self)1009 rb_stat_inspect(VALUE self)
1010 {
1011 VALUE str;
1012 size_t i;
1013 static const struct {
1014 const char *name;
1015 VALUE (*func)(VALUE);
1016 } member[] = {
1017 {"dev", rb_stat_dev},
1018 {"ino", rb_stat_ino},
1019 {"mode", rb_stat_mode},
1020 {"nlink", rb_stat_nlink},
1021 {"uid", rb_stat_uid},
1022 {"gid", rb_stat_gid},
1023 {"rdev", rb_stat_rdev},
1024 {"size", rb_stat_size},
1025 {"blksize", rb_stat_blksize},
1026 {"blocks", rb_stat_blocks},
1027 {"atime", rb_stat_atime},
1028 {"mtime", rb_stat_mtime},
1029 {"ctime", rb_stat_ctime},
1030 #if defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC)
1031 {"birthtime", rb_stat_birthtime},
1032 #endif
1033 };
1034
1035 struct stat* st;
1036 TypedData_Get_Struct(self, struct stat, &stat_data_type, st);
1037 if (!st) {
1038 return rb_sprintf("#<%s: uninitialized>", rb_obj_classname(self));
1039 }
1040
1041 str = rb_str_buf_new2("#<");
1042 rb_str_buf_cat2(str, rb_obj_classname(self));
1043 rb_str_buf_cat2(str, " ");
1044
1045 for (i = 0; i < sizeof(member)/sizeof(member[0]); i++) {
1046 VALUE v;
1047
1048 if (i > 0) {
1049 rb_str_buf_cat2(str, ", ");
1050 }
1051 rb_str_buf_cat2(str, member[i].name);
1052 rb_str_buf_cat2(str, "=");
1053 v = (*member[i].func)(self);
1054 if (i == 2) { /* mode */
1055 rb_str_catf(str, "0%lo", (unsigned long)NUM2ULONG(v));
1056 }
1057 else if (i == 0 || i == 6) { /* dev/rdev */
1058 rb_str_catf(str, "0x%"PRI_DEVT_PREFIX"x", NUM2DEVT(v));
1059 }
1060 else {
1061 rb_str_append(str, rb_inspect(v));
1062 }
1063 }
1064 rb_str_buf_cat2(str, ">");
1065 OBJ_INFECT(str, self);
1066
1067 return str;
1068 }
1069
1070 typedef struct no_gvl_stat_data {
1071 struct stat *st;
1072 union {
1073 const char *path;
1074 int fd;
1075 } file;
1076 } no_gvl_stat_data;
1077
1078 static VALUE
no_gvl_fstat(void * data)1079 no_gvl_fstat(void *data)
1080 {
1081 no_gvl_stat_data *arg = data;
1082 return (VALUE)fstat(arg->file.fd, arg->st);
1083 }
1084
1085 static int
fstat_without_gvl(int fd,struct stat * st)1086 fstat_without_gvl(int fd, struct stat *st)
1087 {
1088 no_gvl_stat_data data;
1089
1090 data.file.fd = fd;
1091 data.st = st;
1092
1093 return (int)(VALUE)rb_thread_io_blocking_region(no_gvl_fstat, &data, fd);
1094 }
1095
1096 static void *
no_gvl_stat(void * data)1097 no_gvl_stat(void * data)
1098 {
1099 no_gvl_stat_data *arg = data;
1100 return (void *)(VALUE)STAT(arg->file.path, arg->st);
1101 }
1102
1103 static int
stat_without_gvl(const char * path,struct stat * st)1104 stat_without_gvl(const char *path, struct stat *st)
1105 {
1106 no_gvl_stat_data data;
1107
1108 data.file.path = path;
1109 data.st = st;
1110
1111 return (int)(VALUE)rb_thread_call_without_gvl(no_gvl_stat, &data,
1112 RUBY_UBF_IO, NULL);
1113 }
1114
1115 static int
rb_stat(VALUE file,struct stat * st)1116 rb_stat(VALUE file, struct stat *st)
1117 {
1118 VALUE tmp;
1119 int result;
1120
1121 tmp = rb_check_convert_type_with_id(file, T_FILE, "IO", idTo_io);
1122 if (!NIL_P(tmp)) {
1123 rb_io_t *fptr;
1124
1125 GetOpenFile(tmp, fptr);
1126 result = fstat_without_gvl(fptr->fd, st);
1127 file = tmp;
1128 }
1129 else {
1130 FilePathValue(file);
1131 file = rb_str_encode_ospath(file);
1132 result = stat_without_gvl(RSTRING_PTR(file), st);
1133 }
1134 RB_GC_GUARD(file);
1135 return result;
1136 }
1137
1138 /*
1139 * call-seq:
1140 * File.stat(file_name) -> stat
1141 *
1142 * Returns a <code>File::Stat</code> object for the named file (see
1143 * <code>File::Stat</code>).
1144 *
1145 * File.stat("testfile").mtime #=> Tue Apr 08 12:58:04 CDT 2003
1146 *
1147 */
1148
1149 static VALUE
rb_file_s_stat(VALUE klass,VALUE fname)1150 rb_file_s_stat(VALUE klass, VALUE fname)
1151 {
1152 struct stat st;
1153
1154 FilePathValue(fname);
1155 fname = rb_str_encode_ospath(fname);
1156 if (stat_without_gvl(RSTRING_PTR(fname), &st) < 0) {
1157 rb_sys_fail_path(fname);
1158 }
1159 return rb_stat_new(&st);
1160 }
1161
1162 /*
1163 * call-seq:
1164 * ios.stat -> stat
1165 *
1166 * Returns status information for <em>ios</em> as an object of type
1167 * <code>File::Stat</code>.
1168 *
1169 * f = File.new("testfile")
1170 * s = f.stat
1171 * "%o" % s.mode #=> "100644"
1172 * s.blksize #=> 4096
1173 * s.atime #=> Wed Apr 09 08:53:54 CDT 2003
1174 *
1175 */
1176
1177 static VALUE
rb_io_stat(VALUE obj)1178 rb_io_stat(VALUE obj)
1179 {
1180 rb_io_t *fptr;
1181 struct stat st;
1182
1183 GetOpenFile(obj, fptr);
1184 if (fstat(fptr->fd, &st) == -1) {
1185 rb_sys_fail_path(fptr->pathv);
1186 }
1187 return rb_stat_new(&st);
1188 }
1189
1190 #ifdef HAVE_LSTAT
1191 static void *
no_gvl_lstat(void * ptr)1192 no_gvl_lstat(void *ptr)
1193 {
1194 no_gvl_stat_data *arg = ptr;
1195 return (void *)(VALUE)lstat(arg->file.path, arg->st);
1196 }
1197
1198 static int
lstat_without_gvl(const char * path,struct stat * st)1199 lstat_without_gvl(const char *path, struct stat *st)
1200 {
1201 no_gvl_stat_data data;
1202
1203 data.file.path = path;
1204 data.st = st;
1205
1206 return (int)(VALUE)rb_thread_call_without_gvl(no_gvl_lstat, &data,
1207 RUBY_UBF_IO, NULL);
1208 }
1209 #endif /* HAVE_LSTAT */
1210
1211 /*
1212 * call-seq:
1213 * File.lstat(file_name) -> stat
1214 *
1215 * Same as <code>File::stat</code>, but does not follow the last symbolic
1216 * link. Instead, reports on the link itself.
1217 *
1218 * File.symlink("testfile", "link2test") #=> 0
1219 * File.stat("testfile").size #=> 66
1220 * File.lstat("link2test").size #=> 8
1221 * File.stat("link2test").size #=> 66
1222 *
1223 */
1224
1225 static VALUE
rb_file_s_lstat(VALUE klass,VALUE fname)1226 rb_file_s_lstat(VALUE klass, VALUE fname)
1227 {
1228 #ifdef HAVE_LSTAT
1229 struct stat st;
1230
1231 FilePathValue(fname);
1232 fname = rb_str_encode_ospath(fname);
1233 if (lstat_without_gvl(StringValueCStr(fname), &st) == -1) {
1234 rb_sys_fail_path(fname);
1235 }
1236 return rb_stat_new(&st);
1237 #else
1238 return rb_file_s_stat(klass, fname);
1239 #endif
1240 }
1241
1242 /*
1243 * call-seq:
1244 * file.lstat -> stat
1245 *
1246 * Same as <code>IO#stat</code>, but does not follow the last symbolic
1247 * link. Instead, reports on the link itself.
1248 *
1249 * File.symlink("testfile", "link2test") #=> 0
1250 * File.stat("testfile").size #=> 66
1251 * f = File.new("link2test")
1252 * f.lstat.size #=> 8
1253 * f.stat.size #=> 66
1254 */
1255
1256 static VALUE
rb_file_lstat(VALUE obj)1257 rb_file_lstat(VALUE obj)
1258 {
1259 #ifdef HAVE_LSTAT
1260 rb_io_t *fptr;
1261 struct stat st;
1262 VALUE path;
1263
1264 GetOpenFile(obj, fptr);
1265 if (NIL_P(fptr->pathv)) return Qnil;
1266 path = rb_str_encode_ospath(fptr->pathv);
1267 if (lstat_without_gvl(RSTRING_PTR(path), &st) == -1) {
1268 rb_sys_fail_path(fptr->pathv);
1269 }
1270 return rb_stat_new(&st);
1271 #else
1272 return rb_io_stat(obj);
1273 #endif
1274 }
1275
1276 static int
rb_group_member(GETGROUPS_T gid)1277 rb_group_member(GETGROUPS_T gid)
1278 {
1279 #if defined(_WIN32) || !defined(HAVE_GETGROUPS)
1280 return FALSE;
1281 #else
1282 int rv = FALSE;
1283 int groups = 16;
1284 VALUE v = 0;
1285 GETGROUPS_T *gary;
1286 int anum = -1;
1287
1288 if (getgid() == gid || getegid() == gid)
1289 return TRUE;
1290
1291 /*
1292 * On Mac OS X (Mountain Lion), NGROUPS is 16. But libc and kernel
1293 * accept more larger value.
1294 * So we don't trunk NGROUPS anymore.
1295 */
1296 while (groups <= RB_MAX_GROUPS) {
1297 gary = ALLOCV_N(GETGROUPS_T, v, groups);
1298 anum = getgroups(groups, gary);
1299 if (anum != -1 && anum != groups)
1300 break;
1301 groups *= 2;
1302 if (v) {
1303 ALLOCV_END(v);
1304 v = 0;
1305 }
1306 }
1307 if (anum == -1)
1308 return FALSE;
1309
1310 while (--anum >= 0) {
1311 if (gary[anum] == gid) {
1312 rv = TRUE;
1313 break;
1314 }
1315 }
1316 if (v)
1317 ALLOCV_END(v);
1318
1319 return rv;
1320 #endif
1321 }
1322
1323 #ifndef S_IXUGO
1324 # define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
1325 #endif
1326
1327 #if defined(S_IXGRP) && !defined(_WIN32) && !defined(__CYGWIN__)
1328 #define USE_GETEUID 1
1329 #endif
1330
1331 #ifndef HAVE_EACCESS
1332 int
eaccess(const char * path,int mode)1333 eaccess(const char *path, int mode)
1334 {
1335 #ifdef USE_GETEUID
1336 struct stat st;
1337 rb_uid_t euid;
1338
1339 euid = geteuid();
1340
1341 /* no setuid nor setgid. run shortcut. */
1342 if (getuid() == euid && getgid() == getegid())
1343 return access(path, mode);
1344
1345 if (STAT(path, &st) < 0)
1346 return -1;
1347
1348 if (euid == 0) {
1349 /* Root can read or write any file. */
1350 if (!(mode & X_OK))
1351 return 0;
1352
1353 /* Root can execute any file that has any one of the execute
1354 bits set. */
1355 if (st.st_mode & S_IXUGO)
1356 return 0;
1357
1358 return -1;
1359 }
1360
1361 if (st.st_uid == euid) /* owner */
1362 mode <<= 6;
1363 else if (rb_group_member(st.st_gid))
1364 mode <<= 3;
1365
1366 if ((int)(st.st_mode & mode) == mode) return 0;
1367
1368 return -1;
1369 #else
1370 return access(path, mode);
1371 #endif
1372 }
1373 #endif
1374
1375 struct access_arg {
1376 const char *path;
1377 int mode;
1378 };
1379
1380 static void *
nogvl_eaccess(void * ptr)1381 nogvl_eaccess(void *ptr)
1382 {
1383 struct access_arg *aa = ptr;
1384
1385 return (void *)(VALUE)eaccess(aa->path, aa->mode);
1386 }
1387
1388 static int
rb_eaccess(VALUE fname,int mode)1389 rb_eaccess(VALUE fname, int mode)
1390 {
1391 struct access_arg aa;
1392
1393 FilePathValue(fname);
1394 fname = rb_str_encode_ospath(fname);
1395 aa.path = StringValueCStr(fname);
1396 aa.mode = mode;
1397
1398 return (int)(VALUE)rb_thread_call_without_gvl(nogvl_eaccess, &aa,
1399 RUBY_UBF_IO, 0);
1400 }
1401
1402 static void *
nogvl_access(void * ptr)1403 nogvl_access(void *ptr)
1404 {
1405 struct access_arg *aa = ptr;
1406
1407 return (void *)(VALUE)access(aa->path, aa->mode);
1408 }
1409
1410 static int
rb_access(VALUE fname,int mode)1411 rb_access(VALUE fname, int mode)
1412 {
1413 struct access_arg aa;
1414
1415 FilePathValue(fname);
1416 fname = rb_str_encode_ospath(fname);
1417 aa.path = StringValueCStr(fname);
1418 aa.mode = mode;
1419
1420 return (int)(VALUE)rb_thread_call_without_gvl(nogvl_access, &aa,
1421 RUBY_UBF_IO, 0);
1422 }
1423
1424 /*
1425 * Document-class: FileTest
1426 *
1427 * <code>FileTest</code> implements file test operations similar to
1428 * those used in <code>File::Stat</code>. It exists as a standalone
1429 * module, and its methods are also insinuated into the <code>File</code>
1430 * class. (Note that this is not done by inclusion: the interpreter cheats).
1431 *
1432 */
1433
1434 /*
1435 * Document-method: directory?
1436 *
1437 * call-seq:
1438 * File.directory?(file_name) -> true or false
1439 *
1440 * Returns <code>true</code> if the named file is a directory,
1441 * or a symlink that points at a directory, and <code>false</code>
1442 * otherwise.
1443 *
1444 * _file_name_ can be an IO object.
1445 *
1446 * File.directory?(".")
1447 */
1448
1449 VALUE
rb_file_directory_p(VALUE obj,VALUE fname)1450 rb_file_directory_p(VALUE obj, VALUE fname)
1451 {
1452 #ifndef S_ISDIR
1453 # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
1454 #endif
1455
1456 struct stat st;
1457
1458 if (rb_stat(fname, &st) < 0) return Qfalse;
1459 if (S_ISDIR(st.st_mode)) return Qtrue;
1460 return Qfalse;
1461 }
1462
1463 /*
1464 * call-seq:
1465 * File.pipe?(file_name) -> true or false
1466 *
1467 * Returns <code>true</code> if the named file is a pipe.
1468 *
1469 * _file_name_ can be an IO object.
1470 */
1471
1472 static VALUE
rb_file_pipe_p(VALUE obj,VALUE fname)1473 rb_file_pipe_p(VALUE obj, VALUE fname)
1474 {
1475 #ifdef S_IFIFO
1476 # ifndef S_ISFIFO
1477 # define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
1478 # endif
1479
1480 struct stat st;
1481
1482 if (rb_stat(fname, &st) < 0) return Qfalse;
1483 if (S_ISFIFO(st.st_mode)) return Qtrue;
1484
1485 #endif
1486 return Qfalse;
1487 }
1488
1489 /*
1490 * call-seq:
1491 * File.symlink?(file_name) -> true or false
1492 *
1493 * Returns <code>true</code> if the named file is a symbolic link.
1494 */
1495
1496 static VALUE
rb_file_symlink_p(VALUE obj,VALUE fname)1497 rb_file_symlink_p(VALUE obj, VALUE fname)
1498 {
1499 #ifndef S_ISLNK
1500 # ifdef _S_ISLNK
1501 # define S_ISLNK(m) _S_ISLNK(m)
1502 # else
1503 # ifdef _S_IFLNK
1504 # define S_ISLNK(m) (((m) & S_IFMT) == _S_IFLNK)
1505 # else
1506 # ifdef S_IFLNK
1507 # define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
1508 # endif
1509 # endif
1510 # endif
1511 #endif
1512
1513 #ifdef S_ISLNK
1514 struct stat st;
1515
1516 FilePathValue(fname);
1517 fname = rb_str_encode_ospath(fname);
1518 if (lstat_without_gvl(StringValueCStr(fname), &st) < 0) return Qfalse;
1519 if (S_ISLNK(st.st_mode)) return Qtrue;
1520 #endif
1521
1522 return Qfalse;
1523 }
1524
1525 /*
1526 * call-seq:
1527 * File.socket?(file_name) -> true or false
1528 *
1529 * Returns <code>true</code> if the named file is a socket.
1530 *
1531 * _file_name_ can be an IO object.
1532 */
1533
1534 static VALUE
rb_file_socket_p(VALUE obj,VALUE fname)1535 rb_file_socket_p(VALUE obj, VALUE fname)
1536 {
1537 #ifndef S_ISSOCK
1538 # ifdef _S_ISSOCK
1539 # define S_ISSOCK(m) _S_ISSOCK(m)
1540 # else
1541 # ifdef _S_IFSOCK
1542 # define S_ISSOCK(m) (((m) & S_IFMT) == _S_IFSOCK)
1543 # else
1544 # ifdef S_IFSOCK
1545 # define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
1546 # endif
1547 # endif
1548 # endif
1549 #endif
1550
1551 #ifdef S_ISSOCK
1552 struct stat st;
1553
1554 if (rb_stat(fname, &st) < 0) return Qfalse;
1555 if (S_ISSOCK(st.st_mode)) return Qtrue;
1556
1557 #endif
1558 return Qfalse;
1559 }
1560
1561 /*
1562 * call-seq:
1563 * File.blockdev?(file_name) -> true or false
1564 *
1565 * Returns <code>true</code> if the named file is a block device.
1566 *
1567 * _file_name_ can be an IO object.
1568 */
1569
1570 static VALUE
rb_file_blockdev_p(VALUE obj,VALUE fname)1571 rb_file_blockdev_p(VALUE obj, VALUE fname)
1572 {
1573 #ifndef S_ISBLK
1574 # ifdef S_IFBLK
1575 # define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
1576 # else
1577 # define S_ISBLK(m) (0) /* anytime false */
1578 # endif
1579 #endif
1580
1581 #ifdef S_ISBLK
1582 struct stat st;
1583
1584 if (rb_stat(fname, &st) < 0) return Qfalse;
1585 if (S_ISBLK(st.st_mode)) return Qtrue;
1586
1587 #endif
1588 return Qfalse;
1589 }
1590
1591 /*
1592 * call-seq:
1593 * File.chardev?(file_name) -> true or false
1594 *
1595 * Returns <code>true</code> if the named file is a character device.
1596 *
1597 * _file_name_ can be an IO object.
1598 */
1599 static VALUE
rb_file_chardev_p(VALUE obj,VALUE fname)1600 rb_file_chardev_p(VALUE obj, VALUE fname)
1601 {
1602 #ifndef S_ISCHR
1603 # define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
1604 #endif
1605
1606 struct stat st;
1607
1608 if (rb_stat(fname, &st) < 0) return Qfalse;
1609 if (S_ISCHR(st.st_mode)) return Qtrue;
1610
1611 return Qfalse;
1612 }
1613
1614 /*
1615 * call-seq:
1616 * File.exist?(file_name) -> true or false
1617 *
1618 * Return <code>true</code> if the named file exists.
1619 *
1620 * _file_name_ can be an IO object.
1621 *
1622 * "file exists" means that stat() or fstat() system call is successful.
1623 */
1624
1625 static VALUE
rb_file_exist_p(VALUE obj,VALUE fname)1626 rb_file_exist_p(VALUE obj, VALUE fname)
1627 {
1628 struct stat st;
1629
1630 if (rb_stat(fname, &st) < 0) return Qfalse;
1631 return Qtrue;
1632 }
1633
1634 /*
1635 * call-seq:
1636 * File.exists?(file_name) -> true or false
1637 *
1638 * Deprecated method. Don't use.
1639 */
1640 static VALUE
rb_file_exists_p(VALUE obj,VALUE fname)1641 rb_file_exists_p(VALUE obj, VALUE fname)
1642 {
1643 const char *s = "FileTest#";
1644 if (obj == rb_mFileTest) {
1645 s = "FileTest.";
1646 }
1647 else if (obj == rb_cFile ||
1648 (RB_TYPE_P(obj, T_CLASS) &&
1649 RTEST(rb_class_inherited_p(obj, rb_cFile)))) {
1650 s = "File.";
1651 }
1652 rb_warning("%sexists? is a deprecated name, use %sexist? instead", s, s);
1653 return rb_file_exist_p(obj, fname);
1654 }
1655
1656 /*
1657 * call-seq:
1658 * File.readable?(file_name) -> true or false
1659 *
1660 * Returns <code>true</code> if the named file is readable by the effective
1661 * user and group id of this process. See eaccess(3).
1662 */
1663
1664 static VALUE
rb_file_readable_p(VALUE obj,VALUE fname)1665 rb_file_readable_p(VALUE obj, VALUE fname)
1666 {
1667 if (rb_eaccess(fname, R_OK) < 0) return Qfalse;
1668 return Qtrue;
1669 }
1670
1671 /*
1672 * call-seq:
1673 * File.readable_real?(file_name) -> true or false
1674 *
1675 * Returns <code>true</code> if the named file is readable by the real
1676 * user and group id of this process. See access(3).
1677 */
1678
1679 static VALUE
rb_file_readable_real_p(VALUE obj,VALUE fname)1680 rb_file_readable_real_p(VALUE obj, VALUE fname)
1681 {
1682 if (rb_access(fname, R_OK) < 0) return Qfalse;
1683 return Qtrue;
1684 }
1685
1686 #ifndef S_IRUGO
1687 # define S_IRUGO (S_IRUSR | S_IRGRP | S_IROTH)
1688 #endif
1689
1690 #ifndef S_IWUGO
1691 # define S_IWUGO (S_IWUSR | S_IWGRP | S_IWOTH)
1692 #endif
1693
1694 /*
1695 * call-seq:
1696 * File.world_readable?(file_name) -> integer or nil
1697 *
1698 * If <i>file_name</i> is readable by others, returns an integer
1699 * representing the file permission bits of <i>file_name</i>. Returns
1700 * <code>nil</code> otherwise. The meaning of the bits is platform
1701 * dependent; on Unix systems, see <code>stat(2)</code>.
1702 *
1703 * _file_name_ can be an IO object.
1704 *
1705 * File.world_readable?("/etc/passwd") #=> 420
1706 * m = File.world_readable?("/etc/passwd")
1707 * sprintf("%o", m) #=> "644"
1708 */
1709
1710 static VALUE
rb_file_world_readable_p(VALUE obj,VALUE fname)1711 rb_file_world_readable_p(VALUE obj, VALUE fname)
1712 {
1713 #ifdef S_IROTH
1714 struct stat st;
1715
1716 if (rb_stat(fname, &st) < 0) return Qnil;
1717 if ((st.st_mode & (S_IROTH)) == S_IROTH) {
1718 return UINT2NUM(st.st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
1719 }
1720 #endif
1721 return Qnil;
1722 }
1723
1724 /*
1725 * call-seq:
1726 * File.writable?(file_name) -> true or false
1727 *
1728 * Returns <code>true</code> if the named file is writable by the effective
1729 * user and group id of this process. See eaccess(3).
1730 */
1731
1732 static VALUE
rb_file_writable_p(VALUE obj,VALUE fname)1733 rb_file_writable_p(VALUE obj, VALUE fname)
1734 {
1735 if (rb_eaccess(fname, W_OK) < 0) return Qfalse;
1736 return Qtrue;
1737 }
1738
1739 /*
1740 * call-seq:
1741 * File.writable_real?(file_name) -> true or false
1742 *
1743 * Returns <code>true</code> if the named file is writable by the real
1744 * user and group id of this process. See access(3)
1745 */
1746
1747 static VALUE
rb_file_writable_real_p(VALUE obj,VALUE fname)1748 rb_file_writable_real_p(VALUE obj, VALUE fname)
1749 {
1750 if (rb_access(fname, W_OK) < 0) return Qfalse;
1751 return Qtrue;
1752 }
1753
1754 /*
1755 * call-seq:
1756 * File.world_writable?(file_name) -> integer or nil
1757 *
1758 * If <i>file_name</i> is writable by others, returns an integer
1759 * representing the file permission bits of <i>file_name</i>. Returns
1760 * <code>nil</code> otherwise. The meaning of the bits is platform
1761 * dependent; on Unix systems, see <code>stat(2)</code>.
1762 *
1763 * _file_name_ can be an IO object.
1764 *
1765 * File.world_writable?("/tmp") #=> 511
1766 * m = File.world_writable?("/tmp")
1767 * sprintf("%o", m) #=> "777"
1768 */
1769
1770 static VALUE
rb_file_world_writable_p(VALUE obj,VALUE fname)1771 rb_file_world_writable_p(VALUE obj, VALUE fname)
1772 {
1773 #ifdef S_IWOTH
1774 struct stat st;
1775
1776 if (rb_stat(fname, &st) < 0) return Qnil;
1777 if ((st.st_mode & (S_IWOTH)) == S_IWOTH) {
1778 return UINT2NUM(st.st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
1779 }
1780 #endif
1781 return Qnil;
1782 }
1783
1784 /*
1785 * call-seq:
1786 * File.executable?(file_name) -> true or false
1787 *
1788 * Returns <code>true</code> if the named file is executable by the effective
1789 * user and group id of this process. See eaccess(3).
1790 */
1791
1792 static VALUE
rb_file_executable_p(VALUE obj,VALUE fname)1793 rb_file_executable_p(VALUE obj, VALUE fname)
1794 {
1795 if (rb_eaccess(fname, X_OK) < 0) return Qfalse;
1796 return Qtrue;
1797 }
1798
1799 /*
1800 * call-seq:
1801 * File.executable_real?(file_name) -> true or false
1802 *
1803 * Returns <code>true</code> if the named file is executable by the real
1804 * user and group id of this process. See access(3).
1805 */
1806
1807 static VALUE
rb_file_executable_real_p(VALUE obj,VALUE fname)1808 rb_file_executable_real_p(VALUE obj, VALUE fname)
1809 {
1810 if (rb_access(fname, X_OK) < 0) return Qfalse;
1811 return Qtrue;
1812 }
1813
1814 #ifndef S_ISREG
1815 # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
1816 #endif
1817
1818 /*
1819 * call-seq:
1820 * File.file?(file) -> true or false
1821 *
1822 * Returns +true+ if the named +file+ exists and is a regular file.
1823 *
1824 * +file+ can be an IO object.
1825 *
1826 * If the +file+ argument is a symbolic link, it will resolve the symbolic link
1827 * and use the file referenced by the link.
1828 */
1829
1830 static VALUE
rb_file_file_p(VALUE obj,VALUE fname)1831 rb_file_file_p(VALUE obj, VALUE fname)
1832 {
1833 struct stat st;
1834
1835 if (rb_stat(fname, &st) < 0) return Qfalse;
1836 if (S_ISREG(st.st_mode)) return Qtrue;
1837 return Qfalse;
1838 }
1839
1840 /*
1841 * call-seq:
1842 * File.zero?(file_name) -> true or false
1843 *
1844 * Returns <code>true</code> if the named file exists and has
1845 * a zero size.
1846 *
1847 * _file_name_ can be an IO object.
1848 */
1849
1850 static VALUE
rb_file_zero_p(VALUE obj,VALUE fname)1851 rb_file_zero_p(VALUE obj, VALUE fname)
1852 {
1853 struct stat st;
1854
1855 if (rb_stat(fname, &st) < 0) return Qfalse;
1856 if (st.st_size == 0) return Qtrue;
1857 return Qfalse;
1858 }
1859
1860 /*
1861 * call-seq:
1862 * File.size?(file_name) -> Integer or nil
1863 *
1864 * Returns +nil+ if +file_name+ doesn't exist or has zero size, the size of the
1865 * file otherwise.
1866 *
1867 * _file_name_ can be an IO object.
1868 */
1869
1870 static VALUE
rb_file_size_p(VALUE obj,VALUE fname)1871 rb_file_size_p(VALUE obj, VALUE fname)
1872 {
1873 struct stat st;
1874
1875 if (rb_stat(fname, &st) < 0) return Qnil;
1876 if (st.st_size == 0) return Qnil;
1877 return OFFT2NUM(st.st_size);
1878 }
1879
1880 /*
1881 * call-seq:
1882 * File.owned?(file_name) -> true or false
1883 *
1884 * Returns <code>true</code> if the named file exists and the
1885 * effective used id of the calling process is the owner of
1886 * the file.
1887 *
1888 * _file_name_ can be an IO object.
1889 */
1890
1891 static VALUE
rb_file_owned_p(VALUE obj,VALUE fname)1892 rb_file_owned_p(VALUE obj, VALUE fname)
1893 {
1894 struct stat st;
1895
1896 if (rb_stat(fname, &st) < 0) return Qfalse;
1897 if (st.st_uid == geteuid()) return Qtrue;
1898 return Qfalse;
1899 }
1900
1901 static VALUE
rb_file_rowned_p(VALUE obj,VALUE fname)1902 rb_file_rowned_p(VALUE obj, VALUE fname)
1903 {
1904 struct stat st;
1905
1906 if (rb_stat(fname, &st) < 0) return Qfalse;
1907 if (st.st_uid == getuid()) return Qtrue;
1908 return Qfalse;
1909 }
1910
1911 /*
1912 * call-seq:
1913 * File.grpowned?(file_name) -> true or false
1914 *
1915 * Returns <code>true</code> if the named file exists and the
1916 * effective group id of the calling process is the owner of
1917 * the file. Returns <code>false</code> on Windows.
1918 *
1919 * _file_name_ can be an IO object.
1920 */
1921
1922 static VALUE
rb_file_grpowned_p(VALUE obj,VALUE fname)1923 rb_file_grpowned_p(VALUE obj, VALUE fname)
1924 {
1925 #ifndef _WIN32
1926 struct stat st;
1927
1928 if (rb_stat(fname, &st) < 0) return Qfalse;
1929 if (rb_group_member(st.st_gid)) return Qtrue;
1930 #endif
1931 return Qfalse;
1932 }
1933
1934 #if defined(S_ISUID) || defined(S_ISGID) || defined(S_ISVTX)
1935 static VALUE
check3rdbyte(VALUE fname,int mode)1936 check3rdbyte(VALUE fname, int mode)
1937 {
1938 struct stat st;
1939
1940 if (rb_stat(fname, &st) < 0) return Qfalse;
1941 if (st.st_mode & mode) return Qtrue;
1942 return Qfalse;
1943 }
1944 #endif
1945
1946 /*
1947 * call-seq:
1948 * File.setuid?(file_name) -> true or false
1949 *
1950 * Returns <code>true</code> if the named file has the setuid bit set.
1951 *
1952 * _file_name_ can be an IO object.
1953 */
1954
1955 static VALUE
rb_file_suid_p(VALUE obj,VALUE fname)1956 rb_file_suid_p(VALUE obj, VALUE fname)
1957 {
1958 #ifdef S_ISUID
1959 return check3rdbyte(fname, S_ISUID);
1960 #else
1961 return Qfalse;
1962 #endif
1963 }
1964
1965 /*
1966 * call-seq:
1967 * File.setgid?(file_name) -> true or false
1968 *
1969 * Returns <code>true</code> if the named file has the setgid bit set.
1970 *
1971 * _file_name_ can be an IO object.
1972 */
1973
1974 static VALUE
rb_file_sgid_p(VALUE obj,VALUE fname)1975 rb_file_sgid_p(VALUE obj, VALUE fname)
1976 {
1977 #ifdef S_ISGID
1978 return check3rdbyte(fname, S_ISGID);
1979 #else
1980 return Qfalse;
1981 #endif
1982 }
1983
1984 /*
1985 * call-seq:
1986 * File.sticky?(file_name) -> true or false
1987 *
1988 * Returns <code>true</code> if the named file has the sticky bit set.
1989 *
1990 * _file_name_ can be an IO object.
1991 */
1992
1993 static VALUE
rb_file_sticky_p(VALUE obj,VALUE fname)1994 rb_file_sticky_p(VALUE obj, VALUE fname)
1995 {
1996 #ifdef S_ISVTX
1997 return check3rdbyte(fname, S_ISVTX);
1998 #else
1999 return Qnil;
2000 #endif
2001 }
2002
2003 /*
2004 * call-seq:
2005 * File.identical?(file_1, file_2) -> true or false
2006 *
2007 * Returns <code>true</code> if the named files are identical.
2008 *
2009 * _file_1_ and _file_2_ can be an IO object.
2010 *
2011 * open("a", "w") {}
2012 * p File.identical?("a", "a") #=> true
2013 * p File.identical?("a", "./a") #=> true
2014 * File.link("a", "b")
2015 * p File.identical?("a", "b") #=> true
2016 * File.symlink("a", "c")
2017 * p File.identical?("a", "c") #=> true
2018 * open("d", "w") {}
2019 * p File.identical?("a", "d") #=> false
2020 */
2021
2022 static VALUE
rb_file_identical_p(VALUE obj,VALUE fname1,VALUE fname2)2023 rb_file_identical_p(VALUE obj, VALUE fname1, VALUE fname2)
2024 {
2025 #ifndef _WIN32
2026 struct stat st1, st2;
2027
2028 if (rb_stat(fname1, &st1) < 0) return Qfalse;
2029 if (rb_stat(fname2, &st2) < 0) return Qfalse;
2030 if (st1.st_dev != st2.st_dev) return Qfalse;
2031 if (st1.st_ino != st2.st_ino) return Qfalse;
2032 return Qtrue;
2033 #else
2034 extern VALUE rb_w32_file_identical_p(VALUE, VALUE);
2035 return rb_w32_file_identical_p(fname1, fname2);
2036 #endif
2037 }
2038
2039 /*
2040 * call-seq:
2041 * File.size(file_name) -> integer
2042 *
2043 * Returns the size of <code>file_name</code>.
2044 *
2045 * _file_name_ can be an IO object.
2046 */
2047
2048 static VALUE
rb_file_s_size(VALUE klass,VALUE fname)2049 rb_file_s_size(VALUE klass, VALUE fname)
2050 {
2051 struct stat st;
2052
2053 if (rb_stat(fname, &st) < 0) {
2054 int e = errno;
2055 FilePathValue(fname);
2056 rb_syserr_fail_path(e, fname);
2057 }
2058 return OFFT2NUM(st.st_size);
2059 }
2060
2061 static VALUE
rb_file_ftype(const struct stat * st)2062 rb_file_ftype(const struct stat *st)
2063 {
2064 const char *t;
2065
2066 if (S_ISREG(st->st_mode)) {
2067 t = "file";
2068 }
2069 else if (S_ISDIR(st->st_mode)) {
2070 t = "directory";
2071 }
2072 else if (S_ISCHR(st->st_mode)) {
2073 t = "characterSpecial";
2074 }
2075 #ifdef S_ISBLK
2076 else if (S_ISBLK(st->st_mode)) {
2077 t = "blockSpecial";
2078 }
2079 #endif
2080 #ifdef S_ISFIFO
2081 else if (S_ISFIFO(st->st_mode)) {
2082 t = "fifo";
2083 }
2084 #endif
2085 #ifdef S_ISLNK
2086 else if (S_ISLNK(st->st_mode)) {
2087 t = "link";
2088 }
2089 #endif
2090 #ifdef S_ISSOCK
2091 else if (S_ISSOCK(st->st_mode)) {
2092 t = "socket";
2093 }
2094 #endif
2095 else {
2096 t = "unknown";
2097 }
2098
2099 return rb_usascii_str_new2(t);
2100 }
2101
2102 /*
2103 * call-seq:
2104 * File.ftype(file_name) -> string
2105 *
2106 * Identifies the type of the named file; the return string is one of
2107 * ``<code>file</code>'', ``<code>directory</code>'',
2108 * ``<code>characterSpecial</code>'', ``<code>blockSpecial</code>'',
2109 * ``<code>fifo</code>'', ``<code>link</code>'',
2110 * ``<code>socket</code>'', or ``<code>unknown</code>''.
2111 *
2112 * File.ftype("testfile") #=> "file"
2113 * File.ftype("/dev/tty") #=> "characterSpecial"
2114 * File.ftype("/tmp/.X11-unix/X0") #=> "socket"
2115 */
2116
2117 static VALUE
rb_file_s_ftype(VALUE klass,VALUE fname)2118 rb_file_s_ftype(VALUE klass, VALUE fname)
2119 {
2120 struct stat st;
2121
2122 FilePathValue(fname);
2123 fname = rb_str_encode_ospath(fname);
2124 if (lstat_without_gvl(StringValueCStr(fname), &st) == -1) {
2125 rb_sys_fail_path(fname);
2126 }
2127
2128 return rb_file_ftype(&st);
2129 }
2130
2131 /*
2132 * call-seq:
2133 * File.atime(file_name) -> time
2134 *
2135 * Returns the last access time for the named file as a Time object.
2136 *
2137 * _file_name_ can be an IO object.
2138 *
2139 * File.atime("testfile") #=> Wed Apr 09 08:51:48 CDT 2003
2140 *
2141 */
2142
2143 static VALUE
rb_file_s_atime(VALUE klass,VALUE fname)2144 rb_file_s_atime(VALUE klass, VALUE fname)
2145 {
2146 struct stat st;
2147
2148 if (rb_stat(fname, &st) < 0) {
2149 int e = errno;
2150 FilePathValue(fname);
2151 rb_syserr_fail_path(e, fname);
2152 }
2153 return stat_atime(&st);
2154 }
2155
2156 /*
2157 * call-seq:
2158 * file.atime -> time
2159 *
2160 * Returns the last access time (a <code>Time</code> object)
2161 * for <i>file</i>, or epoch if <i>file</i> has not been accessed.
2162 *
2163 * File.new("testfile").atime #=> Wed Dec 31 18:00:00 CST 1969
2164 *
2165 */
2166
2167 static VALUE
rb_file_atime(VALUE obj)2168 rb_file_atime(VALUE obj)
2169 {
2170 rb_io_t *fptr;
2171 struct stat st;
2172
2173 GetOpenFile(obj, fptr);
2174 if (fstat(fptr->fd, &st) == -1) {
2175 rb_sys_fail_path(fptr->pathv);
2176 }
2177 return stat_atime(&st);
2178 }
2179
2180 /*
2181 * call-seq:
2182 * File.mtime(file_name) -> time
2183 *
2184 * Returns the modification time for the named file as a Time object.
2185 *
2186 * _file_name_ can be an IO object.
2187 *
2188 * File.mtime("testfile") #=> Tue Apr 08 12:58:04 CDT 2003
2189 *
2190 */
2191
2192 static VALUE
rb_file_s_mtime(VALUE klass,VALUE fname)2193 rb_file_s_mtime(VALUE klass, VALUE fname)
2194 {
2195 struct stat st;
2196
2197 if (rb_stat(fname, &st) < 0) {
2198 int e = errno;
2199 FilePathValue(fname);
2200 rb_syserr_fail_path(e, fname);
2201 }
2202 return stat_mtime(&st);
2203 }
2204
2205 /*
2206 * call-seq:
2207 * file.mtime -> time
2208 *
2209 * Returns the modification time for <i>file</i>.
2210 *
2211 * File.new("testfile").mtime #=> Wed Apr 09 08:53:14 CDT 2003
2212 *
2213 */
2214
2215 static VALUE
rb_file_mtime(VALUE obj)2216 rb_file_mtime(VALUE obj)
2217 {
2218 rb_io_t *fptr;
2219 struct stat st;
2220
2221 GetOpenFile(obj, fptr);
2222 if (fstat(fptr->fd, &st) == -1) {
2223 rb_sys_fail_path(fptr->pathv);
2224 }
2225 return stat_mtime(&st);
2226 }
2227
2228 /*
2229 * call-seq:
2230 * File.ctime(file_name) -> time
2231 *
2232 * Returns the change time for the named file (the time at which
2233 * directory information about the file was changed, not the file
2234 * itself).
2235 *
2236 * _file_name_ can be an IO object.
2237 *
2238 * Note that on Windows (NTFS), returns creation time (birth time).
2239 *
2240 * File.ctime("testfile") #=> Wed Apr 09 08:53:13 CDT 2003
2241 *
2242 */
2243
2244 static VALUE
rb_file_s_ctime(VALUE klass,VALUE fname)2245 rb_file_s_ctime(VALUE klass, VALUE fname)
2246 {
2247 struct stat st;
2248
2249 if (rb_stat(fname, &st) < 0) {
2250 int e = errno;
2251 FilePathValue(fname);
2252 rb_syserr_fail_path(e, fname);
2253 }
2254 return stat_ctime(&st);
2255 }
2256
2257 /*
2258 * call-seq:
2259 * file.ctime -> time
2260 *
2261 * Returns the change time for <i>file</i> (that is, the time directory
2262 * information about the file was changed, not the file itself).
2263 *
2264 * Note that on Windows (NTFS), returns creation time (birth time).
2265 *
2266 * File.new("testfile").ctime #=> Wed Apr 09 08:53:14 CDT 2003
2267 *
2268 */
2269
2270 static VALUE
rb_file_ctime(VALUE obj)2271 rb_file_ctime(VALUE obj)
2272 {
2273 rb_io_t *fptr;
2274 struct stat st;
2275
2276 GetOpenFile(obj, fptr);
2277 if (fstat(fptr->fd, &st) == -1) {
2278 rb_sys_fail_path(fptr->pathv);
2279 }
2280 return stat_ctime(&st);
2281 }
2282
2283 #if defined(HAVE_STAT_BIRTHTIME)
2284 /*
2285 * call-seq:
2286 * File.birthtime(file_name) -> time
2287 *
2288 * Returns the birth time for the named file.
2289 *
2290 * _file_name_ can be an IO object.
2291 *
2292 * File.birthtime("testfile") #=> Wed Apr 09 08:53:13 CDT 2003
2293 *
2294 * If the platform doesn't have birthtime, raises NotImplementedError.
2295 *
2296 */
2297
2298 static VALUE
rb_file_s_birthtime(VALUE klass,VALUE fname)2299 rb_file_s_birthtime(VALUE klass, VALUE fname)
2300 {
2301 struct stat st;
2302
2303 if (rb_stat(fname, &st) < 0) {
2304 int e = errno;
2305 FilePathValue(fname);
2306 rb_syserr_fail_path(e, fname);
2307 }
2308 return stat_birthtime(&st);
2309 }
2310 #else
2311 # define rb_file_s_birthtime rb_f_notimplement
2312 #endif
2313
2314 #if defined(HAVE_STAT_BIRTHTIME)
2315 /*
2316 * call-seq:
2317 * file.birthtime -> time
2318 *
2319 * Returns the birth time for <i>file</i>.
2320 *
2321 * File.new("testfile").birthtime #=> Wed Apr 09 08:53:14 CDT 2003
2322 *
2323 * If the platform doesn't have birthtime, raises NotImplementedError.
2324 *
2325 */
2326
2327 static VALUE
rb_file_birthtime(VALUE obj)2328 rb_file_birthtime(VALUE obj)
2329 {
2330 rb_io_t *fptr;
2331 struct stat st;
2332
2333 GetOpenFile(obj, fptr);
2334 if (fstat(fptr->fd, &st) == -1) {
2335 rb_sys_fail_path(fptr->pathv);
2336 }
2337 return stat_birthtime(&st);
2338 }
2339 #else
2340 # define rb_file_birthtime rb_f_notimplement
2341 #endif
2342
2343 /*
2344 * call-seq:
2345 * file.size -> integer
2346 *
2347 * Returns the size of <i>file</i> in bytes.
2348 *
2349 * File.new("testfile").size #=> 66
2350 *
2351 */
2352
2353 static VALUE
rb_file_size(VALUE obj)2354 rb_file_size(VALUE obj)
2355 {
2356 rb_io_t *fptr;
2357 struct stat st;
2358
2359 GetOpenFile(obj, fptr);
2360 if (fptr->mode & FMODE_WRITABLE) {
2361 rb_io_flush_raw(obj, 0);
2362 }
2363 if (fstat(fptr->fd, &st) == -1) {
2364 rb_sys_fail_path(fptr->pathv);
2365 }
2366 return OFFT2NUM(st.st_size);
2367 }
2368
2369 static int
chmod_internal(const char * path,void * mode)2370 chmod_internal(const char *path, void *mode)
2371 {
2372 return chmod(path, *(mode_t *)mode);
2373 }
2374
2375 /*
2376 * call-seq:
2377 * File.chmod(mode_int, file_name, ... ) -> integer
2378 *
2379 * Changes permission bits on the named file(s) to the bit pattern
2380 * represented by <i>mode_int</i>. Actual effects are operating system
2381 * dependent (see the beginning of this section). On Unix systems, see
2382 * <code>chmod(2)</code> for details. Returns the number of files
2383 * processed.
2384 *
2385 * File.chmod(0644, "testfile", "out") #=> 2
2386 */
2387
2388 static VALUE
rb_file_s_chmod(int argc,VALUE * argv)2389 rb_file_s_chmod(int argc, VALUE *argv)
2390 {
2391 mode_t mode;
2392
2393 apply2args(1);
2394 mode = NUM2MODET(*argv++);
2395
2396 return apply2files(chmod_internal, argc, argv, &mode);
2397 }
2398
2399 /*
2400 * call-seq:
2401 * file.chmod(mode_int) -> 0
2402 *
2403 * Changes permission bits on <i>file</i> to the bit pattern
2404 * represented by <i>mode_int</i>. Actual effects are platform
2405 * dependent; on Unix systems, see <code>chmod(2)</code> for details.
2406 * Follows symbolic links. Also see <code>File#lchmod</code>.
2407 *
2408 * f = File.new("out", "w");
2409 * f.chmod(0644) #=> 0
2410 */
2411
2412 static VALUE
rb_file_chmod(VALUE obj,VALUE vmode)2413 rb_file_chmod(VALUE obj, VALUE vmode)
2414 {
2415 rb_io_t *fptr;
2416 mode_t mode;
2417 #if !defined HAVE_FCHMOD || !HAVE_FCHMOD
2418 VALUE path;
2419 #endif
2420
2421 mode = NUM2MODET(vmode);
2422
2423 GetOpenFile(obj, fptr);
2424 #ifdef HAVE_FCHMOD
2425 if (fchmod(fptr->fd, mode) == -1) {
2426 if (HAVE_FCHMOD || errno != ENOSYS)
2427 rb_sys_fail_path(fptr->pathv);
2428 }
2429 else {
2430 if (!HAVE_FCHMOD) return INT2FIX(0);
2431 }
2432 #endif
2433 #if !defined HAVE_FCHMOD || !HAVE_FCHMOD
2434 if (NIL_P(fptr->pathv)) return Qnil;
2435 path = rb_str_encode_ospath(fptr->pathv);
2436 if (chmod(RSTRING_PTR(path), mode) == -1)
2437 rb_sys_fail_path(fptr->pathv);
2438 #endif
2439
2440 return INT2FIX(0);
2441 }
2442
2443 #if defined(HAVE_LCHMOD)
2444 static int
lchmod_internal(const char * path,void * mode)2445 lchmod_internal(const char *path, void *mode)
2446 {
2447 return lchmod(path, *(mode_t *)mode);
2448 }
2449
2450 /*
2451 * call-seq:
2452 * File.lchmod(mode_int, file_name, ...) -> integer
2453 *
2454 * Equivalent to <code>File::chmod</code>, but does not follow symbolic
2455 * links (so it will change the permissions associated with the link,
2456 * not the file referenced by the link). Often not available.
2457 *
2458 */
2459
2460 static VALUE
rb_file_s_lchmod(int argc,VALUE * argv)2461 rb_file_s_lchmod(int argc, VALUE *argv)
2462 {
2463 mode_t mode;
2464
2465 apply2args(1);
2466 mode = NUM2MODET(*argv++);
2467
2468 return apply2files(lchmod_internal, argc, argv, &mode);
2469 }
2470 #else
2471 #define rb_file_s_lchmod rb_f_notimplement
2472 #endif
2473
2474 static inline rb_uid_t
to_uid(VALUE u)2475 to_uid(VALUE u)
2476 {
2477 if (NIL_P(u)) {
2478 return (rb_uid_t)-1;
2479 }
2480 return NUM2UIDT(u);
2481 }
2482
2483 static inline rb_gid_t
to_gid(VALUE g)2484 to_gid(VALUE g)
2485 {
2486 if (NIL_P(g)) {
2487 return (rb_gid_t)-1;
2488 }
2489 return NUM2GIDT(g);
2490 }
2491
2492 struct chown_args {
2493 rb_uid_t owner;
2494 rb_gid_t group;
2495 };
2496
2497 static int
chown_internal(const char * path,void * arg)2498 chown_internal(const char *path, void *arg)
2499 {
2500 struct chown_args *args = arg;
2501 return chown(path, args->owner, args->group);
2502 }
2503
2504 /*
2505 * call-seq:
2506 * File.chown(owner_int, group_int, file_name, ...) -> integer
2507 *
2508 * Changes the owner and group of the named file(s) to the given
2509 * numeric owner and group id's. Only a process with superuser
2510 * privileges may change the owner of a file. The current owner of a
2511 * file may change the file's group to any group to which the owner
2512 * belongs. A <code>nil</code> or -1 owner or group id is ignored.
2513 * Returns the number of files processed.
2514 *
2515 * File.chown(nil, 100, "testfile")
2516 *
2517 */
2518
2519 static VALUE
rb_file_s_chown(int argc,VALUE * argv)2520 rb_file_s_chown(int argc, VALUE *argv)
2521 {
2522 struct chown_args arg;
2523
2524 apply2args(2);
2525 arg.owner = to_uid(*argv++);
2526 arg.group = to_gid(*argv++);
2527
2528 return apply2files(chown_internal, argc, argv, &arg);
2529 }
2530
2531 /*
2532 * call-seq:
2533 * file.chown(owner_int, group_int ) -> 0
2534 *
2535 * Changes the owner and group of <i>file</i> to the given numeric
2536 * owner and group id's. Only a process with superuser privileges may
2537 * change the owner of a file. The current owner of a file may change
2538 * the file's group to any group to which the owner belongs. A
2539 * <code>nil</code> or -1 owner or group id is ignored. Follows
2540 * symbolic links. See also <code>File#lchown</code>.
2541 *
2542 * File.new("testfile").chown(502, 1000)
2543 *
2544 */
2545
2546 static VALUE
rb_file_chown(VALUE obj,VALUE owner,VALUE group)2547 rb_file_chown(VALUE obj, VALUE owner, VALUE group)
2548 {
2549 rb_io_t *fptr;
2550 rb_uid_t o;
2551 rb_gid_t g;
2552 #ifndef HAVE_FCHOWN
2553 VALUE path;
2554 #endif
2555
2556 o = to_uid(owner);
2557 g = to_gid(group);
2558 GetOpenFile(obj, fptr);
2559 #ifndef HAVE_FCHOWN
2560 if (NIL_P(fptr->pathv)) return Qnil;
2561 path = rb_str_encode_ospath(fptr->pathv);
2562 if (chown(RSTRING_PTR(path), o, g) == -1)
2563 rb_sys_fail_path(fptr->pathv);
2564 #else
2565 if (fchown(fptr->fd, o, g) == -1)
2566 rb_sys_fail_path(fptr->pathv);
2567 #endif
2568
2569 return INT2FIX(0);
2570 }
2571
2572 #if defined(HAVE_LCHOWN)
2573 static int
lchown_internal(const char * path,void * arg)2574 lchown_internal(const char *path, void *arg)
2575 {
2576 struct chown_args *args = arg;
2577 return lchown(path, args->owner, args->group);
2578 }
2579
2580 /*
2581 * call-seq:
2582 * File.lchown(owner_int, group_int, file_name,..) -> integer
2583 *
2584 * Equivalent to <code>File::chown</code>, but does not follow symbolic
2585 * links (so it will change the owner associated with the link, not the
2586 * file referenced by the link). Often not available. Returns number
2587 * of files in the argument list.
2588 *
2589 */
2590
2591 static VALUE
rb_file_s_lchown(int argc,VALUE * argv)2592 rb_file_s_lchown(int argc, VALUE *argv)
2593 {
2594 struct chown_args arg;
2595
2596 apply2args(2);
2597 arg.owner = to_uid(*argv++);
2598 arg.group = to_gid(*argv++);
2599
2600 return apply2files(lchown_internal, argc, argv, &arg);
2601 }
2602 #else
2603 #define rb_file_s_lchown rb_f_notimplement
2604 #endif
2605
2606 struct utime_args {
2607 const struct timespec* tsp;
2608 VALUE atime, mtime;
2609 int follow; /* Whether to act on symlinks (1) or their referent (0) */
2610 };
2611
2612 #ifdef UTIME_EINVAL
2613 NORETURN(static void utime_failed(struct apply_arg *));
2614
2615 static void
utime_failed(struct apply_arg * aa)2616 utime_failed(struct apply_arg *aa)
2617 {
2618 int e = aa->errnum;
2619 VALUE path = aa->fn[aa->i].path;
2620 struct utime_args *ua = aa->arg;
2621
2622 if (ua->tsp && e == EINVAL) {
2623 VALUE e[2], a = Qnil, m = Qnil;
2624 int d = 0;
2625 VALUE atime = ua->atime;
2626 VALUE mtime = ua->mtime;
2627
2628 if (!NIL_P(atime)) {
2629 a = rb_inspect(atime);
2630 }
2631 if (!NIL_P(mtime) && mtime != atime && !rb_equal(atime, mtime)) {
2632 m = rb_inspect(mtime);
2633 }
2634 if (NIL_P(a)) e[0] = m;
2635 else if (NIL_P(m) || rb_str_cmp(a, m) == 0) e[0] = a;
2636 else {
2637 e[0] = rb_str_plus(a, rb_str_new_cstr(" or "));
2638 rb_str_append(e[0], m);
2639 d = 1;
2640 }
2641 if (!NIL_P(e[0])) {
2642 if (path) {
2643 if (!d) e[0] = rb_str_dup(e[0]);
2644 rb_str_append(rb_str_cat2(e[0], " for "), path);
2645 }
2646 e[1] = INT2FIX(EINVAL);
2647 rb_exc_raise(rb_class_new_instance(2, e, rb_eSystemCallError));
2648 }
2649 }
2650 rb_syserr_fail_path(e, path);
2651 }
2652 #endif
2653
2654 #if defined(HAVE_UTIMES)
2655
2656 static int
utime_internal(const char * path,void * arg)2657 utime_internal(const char *path, void *arg)
2658 {
2659 struct utime_args *v = arg;
2660 const struct timespec *tsp = v->tsp;
2661 struct timeval tvbuf[2], *tvp = NULL;
2662
2663 #if defined(HAVE_UTIMENSAT)
2664 static int try_utimensat = 1;
2665 # ifdef AT_SYMLINK_NOFOLLOW
2666 static int try_utimensat_follow = 1;
2667 # else
2668 const int try_utimensat_follow = 0;
2669 # endif
2670 int flags = 0;
2671
2672 if (v->follow ? try_utimensat_follow : try_utimensat) {
2673 # ifdef AT_SYMLINK_NOFOLLOW
2674 if (v->follow) {
2675 flags = AT_SYMLINK_NOFOLLOW;
2676 }
2677 # endif
2678
2679 if (utimensat(AT_FDCWD, path, tsp, flags) < 0) {
2680 if (errno == ENOSYS) {
2681 # ifdef AT_SYMLINK_NOFOLLOW
2682 try_utimensat_follow = 0;
2683 # endif
2684 if (!v->follow)
2685 try_utimensat = 0;
2686 goto no_utimensat;
2687 }
2688 return -1; /* calls utime_failed */
2689 }
2690 return 0;
2691 }
2692 no_utimensat:
2693 #endif
2694
2695 if (tsp) {
2696 tvbuf[0].tv_sec = tsp[0].tv_sec;
2697 tvbuf[0].tv_usec = (int)(tsp[0].tv_nsec / 1000);
2698 tvbuf[1].tv_sec = tsp[1].tv_sec;
2699 tvbuf[1].tv_usec = (int)(tsp[1].tv_nsec / 1000);
2700 tvp = tvbuf;
2701 }
2702 #ifdef HAVE_LUTIMES
2703 if (v->follow) return lutimes(path, tvp);
2704 #endif
2705 return utimes(path, tvp);
2706 }
2707
2708 #else
2709
2710 #if !defined HAVE_UTIME_H && !defined HAVE_SYS_UTIME_H
2711 struct utimbuf {
2712 long actime;
2713 long modtime;
2714 };
2715 #endif
2716
2717 static int
utime_internal(const char * path,void * arg)2718 utime_internal(const char *path, void *arg)
2719 {
2720 struct utime_args *v = arg;
2721 const struct timespec *tsp = v->tsp;
2722 struct utimbuf utbuf, *utp = NULL;
2723 if (tsp) {
2724 utbuf.actime = tsp[0].tv_sec;
2725 utbuf.modtime = tsp[1].tv_sec;
2726 utp = &utbuf;
2727 }
2728 return utime(path, utp);
2729 }
2730
2731 #endif
2732
2733 static VALUE
utime_internal_i(int argc,VALUE * argv,int follow)2734 utime_internal_i(int argc, VALUE *argv, int follow)
2735 {
2736 struct utime_args args;
2737 struct timespec tss[2], *tsp = NULL;
2738
2739 apply2args(2);
2740 args.atime = *argv++;
2741 args.mtime = *argv++;
2742
2743 args.follow = follow;
2744
2745 if (!NIL_P(args.atime) || !NIL_P(args.mtime)) {
2746 tsp = tss;
2747 tsp[0] = rb_time_timespec(args.atime);
2748 if (args.atime == args.mtime)
2749 tsp[1] = tsp[0];
2750 else
2751 tsp[1] = rb_time_timespec(args.mtime);
2752 }
2753 args.tsp = tsp;
2754
2755 return apply2files(utime_internal, argc, argv, &args);
2756 }
2757
2758 /*
2759 * call-seq:
2760 * File.utime(atime, mtime, file_name, ...) -> integer
2761 *
2762 * Sets the access and modification times of each named file to the
2763 * first two arguments. If a file is a symlink, this method acts upon
2764 * its referent rather than the link itself; for the inverse
2765 * behavior see File.lutime. Returns the number of file
2766 * names in the argument list.
2767 */
2768
2769 static VALUE
rb_file_s_utime(int argc,VALUE * argv)2770 rb_file_s_utime(int argc, VALUE *argv)
2771 {
2772 return utime_internal_i(argc, argv, FALSE);
2773 }
2774
2775 #if defined(HAVE_UTIMES) && (defined(HAVE_LUTIMES) || (defined(HAVE_UTIMENSAT) && defined(AT_SYMLINK_NOFOLLOW)))
2776
2777 /*
2778 * call-seq:
2779 * File.lutime(atime, mtime, file_name, ...) -> integer
2780 *
2781 * Sets the access and modification times of each named file to the
2782 * first two arguments. If a file is a symlink, this method acts upon
2783 * the link itself as opposed to its referent; for the inverse
2784 * behavior, see File.utime. Returns the number of file
2785 * names in the argument list.
2786 */
2787
2788 static VALUE
rb_file_s_lutime(int argc,VALUE * argv)2789 rb_file_s_lutime(int argc, VALUE *argv)
2790 {
2791 return utime_internal_i(argc, argv, TRUE);
2792 }
2793 #else
2794 #define rb_file_s_lutime rb_f_notimplement
2795 #endif
2796
2797 #ifdef RUBY_FUNCTION_NAME_STRING
2798 # define syserr_fail2(e, s1, s2) syserr_fail2_in(RUBY_FUNCTION_NAME_STRING, e, s1, s2)
2799 #else
2800 # define syserr_fail2_in(func, e, s1, s2) syserr_fail2(e, s1, s2)
2801 #endif
2802 #define sys_fail2(s1, s2) syserr_fail2(errno, s1, s2)
2803 NORETURN(static void syserr_fail2_in(const char *,int,VALUE,VALUE));
2804 static void
syserr_fail2_in(const char * func,int e,VALUE s1,VALUE s2)2805 syserr_fail2_in(const char *func, int e, VALUE s1, VALUE s2)
2806 {
2807 VALUE str;
2808 #ifdef MAX_PATH
2809 const int max_pathlen = MAX_PATH;
2810 #else
2811 const int max_pathlen = MAXPATHLEN;
2812 #endif
2813
2814 if (e == EEXIST) {
2815 rb_syserr_fail_path(e, rb_str_ellipsize(s2, max_pathlen));
2816 }
2817 str = rb_str_new_cstr("(");
2818 rb_str_append(str, rb_str_ellipsize(s1, max_pathlen));
2819 rb_str_cat2(str, ", ");
2820 rb_str_append(str, rb_str_ellipsize(s2, max_pathlen));
2821 rb_str_cat2(str, ")");
2822 #ifdef RUBY_FUNCTION_NAME_STRING
2823 rb_syserr_fail_path_in(func, e, str);
2824 #else
2825 rb_syserr_fail_path(e, str);
2826 #endif
2827 }
2828
2829 #ifdef HAVE_LINK
2830 /*
2831 * call-seq:
2832 * File.link(old_name, new_name) -> 0
2833 *
2834 * Creates a new name for an existing file using a hard link. Will not
2835 * overwrite <i>new_name</i> if it already exists (raising a subclass
2836 * of <code>SystemCallError</code>). Not available on all platforms.
2837 *
2838 * File.link("testfile", ".testfile") #=> 0
2839 * IO.readlines(".testfile")[0] #=> "This is line one\n"
2840 */
2841
2842 static VALUE
rb_file_s_link(VALUE klass,VALUE from,VALUE to)2843 rb_file_s_link(VALUE klass, VALUE from, VALUE to)
2844 {
2845 FilePathValue(from);
2846 FilePathValue(to);
2847 from = rb_str_encode_ospath(from);
2848 to = rb_str_encode_ospath(to);
2849
2850 if (link(StringValueCStr(from), StringValueCStr(to)) < 0) {
2851 sys_fail2(from, to);
2852 }
2853 return INT2FIX(0);
2854 }
2855 #else
2856 #define rb_file_s_link rb_f_notimplement
2857 #endif
2858
2859 #ifdef HAVE_SYMLINK
2860 /*
2861 * call-seq:
2862 * File.symlink(old_name, new_name) -> 0
2863 *
2864 * Creates a symbolic link called <i>new_name</i> for the existing file
2865 * <i>old_name</i>. Raises a <code>NotImplemented</code> exception on
2866 * platforms that do not support symbolic links.
2867 *
2868 * File.symlink("testfile", "link2test") #=> 0
2869 *
2870 */
2871
2872 static VALUE
rb_file_s_symlink(VALUE klass,VALUE from,VALUE to)2873 rb_file_s_symlink(VALUE klass, VALUE from, VALUE to)
2874 {
2875 FilePathValue(from);
2876 FilePathValue(to);
2877 from = rb_str_encode_ospath(from);
2878 to = rb_str_encode_ospath(to);
2879
2880 if (symlink(StringValueCStr(from), StringValueCStr(to)) < 0) {
2881 sys_fail2(from, to);
2882 }
2883 return INT2FIX(0);
2884 }
2885 #else
2886 #define rb_file_s_symlink rb_f_notimplement
2887 #endif
2888
2889 #ifdef HAVE_READLINK
2890 /*
2891 * call-seq:
2892 * File.readlink(link_name) -> file_name
2893 *
2894 * Returns the name of the file referenced by the given link.
2895 * Not available on all platforms.
2896 *
2897 * File.symlink("testfile", "link2test") #=> 0
2898 * File.readlink("link2test") #=> "testfile"
2899 */
2900
2901 static VALUE
rb_file_s_readlink(VALUE klass,VALUE path)2902 rb_file_s_readlink(VALUE klass, VALUE path)
2903 {
2904 return rb_readlink(path, rb_filesystem_encoding());
2905 }
2906
2907 #ifndef _WIN32
2908 struct readlink_arg {
2909 const char *path;
2910 char *buf;
2911 size_t size;
2912 };
2913
2914 static void *
nogvl_readlink(void * ptr)2915 nogvl_readlink(void *ptr)
2916 {
2917 struct readlink_arg *ra = ptr;
2918
2919 return (void *)(VALUE)readlink(ra->path, ra->buf, ra->size);
2920 }
2921
2922 static ssize_t
readlink_without_gvl(VALUE path,VALUE buf,size_t size)2923 readlink_without_gvl(VALUE path, VALUE buf, size_t size)
2924 {
2925 struct readlink_arg ra;
2926
2927 ra.path = RSTRING_PTR(path);
2928 ra.buf = RSTRING_PTR(buf);
2929 ra.size = size;
2930
2931 return (ssize_t)rb_thread_call_without_gvl(nogvl_readlink, &ra,
2932 RUBY_UBF_IO, 0);
2933 }
2934
2935 VALUE
rb_readlink(VALUE path,rb_encoding * enc)2936 rb_readlink(VALUE path, rb_encoding *enc)
2937 {
2938 int size = 100;
2939 ssize_t rv;
2940 VALUE v;
2941
2942 FilePathValue(path);
2943 path = rb_str_encode_ospath(path);
2944 v = rb_enc_str_new(0, size, enc);
2945 while ((rv = readlink_without_gvl(path, v, size)) == size
2946 #ifdef _AIX
2947 || (rv < 0 && errno == ERANGE) /* quirky behavior of GPFS */
2948 #endif
2949 ) {
2950 rb_str_modify_expand(v, size);
2951 size *= 2;
2952 rb_str_set_len(v, size);
2953 }
2954 if (rv < 0) {
2955 int e = errno;
2956 rb_str_resize(v, 0);
2957 rb_syserr_fail_path(e, path);
2958 }
2959 rb_str_resize(v, rv);
2960
2961 return v;
2962 }
2963 #endif
2964 #else
2965 #define rb_file_s_readlink rb_f_notimplement
2966 #endif
2967
2968 static int
unlink_internal(const char * path,void * arg)2969 unlink_internal(const char *path, void *arg)
2970 {
2971 return unlink(path);
2972 }
2973
2974 /*
2975 * call-seq:
2976 * File.delete(file_name, ...) -> integer
2977 * File.unlink(file_name, ...) -> integer
2978 *
2979 * Deletes the named files, returning the number of names
2980 * passed as arguments. Raises an exception on any error.
2981 * Since the underlying implementation relies on the
2982 * <code>unlink(2)</code> system call, the type of
2983 * exception raised depends on its error type (see
2984 * https://linux.die.net/man/2/unlink) and has the form of
2985 * e.g. <code>Errno::ENOENT</code>.
2986 *
2987 * See also <code>Dir::rmdir</code>.
2988 */
2989
2990 static VALUE
rb_file_s_unlink(int argc,VALUE * argv,VALUE klass)2991 rb_file_s_unlink(int argc, VALUE *argv, VALUE klass)
2992 {
2993 return apply2files(unlink_internal, argc, argv, 0);
2994 }
2995
2996 struct rename_args {
2997 const char *src;
2998 const char *dst;
2999 };
3000
3001 static void *
no_gvl_rename(void * ptr)3002 no_gvl_rename(void *ptr)
3003 {
3004 struct rename_args *ra = ptr;
3005
3006 return (void *)(VALUE)rename(ra->src, ra->dst);
3007 }
3008
3009 /*
3010 * call-seq:
3011 * File.rename(old_name, new_name) -> 0
3012 *
3013 * Renames the given file to the new name. Raises a
3014 * <code>SystemCallError</code> if the file cannot be renamed.
3015 *
3016 * File.rename("afile", "afile.bak") #=> 0
3017 */
3018
3019 static VALUE
rb_file_s_rename(VALUE klass,VALUE from,VALUE to)3020 rb_file_s_rename(VALUE klass, VALUE from, VALUE to)
3021 {
3022 struct rename_args ra;
3023 VALUE f, t;
3024
3025 FilePathValue(from);
3026 FilePathValue(to);
3027 f = rb_str_encode_ospath(from);
3028 t = rb_str_encode_ospath(to);
3029 ra.src = StringValueCStr(f);
3030 ra.dst = StringValueCStr(t);
3031 #if defined __CYGWIN__
3032 errno = 0;
3033 #endif
3034 if ((int)(VALUE)rb_thread_call_without_gvl(no_gvl_rename, &ra,
3035 RUBY_UBF_IO, 0) < 0) {
3036 int e = errno;
3037 #if defined DOSISH
3038 switch (e) {
3039 case EEXIST:
3040 if (chmod(ra.dst, 0666) == 0 &&
3041 unlink(ra.dst) == 0 &&
3042 rename(ra.src, ra.dst) == 0)
3043 return INT2FIX(0);
3044 }
3045 #endif
3046 syserr_fail2(e, from, to);
3047 }
3048
3049 return INT2FIX(0);
3050 }
3051
3052 /*
3053 * call-seq:
3054 * File.umask() -> integer
3055 * File.umask(integer) -> integer
3056 *
3057 * Returns the current umask value for this process. If the optional
3058 * argument is given, set the umask to that value and return the
3059 * previous value. Umask values are <em>subtracted</em> from the
3060 * default permissions, so a umask of <code>0222</code> would make a
3061 * file read-only for everyone.
3062 *
3063 * File.umask(0006) #=> 18
3064 * File.umask #=> 6
3065 */
3066
3067 static VALUE
rb_file_s_umask(int argc,VALUE * argv)3068 rb_file_s_umask(int argc, VALUE *argv)
3069 {
3070 mode_t omask = 0;
3071
3072 if (argc == 0) {
3073 omask = umask(0);
3074 umask(omask);
3075 }
3076 else if (argc == 1) {
3077 omask = umask(NUM2MODET(argv[0]));
3078 }
3079 else {
3080 rb_check_arity(argc, 0, 1);
3081 }
3082 return MODET2NUM(omask);
3083 }
3084
3085 #ifdef __CYGWIN__
3086 #undef DOSISH
3087 #endif
3088 #if defined __CYGWIN__ || defined DOSISH
3089 #define DOSISH_UNC
3090 #define DOSISH_DRIVE_LETTER
3091 #define FILE_ALT_SEPARATOR '\\'
3092 #endif
3093 #ifdef FILE_ALT_SEPARATOR
3094 #define isdirsep(x) ((x) == '/' || (x) == FILE_ALT_SEPARATOR)
3095 # ifdef DOSISH
3096 static const char file_alt_separator[] = {FILE_ALT_SEPARATOR, '\0'};
3097 # endif
3098 #else
3099 #define isdirsep(x) ((x) == '/')
3100 #endif
3101
3102 #ifndef USE_NTFS
3103 #if defined _WIN32
3104 #define USE_NTFS 1
3105 #else
3106 #define USE_NTFS 0
3107 #endif
3108 #endif
3109 #ifndef USE_NTFS_ADS
3110 # if USE_NTFS
3111 # define USE_NTFS_ADS 1
3112 # else
3113 # define USE_NTFS_ADS 0
3114 # endif
3115 #endif
3116
3117 #if USE_NTFS
3118 #define istrailinggarbage(x) ((x) == '.' || (x) == ' ')
3119 #else
3120 #define istrailinggarbage(x) 0
3121 #endif
3122 #if USE_NTFS_ADS
3123 # define isADS(x) ((x) == ':')
3124 #else
3125 # define isADS(x) 0
3126 #endif
3127
3128 #define Next(p, e, enc) ((p) + rb_enc_mbclen((p), (e), (enc)))
3129 #define Inc(p, e, enc) ((p) = Next((p), (e), (enc)))
3130
3131 #if defined(DOSISH_UNC)
3132 #define has_unc(buf) (isdirsep((buf)[0]) && isdirsep((buf)[1]))
3133 #else
3134 #define has_unc(buf) 0
3135 #endif
3136
3137 #ifdef DOSISH_DRIVE_LETTER
3138 static inline int
has_drive_letter(const char * buf)3139 has_drive_letter(const char *buf)
3140 {
3141 if (ISALPHA(buf[0]) && buf[1] == ':') {
3142 return 1;
3143 }
3144 else {
3145 return 0;
3146 }
3147 }
3148
3149 #ifndef _WIN32
3150 static char*
getcwdofdrv(int drv)3151 getcwdofdrv(int drv)
3152 {
3153 char drive[4];
3154 char *drvcwd, *oldcwd;
3155
3156 drive[0] = drv;
3157 drive[1] = ':';
3158 drive[2] = '\0';
3159
3160 /* the only way that I know to get the current directory
3161 of a particular drive is to change chdir() to that drive,
3162 so save the old cwd before chdir()
3163 */
3164 oldcwd = ruby_getcwd();
3165 if (chdir(drive) == 0) {
3166 drvcwd = ruby_getcwd();
3167 chdir(oldcwd);
3168 xfree(oldcwd);
3169 }
3170 else {
3171 /* perhaps the drive is not exist. we return only drive letter */
3172 drvcwd = strdup(drive);
3173 }
3174 return drvcwd;
3175 }
3176 #endif
3177
3178 static inline int
not_same_drive(VALUE path,int drive)3179 not_same_drive(VALUE path, int drive)
3180 {
3181 const char *p = RSTRING_PTR(path);
3182 if (RSTRING_LEN(path) < 2) return 0;
3183 if (has_drive_letter(p)) {
3184 return TOLOWER(p[0]) != TOLOWER(drive);
3185 }
3186 else {
3187 return has_unc(p);
3188 }
3189 }
3190 #endif
3191
3192 static inline char *
skiproot(const char * path,const char * end,rb_encoding * enc)3193 skiproot(const char *path, const char *end, rb_encoding *enc)
3194 {
3195 #ifdef DOSISH_DRIVE_LETTER
3196 if (path + 2 <= end && has_drive_letter(path)) path += 2;
3197 #endif
3198 while (path < end && isdirsep(*path)) path++;
3199 return (char *)path;
3200 }
3201
3202 #define nextdirsep rb_enc_path_next
3203 char *
rb_enc_path_next(const char * s,const char * e,rb_encoding * enc)3204 rb_enc_path_next(const char *s, const char *e, rb_encoding *enc)
3205 {
3206 while (s < e && !isdirsep(*s)) {
3207 Inc(s, e, enc);
3208 }
3209 return (char *)s;
3210 }
3211
3212 #if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
3213 #define skipprefix rb_enc_path_skip_prefix
3214 #else
3215 #define skipprefix(path, end, enc) (path)
3216 #endif
3217 char *
rb_enc_path_skip_prefix(const char * path,const char * end,rb_encoding * enc)3218 rb_enc_path_skip_prefix(const char *path, const char *end, rb_encoding *enc)
3219 {
3220 #if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
3221 #ifdef DOSISH_UNC
3222 if (path + 2 <= end && isdirsep(path[0]) && isdirsep(path[1])) {
3223 path += 2;
3224 while (path < end && isdirsep(*path)) path++;
3225 if ((path = rb_enc_path_next(path, end, enc)) < end && path[0] && path[1] && !isdirsep(path[1]))
3226 path = rb_enc_path_next(path + 1, end, enc);
3227 return (char *)path;
3228 }
3229 #endif
3230 #ifdef DOSISH_DRIVE_LETTER
3231 if (has_drive_letter(path))
3232 return (char *)(path + 2);
3233 #endif
3234 #endif
3235 return (char *)path;
3236 }
3237
3238 static inline char *
skipprefixroot(const char * path,const char * end,rb_encoding * enc)3239 skipprefixroot(const char *path, const char *end, rb_encoding *enc)
3240 {
3241 #if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
3242 char *p = skipprefix(path, end, enc);
3243 while (isdirsep(*p)) p++;
3244 return p;
3245 #else
3246 return skiproot(path, end, enc);
3247 #endif
3248 }
3249
3250 #define strrdirsep rb_enc_path_last_separator
3251 char *
rb_enc_path_last_separator(const char * path,const char * end,rb_encoding * enc)3252 rb_enc_path_last_separator(const char *path, const char *end, rb_encoding *enc)
3253 {
3254 char *last = NULL;
3255 while (path < end) {
3256 if (isdirsep(*path)) {
3257 const char *tmp = path++;
3258 while (path < end && isdirsep(*path)) path++;
3259 if (path >= end) break;
3260 last = (char *)tmp;
3261 }
3262 else {
3263 Inc(path, end, enc);
3264 }
3265 }
3266 return last;
3267 }
3268
3269 static char *
chompdirsep(const char * path,const char * end,rb_encoding * enc)3270 chompdirsep(const char *path, const char *end, rb_encoding *enc)
3271 {
3272 while (path < end) {
3273 if (isdirsep(*path)) {
3274 const char *last = path++;
3275 while (path < end && isdirsep(*path)) path++;
3276 if (path >= end) return (char *)last;
3277 }
3278 else {
3279 Inc(path, end, enc);
3280 }
3281 }
3282 return (char *)path;
3283 }
3284
3285 char *
rb_enc_path_end(const char * path,const char * end,rb_encoding * enc)3286 rb_enc_path_end(const char *path, const char *end, rb_encoding *enc)
3287 {
3288 if (path < end && isdirsep(*path)) path++;
3289 return chompdirsep(path, end, enc);
3290 }
3291
3292 #if USE_NTFS
3293 static char *
ntfs_tail(const char * path,const char * end,rb_encoding * enc)3294 ntfs_tail(const char *path, const char *end, rb_encoding *enc)
3295 {
3296 while (path < end && *path == '.') path++;
3297 while (path < end && !isADS(*path)) {
3298 if (istrailinggarbage(*path)) {
3299 const char *last = path++;
3300 while (path < end && istrailinggarbage(*path)) path++;
3301 if (path >= end || isADS(*path)) return (char *)last;
3302 }
3303 else if (isdirsep(*path)) {
3304 const char *last = path++;
3305 while (path < end && isdirsep(*path)) path++;
3306 if (path >= end) return (char *)last;
3307 if (isADS(*path)) path++;
3308 }
3309 else {
3310 Inc(path, end, enc);
3311 }
3312 }
3313 return (char *)path;
3314 }
3315 #endif
3316
3317 #define BUFCHECK(cond) do {\
3318 bdiff = p - buf;\
3319 if (cond) {\
3320 do {buflen *= 2;} while (cond);\
3321 rb_str_resize(result, buflen);\
3322 buf = RSTRING_PTR(result);\
3323 p = buf + bdiff;\
3324 pend = buf + buflen;\
3325 }\
3326 } while (0)
3327
3328 #define BUFINIT() (\
3329 p = buf = RSTRING_PTR(result),\
3330 buflen = RSTRING_LEN(result),\
3331 pend = p + buflen)
3332
3333 #ifdef __APPLE__
3334 # define SKIPPATHSEP(p) ((*(p)) ? 1 : 0)
3335 #else
3336 # define SKIPPATHSEP(p) 1
3337 #endif
3338
3339 #define BUFCOPY(srcptr, srclen) do { \
3340 const int skip = SKIPPATHSEP(p); \
3341 rb_str_set_len(result, p-buf+skip); \
3342 BUFCHECK(bdiff + ((srclen)+skip) >= buflen); \
3343 p += skip; \
3344 memcpy(p, (srcptr), (srclen)); \
3345 p += (srclen); \
3346 } while (0)
3347
3348 #define WITH_ROOTDIFF(stmt) do { \
3349 long rootdiff = root - buf; \
3350 stmt; \
3351 root = buf + rootdiff; \
3352 } while (0)
3353
3354 static VALUE
copy_home_path(VALUE result,const char * dir)3355 copy_home_path(VALUE result, const char *dir)
3356 {
3357 char *buf;
3358 #if defined DOSISH || defined __CYGWIN__
3359 char *p, *bend;
3360 rb_encoding *enc;
3361 #endif
3362 long dirlen;
3363 int encidx;
3364
3365 dirlen = strlen(dir);
3366 rb_str_resize(result, dirlen);
3367 memcpy(buf = RSTRING_PTR(result), dir, dirlen);
3368 encidx = rb_filesystem_encindex();
3369 rb_enc_associate_index(result, encidx);
3370 #if defined DOSISH || defined __CYGWIN__
3371 enc = rb_enc_from_index(encidx);
3372 for (bend = (p = buf) + dirlen; p < bend; Inc(p, bend, enc)) {
3373 if (*p == '\\') {
3374 *p = '/';
3375 }
3376 }
3377 #endif
3378 return result;
3379 }
3380
3381 VALUE
rb_home_dir_of(VALUE user,VALUE result)3382 rb_home_dir_of(VALUE user, VALUE result)
3383 {
3384 #ifdef HAVE_PWD_H
3385 struct passwd *pwPtr;
3386 #else
3387 extern char *getlogin(void);
3388 const char *pwPtr = 0;
3389 # define endpwent() ((void)0)
3390 #endif
3391 const char *dir, *username = RSTRING_PTR(user);
3392 rb_encoding *enc = rb_enc_get(user);
3393 #if defined _WIN32
3394 rb_encoding *fsenc = rb_utf8_encoding();
3395 #else
3396 rb_encoding *fsenc = rb_filesystem_encoding();
3397 #endif
3398 if (enc != fsenc) {
3399 dir = username = RSTRING_PTR(rb_str_conv_enc(user, enc, fsenc));
3400 }
3401
3402 #ifdef HAVE_PWD_H
3403 pwPtr = getpwnam(username);
3404 #else
3405 if (strcasecmp(username, getlogin()) == 0)
3406 dir = pwPtr = getenv("HOME");
3407 #endif
3408 if (!pwPtr) {
3409 endpwent();
3410 rb_raise(rb_eArgError, "user %"PRIsVALUE" doesn't exist", user);
3411 }
3412 #ifdef HAVE_PWD_H
3413 dir = pwPtr->pw_dir;
3414 #endif
3415 copy_home_path(result, dir);
3416 endpwent();
3417 return result;
3418 }
3419
3420 #ifndef _WIN32
3421 VALUE
rb_default_home_dir(VALUE result)3422 rb_default_home_dir(VALUE result)
3423 {
3424 const char *dir = getenv("HOME");
3425
3426 #if defined HAVE_PWD_H
3427 if (!dir) {
3428 /* We'll look up the user's default home dir in the password db by
3429 * login name, if possible, and failing that will fall back to looking
3430 * the information up by uid (as would be needed for processes that
3431 * are not a descendant of login(1) or a work-alike).
3432 *
3433 * While the lookup by uid is more likely to succeed (since we always
3434 * have a uid, but may or may not have a login name), we prefer first
3435 * looking up by name to accommodate the possibility of multiple login
3436 * names (each with its own record in the password database, so each
3437 * with a potentially different home directory) being mapped to the
3438 * same uid (as explicitly allowed for by POSIX; see getlogin(3posix)).
3439 */
3440 VALUE login_name = rb_getlogin();
3441
3442 # if !defined(HAVE_GETPWUID_R) && !defined(HAVE_GETPWUID)
3443 /* This is a corner case, but for backward compatibility reasons we
3444 * want to emit this error if neither the lookup by login name nor
3445 * lookup by getuid() has a chance of succeeding.
3446 */
3447 if (NIL_P(login_name)) {
3448 rb_raise(rb_eArgError, "couldn't find login name -- expanding `~'");
3449 }
3450 # endif
3451
3452 VALUE pw_dir = rb_getpwdirnam_for_login(login_name);
3453 if (NIL_P(pw_dir)) {
3454 pw_dir = rb_getpwdiruid();
3455 if (NIL_P(pw_dir)) {
3456 rb_raise(rb_eArgError, "couldn't find home for uid `%ld'", (long)getuid());
3457 }
3458 }
3459
3460 /* found it */
3461 copy_home_path(result, RSTRING_PTR(pw_dir));
3462 rb_str_resize(pw_dir, 0);
3463 return result;
3464 }
3465 #endif
3466 if (!dir) {
3467 rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'");
3468 }
3469 return copy_home_path(result, dir);
3470 }
3471
3472 static VALUE
ospath_new(const char * ptr,long len,rb_encoding * fsenc)3473 ospath_new(const char *ptr, long len, rb_encoding *fsenc)
3474 {
3475 #if NORMALIZE_UTF8PATH
3476 VALUE path = rb_str_normalize_ospath(ptr, len);
3477 rb_enc_associate(path, fsenc);
3478 return path;
3479 #else
3480 return rb_enc_str_new(ptr, len, fsenc);
3481 #endif
3482 }
3483
3484 static char *
append_fspath(VALUE result,VALUE fname,char * dir,rb_encoding ** enc,rb_encoding * fsenc)3485 append_fspath(VALUE result, VALUE fname, char *dir, rb_encoding **enc, rb_encoding *fsenc)
3486 {
3487 char *buf, *cwdp = dir;
3488 VALUE dirname = Qnil;
3489 size_t dirlen = strlen(dir), buflen = rb_str_capacity(result);
3490
3491 if (NORMALIZE_UTF8PATH || *enc != fsenc) {
3492 rb_encoding *direnc = rb_enc_check(fname, dirname = ospath_new(dir, dirlen, fsenc));
3493 if (direnc != fsenc) {
3494 dirname = rb_str_conv_enc(dirname, fsenc, direnc);
3495 RSTRING_GETMEM(dirname, cwdp, dirlen);
3496 }
3497 else if (NORMALIZE_UTF8PATH) {
3498 RSTRING_GETMEM(dirname, cwdp, dirlen);
3499 }
3500 *enc = direnc;
3501 }
3502 do {buflen *= 2;} while (dirlen > buflen);
3503 rb_str_resize(result, buflen);
3504 buf = RSTRING_PTR(result);
3505 memcpy(buf, cwdp, dirlen);
3506 xfree(dir);
3507 if (!NIL_P(dirname)) rb_str_resize(dirname, 0);
3508 rb_enc_associate(result, *enc);
3509 return buf + dirlen;
3510 }
3511
3512 VALUE
rb_file_expand_path_internal(VALUE fname,VALUE dname,int abs_mode,int long_name,VALUE result)3513 rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_name, VALUE result)
3514 {
3515 const char *s, *b, *fend;
3516 char *buf, *p, *pend, *root;
3517 size_t buflen, bdiff;
3518 int tainted;
3519 rb_encoding *enc, *fsenc = rb_filesystem_encoding();
3520
3521 s = StringValuePtr(fname);
3522 fend = s + RSTRING_LEN(fname);
3523 enc = rb_enc_get(fname);
3524 BUFINIT();
3525 tainted = OBJ_TAINTED(fname);
3526
3527 if (s[0] == '~' && abs_mode == 0) { /* execute only if NOT absolute_path() */
3528 long userlen = 0;
3529 tainted = 1;
3530 if (isdirsep(s[1]) || s[1] == '\0') {
3531 buf = 0;
3532 b = 0;
3533 rb_str_set_len(result, 0);
3534 if (*++s) ++s;
3535 rb_default_home_dir(result);
3536 }
3537 else {
3538 s = nextdirsep(b = s, fend, enc);
3539 b++; /* b[0] is '~' */
3540 userlen = s - b;
3541 BUFCHECK(bdiff + userlen >= buflen);
3542 memcpy(p, b, userlen);
3543 ENC_CODERANGE_CLEAR(result);
3544 rb_str_set_len(result, userlen);
3545 rb_enc_associate(result, enc);
3546 rb_home_dir_of(result, result);
3547 buf = p + 1;
3548 p += userlen;
3549 }
3550 if (!rb_is_absolute_path(RSTRING_PTR(result))) {
3551 if (userlen) {
3552 rb_enc_raise(enc, rb_eArgError, "non-absolute home of %.*s%.0"PRIsVALUE,
3553 (int)userlen, b, fname);
3554 }
3555 else {
3556 rb_raise(rb_eArgError, "non-absolute home");
3557 }
3558 }
3559 BUFINIT();
3560 p = pend;
3561 }
3562 #ifdef DOSISH_DRIVE_LETTER
3563 /* skip drive letter */
3564 else if (has_drive_letter(s)) {
3565 if (isdirsep(s[2])) {
3566 /* specified drive letter, and full path */
3567 /* skip drive letter */
3568 BUFCHECK(bdiff + 2 >= buflen);
3569 memcpy(p, s, 2);
3570 p += 2;
3571 s += 2;
3572 rb_enc_copy(result, fname);
3573 }
3574 else {
3575 /* specified drive, but not full path */
3576 int same = 0;
3577 if (!NIL_P(dname) && !not_same_drive(dname, s[0])) {
3578 rb_file_expand_path_internal(dname, Qnil, abs_mode, long_name, result);
3579 BUFINIT();
3580 if (has_drive_letter(p) && TOLOWER(p[0]) == TOLOWER(s[0])) {
3581 /* ok, same drive */
3582 same = 1;
3583 }
3584 }
3585 if (!same) {
3586 char *e = append_fspath(result, fname, getcwdofdrv(*s), &enc, fsenc);
3587 tainted = 1;
3588 BUFINIT();
3589 p = e;
3590 }
3591 else {
3592 rb_enc_associate(result, enc = rb_enc_check(result, fname));
3593 p = pend;
3594 }
3595 p = chompdirsep(skiproot(buf, p, enc), p, enc);
3596 s += 2;
3597 }
3598 }
3599 #endif
3600 else if (!rb_is_absolute_path(s)) {
3601 if (!NIL_P(dname)) {
3602 rb_file_expand_path_internal(dname, Qnil, abs_mode, long_name, result);
3603 rb_enc_associate(result, rb_enc_check(result, fname));
3604 BUFINIT();
3605 p = pend;
3606 }
3607 else {
3608 char *e = append_fspath(result, fname, ruby_getcwd(), &enc, fsenc);
3609 tainted = 1;
3610 BUFINIT();
3611 p = e;
3612 }
3613 #if defined DOSISH || defined __CYGWIN__
3614 if (isdirsep(*s)) {
3615 /* specified full path, but not drive letter nor UNC */
3616 /* we need to get the drive letter or UNC share name */
3617 p = skipprefix(buf, p, enc);
3618 }
3619 else
3620 #endif
3621 p = chompdirsep(skiproot(buf, p, enc), p, enc);
3622 }
3623 else {
3624 size_t len;
3625 b = s;
3626 do s++; while (isdirsep(*s));
3627 len = s - b;
3628 p = buf + len;
3629 BUFCHECK(bdiff >= buflen);
3630 memset(buf, '/', len);
3631 rb_str_set_len(result, len);
3632 rb_enc_associate(result, rb_enc_check(result, fname));
3633 }
3634 if (p > buf && p[-1] == '/')
3635 --p;
3636 else {
3637 rb_str_set_len(result, p-buf);
3638 BUFCHECK(bdiff + 1 >= buflen);
3639 *p = '/';
3640 }
3641
3642 rb_str_set_len(result, p-buf+1);
3643 BUFCHECK(bdiff + 1 >= buflen);
3644 p[1] = 0;
3645 root = skipprefix(buf, p+1, enc);
3646
3647 b = s;
3648 while (*s) {
3649 switch (*s) {
3650 case '.':
3651 if (b == s++) { /* beginning of path element */
3652 switch (*s) {
3653 case '\0':
3654 b = s;
3655 break;
3656 case '.':
3657 if (*(s+1) == '\0' || isdirsep(*(s+1))) {
3658 /* We must go back to the parent */
3659 char *n;
3660 *p = '\0';
3661 if (!(n = strrdirsep(root, p, enc))) {
3662 *p = '/';
3663 }
3664 else {
3665 p = n;
3666 }
3667 b = ++s;
3668 }
3669 #if USE_NTFS
3670 else {
3671 do ++s; while (istrailinggarbage(*s));
3672 }
3673 #endif
3674 break;
3675 case '/':
3676 #if defined DOSISH || defined __CYGWIN__
3677 case '\\':
3678 #endif
3679 b = ++s;
3680 break;
3681 default:
3682 /* ordinary path element, beginning don't move */
3683 break;
3684 }
3685 }
3686 #if USE_NTFS
3687 else {
3688 --s;
3689 case ' ': {
3690 const char *e = s;
3691 while (s < fend && istrailinggarbage(*s)) s++;
3692 if (s >= fend) {
3693 s = e;
3694 goto endpath;
3695 }
3696 }
3697 }
3698 #endif
3699 break;
3700 case '/':
3701 #if defined DOSISH || defined __CYGWIN__
3702 case '\\':
3703 #endif
3704 if (s > b) {
3705 WITH_ROOTDIFF(BUFCOPY(b, s-b));
3706 *p = '/';
3707 }
3708 b = ++s;
3709 break;
3710 default:
3711 #ifdef __APPLE__
3712 {
3713 int n = ignored_char_p(s, fend, enc);
3714 if (n) {
3715 if (s > b) {
3716 WITH_ROOTDIFF(BUFCOPY(b, s-b));
3717 *p = '\0';
3718 }
3719 b = s += n;
3720 break;
3721 }
3722 }
3723 #endif
3724 Inc(s, fend, enc);
3725 break;
3726 }
3727 }
3728
3729 if (s > b) {
3730 #if USE_NTFS
3731 # if USE_NTFS_ADS
3732 static const char prime[] = ":$DATA";
3733 enum {prime_len = sizeof(prime) -1};
3734 # endif
3735 endpath:
3736 # if USE_NTFS_ADS
3737 if (s > b + prime_len && strncasecmp(s - prime_len, prime, prime_len) == 0) {
3738 /* alias of stream */
3739 /* get rid of a bug of x64 VC++ */
3740 if (isADS(*(s - (prime_len+1)))) {
3741 s -= prime_len + 1; /* prime */
3742 }
3743 else if (memchr(b, ':', s - prime_len - b)) {
3744 s -= prime_len; /* alternative */
3745 }
3746 }
3747 # endif
3748 #endif
3749 BUFCOPY(b, s-b);
3750 rb_str_set_len(result, p-buf);
3751 }
3752 if (p == skiproot(buf, p + !!*p, enc) - 1) p++;
3753
3754 #if USE_NTFS
3755 *p = '\0';
3756 if ((s = strrdirsep(b = buf, p, enc)) != 0 && !strpbrk(s, "*?")) {
3757 VALUE tmp, v;
3758 size_t len;
3759 int encidx;
3760 WCHAR *wstr;
3761 WIN32_FIND_DATAW wfd;
3762 HANDLE h;
3763 #ifdef __CYGWIN__
3764 #ifdef HAVE_CYGWIN_CONV_PATH
3765 char *w32buf = NULL;
3766 const int flags = CCP_POSIX_TO_WIN_A | CCP_RELATIVE;
3767 #else
3768 char w32buf[MAXPATHLEN];
3769 #endif
3770 const char *path;
3771 ssize_t bufsize;
3772 int lnk_added = 0, is_symlink = 0;
3773 struct stat st;
3774 p = (char *)s;
3775 len = strlen(p);
3776 if (lstat_without_gvl(buf, &st) == 0 && S_ISLNK(st.st_mode)) {
3777 is_symlink = 1;
3778 if (len > 4 && STRCASECMP(p + len - 4, ".lnk") != 0) {
3779 lnk_added = 1;
3780 }
3781 }
3782 path = *buf ? buf : "/";
3783 #ifdef HAVE_CYGWIN_CONV_PATH
3784 bufsize = cygwin_conv_path(flags, path, NULL, 0);
3785 if (bufsize > 0) {
3786 bufsize += len;
3787 if (lnk_added) bufsize += 4;
3788 w32buf = ALLOCA_N(char, bufsize);
3789 if (cygwin_conv_path(flags, path, w32buf, bufsize) == 0) {
3790 b = w32buf;
3791 }
3792 }
3793 #else
3794 bufsize = MAXPATHLEN;
3795 if (cygwin_conv_to_win32_path(path, w32buf) == 0) {
3796 b = w32buf;
3797 }
3798 #endif
3799 if (is_symlink && b == w32buf) {
3800 *p = '\\';
3801 strlcat(w32buf, p, bufsize);
3802 if (lnk_added) {
3803 strlcat(w32buf, ".lnk", bufsize);
3804 }
3805 }
3806 else {
3807 lnk_added = 0;
3808 }
3809 *p = '/';
3810 #endif
3811 rb_str_set_len(result, p - buf + strlen(p));
3812 encidx = ENCODING_GET(result);
3813 tmp = result;
3814 if (encidx != ENCINDEX_UTF_8 && rb_enc_str_coderange(result) != ENC_CODERANGE_7BIT) {
3815 tmp = rb_str_encode_ospath(result);
3816 }
3817 len = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, NULL, 0);
3818 wstr = ALLOCV_N(WCHAR, v, len);
3819 MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, wstr, len);
3820 if (tmp != result) rb_str_set_len(tmp, 0);
3821 h = FindFirstFileW(wstr, &wfd);
3822 ALLOCV_END(v);
3823 if (h != INVALID_HANDLE_VALUE) {
3824 size_t wlen;
3825 FindClose(h);
3826 len = lstrlenW(wfd.cFileName);
3827 #ifdef __CYGWIN__
3828 if (lnk_added && len > 4 &&
3829 wcscasecmp(wfd.cFileName + len - 4, L".lnk") == 0) {
3830 wfd.cFileName[len -= 4] = L'\0';
3831 }
3832 #else
3833 p = (char *)s;
3834 #endif
3835 ++p;
3836 wlen = (int)len;
3837 len = WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, NULL, 0, NULL, NULL);
3838 if (tmp == result) {
3839 BUFCHECK(bdiff + len >= buflen);
3840 WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, p, len + 1, NULL, NULL);
3841 }
3842 else {
3843 rb_str_modify_expand(tmp, len);
3844 WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, RSTRING_PTR(tmp), len + 1, NULL, NULL);
3845 rb_str_cat_conv_enc_opts(result, bdiff, RSTRING_PTR(tmp), len,
3846 rb_utf8_encoding(), 0, Qnil);
3847 BUFINIT();
3848 rb_str_resize(tmp, 0);
3849 }
3850 p += len;
3851 }
3852 #ifdef __CYGWIN__
3853 else {
3854 p += strlen(p);
3855 }
3856 #endif
3857 }
3858 #endif
3859
3860 if (tainted) OBJ_TAINT(result);
3861 rb_str_set_len(result, p - buf);
3862 rb_enc_check(fname, result);
3863 ENC_CODERANGE_CLEAR(result);
3864 return result;
3865 }
3866 #endif /* _WIN32 */
3867
3868 #define EXPAND_PATH_BUFFER() rb_usascii_str_new(0, MAXPATHLEN + 2)
3869
3870 static VALUE
str_shrink(VALUE str)3871 str_shrink(VALUE str)
3872 {
3873 rb_str_resize(str, RSTRING_LEN(str));
3874 return str;
3875 }
3876
3877 #define expand_path(fname, dname, abs_mode, long_name, result) \
3878 str_shrink(rb_file_expand_path_internal(fname, dname, abs_mode, long_name, result))
3879
3880 #define check_expand_path_args(fname, dname) \
3881 (((fname) = rb_get_path(fname)), \
3882 (void)(NIL_P(dname) ? (dname) : ((dname) = rb_get_path(dname))))
3883
3884 static VALUE
file_expand_path_1(VALUE fname)3885 file_expand_path_1(VALUE fname)
3886 {
3887 return rb_file_expand_path_internal(fname, Qnil, 0, 0, EXPAND_PATH_BUFFER());
3888 }
3889
3890 VALUE
rb_file_expand_path(VALUE fname,VALUE dname)3891 rb_file_expand_path(VALUE fname, VALUE dname)
3892 {
3893 check_expand_path_args(fname, dname);
3894 return expand_path(fname, dname, 0, 1, EXPAND_PATH_BUFFER());
3895 }
3896
3897 VALUE
rb_file_expand_path_fast(VALUE fname,VALUE dname)3898 rb_file_expand_path_fast(VALUE fname, VALUE dname)
3899 {
3900 return expand_path(fname, dname, 0, 0, EXPAND_PATH_BUFFER());
3901 }
3902
3903 /*
3904 * call-seq:
3905 * File.expand_path(file_name [, dir_string] ) -> abs_file_name
3906 *
3907 * Converts a pathname to an absolute pathname. Relative paths are
3908 * referenced from the current working directory of the process unless
3909 * +dir_string+ is given, in which case it will be used as the
3910 * starting point. The given pathname may start with a
3911 * ``<code>~</code>'', which expands to the process owner's home
3912 * directory (the environment variable +HOME+ must be set
3913 * correctly). ``<code>~</code><i>user</i>'' expands to the named
3914 * user's home directory.
3915 *
3916 * File.expand_path("~oracle/bin") #=> "/home/oracle/bin"
3917 *
3918 * A simple example of using +dir_string+ is as follows.
3919 * File.expand_path("ruby", "/usr/bin") #=> "/usr/bin/ruby"
3920 *
3921 * A more complex example which also resolves parent directory is as follows.
3922 * Suppose we are in bin/mygem and want the absolute path of lib/mygem.rb.
3923 *
3924 * File.expand_path("../../lib/mygem.rb", __FILE__)
3925 * #=> ".../path/to/project/lib/mygem.rb"
3926 *
3927 * So first it resolves the parent of __FILE__, that is bin/, then go to the
3928 * parent, the root of the project and appends +lib/mygem.rb+.
3929 */
3930
3931 VALUE
rb_file_s_expand_path(int argc,const VALUE * argv)3932 rb_file_s_expand_path(int argc, const VALUE *argv)
3933 {
3934 rb_check_arity(argc, 1, 2);
3935 return rb_file_expand_path(argv[0], argc > 1 ? argv[1] : Qnil);
3936 }
3937
3938 VALUE
rb_file_absolute_path(VALUE fname,VALUE dname)3939 rb_file_absolute_path(VALUE fname, VALUE dname)
3940 {
3941 check_expand_path_args(fname, dname);
3942 return expand_path(fname, dname, 1, 1, EXPAND_PATH_BUFFER());
3943 }
3944
3945 /*
3946 * call-seq:
3947 * File.absolute_path(file_name [, dir_string] ) -> abs_file_name
3948 *
3949 * Converts a pathname to an absolute pathname. Relative paths are
3950 * referenced from the current working directory of the process unless
3951 * <i>dir_string</i> is given, in which case it will be used as the
3952 * starting point. If the given pathname starts with a ``<code>~</code>''
3953 * it is NOT expanded, it is treated as a normal directory name.
3954 *
3955 * File.absolute_path("~oracle/bin") #=> "<relative_path>/~oracle/bin"
3956 */
3957
3958 VALUE
rb_file_s_absolute_path(int argc,const VALUE * argv)3959 rb_file_s_absolute_path(int argc, const VALUE *argv)
3960 {
3961 rb_check_arity(argc, 1, 2);
3962 return rb_file_absolute_path(argv[0], argc > 1 ? argv[1] : Qnil);
3963 }
3964
3965 enum rb_realpath_mode {
3966 RB_REALPATH_CHECK,
3967 RB_REALPATH_DIR,
3968 RB_REALPATH_STRICT,
3969 RB_REALPATH_MODE_MAX
3970 };
3971
3972 static int
realpath_rec(long * prefixlenp,VALUE * resolvedp,const char * unresolved,VALUE fallback,VALUE loopcheck,enum rb_realpath_mode mode,int last)3973 realpath_rec(long *prefixlenp, VALUE *resolvedp, const char *unresolved, VALUE fallback,
3974 VALUE loopcheck, enum rb_realpath_mode mode, int last)
3975 {
3976 const char *pend = unresolved + strlen(unresolved);
3977 rb_encoding *enc = rb_enc_get(*resolvedp);
3978 ID resolving;
3979 CONST_ID(resolving, "resolving");
3980 while (unresolved < pend) {
3981 const char *testname = unresolved;
3982 const char *unresolved_firstsep = rb_enc_path_next(unresolved, pend, enc);
3983 long testnamelen = unresolved_firstsep - unresolved;
3984 const char *unresolved_nextname = unresolved_firstsep;
3985 while (unresolved_nextname < pend && isdirsep(*unresolved_nextname))
3986 unresolved_nextname++;
3987 unresolved = unresolved_nextname;
3988 if (testnamelen == 1 && testname[0] == '.') {
3989 }
3990 else if (testnamelen == 2 && testname[0] == '.' && testname[1] == '.') {
3991 if (*prefixlenp < RSTRING_LEN(*resolvedp)) {
3992 const char *resolved_str = RSTRING_PTR(*resolvedp);
3993 const char *resolved_names = resolved_str + *prefixlenp;
3994 const char *lastsep = strrdirsep(resolved_names, resolved_str + RSTRING_LEN(*resolvedp), enc);
3995 long len = lastsep ? lastsep - resolved_names : 0;
3996 rb_str_resize(*resolvedp, *prefixlenp + len);
3997 }
3998 }
3999 else {
4000 VALUE checkval;
4001 VALUE testpath = rb_str_dup(*resolvedp);
4002 if (*prefixlenp < RSTRING_LEN(testpath))
4003 rb_str_cat2(testpath, "/");
4004 #if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
4005 if (*prefixlenp > 1 && *prefixlenp == RSTRING_LEN(testpath)) {
4006 const char *prefix = RSTRING_PTR(testpath);
4007 const char *last = rb_enc_left_char_head(prefix, prefix + *prefixlenp - 1, prefix + *prefixlenp, enc);
4008 if (!isdirsep(*last)) rb_str_cat2(testpath, "/");
4009 }
4010 #endif
4011 rb_str_cat(testpath, testname, testnamelen);
4012 checkval = rb_hash_aref(loopcheck, testpath);
4013 if (!NIL_P(checkval)) {
4014 if (checkval == ID2SYM(resolving)) {
4015 if (mode == RB_REALPATH_CHECK) {
4016 errno = ELOOP;
4017 return -1;
4018 }
4019 rb_syserr_fail_path(ELOOP, testpath);
4020 }
4021 else {
4022 *resolvedp = rb_str_dup(checkval);
4023 }
4024 }
4025 else {
4026 struct stat sbuf;
4027 int ret;
4028 ret = lstat_without_gvl(RSTRING_PTR(testpath), &sbuf);
4029 if (ret == -1) {
4030 int e = errno;
4031 if (e == ENOENT && !NIL_P(fallback)) {
4032 if (stat_without_gvl(RSTRING_PTR(fallback), &sbuf) == 0) {
4033 rb_str_replace(*resolvedp, fallback);
4034 return 0;
4035 }
4036 }
4037 if (mode == RB_REALPATH_CHECK) return -1;
4038 if (e == ENOENT) {
4039 if (mode == RB_REALPATH_STRICT || !last || *unresolved_firstsep)
4040 rb_syserr_fail_path(e, testpath);
4041 *resolvedp = testpath;
4042 break;
4043 }
4044 else {
4045 rb_syserr_fail_path(e, testpath);
4046 }
4047 }
4048 #ifdef HAVE_READLINK
4049 if (S_ISLNK(sbuf.st_mode)) {
4050 VALUE link;
4051 VALUE link_orig = Qnil;
4052 const char *link_prefix, *link_names;
4053 long link_prefixlen;
4054 rb_hash_aset(loopcheck, testpath, ID2SYM(resolving));
4055 link = rb_readlink(testpath, enc);
4056 link_prefix = RSTRING_PTR(link);
4057 link_names = skipprefixroot(link_prefix, link_prefix + RSTRING_LEN(link), rb_enc_get(link));
4058 link_prefixlen = link_names - link_prefix;
4059 if (link_prefixlen > 0) {
4060 rb_encoding *tmpenc, *linkenc = rb_enc_get(link);
4061 link_orig = link;
4062 link = rb_str_subseq(link, 0, link_prefixlen);
4063 tmpenc = rb_enc_check(*resolvedp, link);
4064 if (tmpenc != linkenc) link = rb_str_conv_enc(link, linkenc, tmpenc);
4065 *resolvedp = link;
4066 *prefixlenp = link_prefixlen;
4067 }
4068 if (realpath_rec(prefixlenp, resolvedp, link_names, testpath,
4069 loopcheck, mode, !*unresolved_firstsep))
4070 return -1;
4071 RB_GC_GUARD(link_orig);
4072 rb_hash_aset(loopcheck, testpath, rb_str_dup_frozen(*resolvedp));
4073 }
4074 else
4075 #endif
4076 {
4077 VALUE s = rb_str_dup_frozen(testpath);
4078 rb_hash_aset(loopcheck, s, s);
4079 *resolvedp = testpath;
4080 }
4081 }
4082 }
4083 }
4084 return 0;
4085 }
4086
4087 static VALUE
rb_check_realpath_internal(VALUE basedir,VALUE path,enum rb_realpath_mode mode)4088 rb_check_realpath_internal(VALUE basedir, VALUE path, enum rb_realpath_mode mode)
4089 {
4090 long prefixlen;
4091 VALUE resolved;
4092 VALUE unresolved_path;
4093 VALUE loopcheck;
4094 VALUE curdir = Qnil;
4095
4096 rb_encoding *enc, *origenc;
4097 char *path_names = NULL, *basedir_names = NULL, *curdir_names = NULL;
4098 char *ptr, *prefixptr = NULL, *pend;
4099 long len;
4100
4101 unresolved_path = rb_str_dup_frozen(path);
4102
4103 if (!NIL_P(basedir)) {
4104 FilePathValue(basedir);
4105 basedir = TO_OSPATH(rb_str_dup_frozen(basedir));
4106 }
4107
4108 enc = rb_enc_get(unresolved_path);
4109 origenc = enc;
4110 unresolved_path = TO_OSPATH(unresolved_path);
4111 RSTRING_GETMEM(unresolved_path, ptr, len);
4112 path_names = skipprefixroot(ptr, ptr + len, rb_enc_get(unresolved_path));
4113 if (ptr != path_names) {
4114 resolved = rb_str_subseq(unresolved_path, 0, path_names - ptr);
4115 goto root_found;
4116 }
4117
4118 if (!NIL_P(basedir)) {
4119 RSTRING_GETMEM(basedir, ptr, len);
4120 basedir_names = skipprefixroot(ptr, ptr + len, rb_enc_get(basedir));
4121 if (ptr != basedir_names) {
4122 resolved = rb_str_subseq(basedir, 0, basedir_names - ptr);
4123 goto root_found;
4124 }
4125 }
4126
4127 curdir = rb_dir_getwd_ospath();
4128 RSTRING_GETMEM(curdir, ptr, len);
4129 curdir_names = skipprefixroot(ptr, ptr + len, rb_enc_get(curdir));
4130 resolved = rb_str_subseq(curdir, 0, curdir_names - ptr);
4131
4132 root_found:
4133 RSTRING_GETMEM(resolved, prefixptr, prefixlen);
4134 pend = prefixptr + prefixlen;
4135 ptr = chompdirsep(prefixptr, pend, enc);
4136 if (ptr < pend) {
4137 prefixlen = ++ptr - prefixptr;
4138 rb_str_set_len(resolved, prefixlen);
4139 }
4140 #ifdef FILE_ALT_SEPARATOR
4141 while (prefixptr < ptr) {
4142 if (*prefixptr == FILE_ALT_SEPARATOR) {
4143 *prefixptr = '/';
4144 }
4145 Inc(prefixptr, pend, enc);
4146 }
4147 #endif
4148
4149 switch (rb_enc_to_index(enc)) {
4150 case ENCINDEX_ASCII:
4151 case ENCINDEX_US_ASCII:
4152 rb_enc_associate_index(resolved, rb_filesystem_encindex());
4153 }
4154
4155 loopcheck = rb_hash_new();
4156 if (curdir_names) {
4157 if (realpath_rec(&prefixlen, &resolved, curdir_names, Qnil, loopcheck, mode, 0))
4158 return Qnil;
4159 }
4160 if (basedir_names) {
4161 if (realpath_rec(&prefixlen, &resolved, basedir_names, Qnil, loopcheck, mode, 0))
4162 return Qnil;
4163 }
4164 if (realpath_rec(&prefixlen, &resolved, path_names, Qnil, loopcheck, mode, 1))
4165 return Qnil;
4166
4167 if (origenc != rb_enc_get(resolved)) {
4168 if (rb_enc_str_asciionly_p(resolved)) {
4169 rb_enc_associate(resolved, origenc);
4170 }
4171 else {
4172 resolved = rb_str_conv_enc(resolved, NULL, origenc);
4173 }
4174 }
4175
4176 rb_obj_taint(resolved);
4177 RB_GC_GUARD(unresolved_path);
4178 RB_GC_GUARD(curdir);
4179 return resolved;
4180 }
4181
4182 VALUE
rb_realpath_internal(VALUE basedir,VALUE path,int strict)4183 rb_realpath_internal(VALUE basedir, VALUE path, int strict)
4184 {
4185 const enum rb_realpath_mode mode =
4186 strict ? RB_REALPATH_STRICT : RB_REALPATH_DIR;
4187 return rb_check_realpath_internal(basedir, path, mode);
4188 }
4189
4190 VALUE
rb_check_realpath(VALUE basedir,VALUE path)4191 rb_check_realpath(VALUE basedir, VALUE path)
4192 {
4193 return rb_check_realpath_internal(basedir, path, RB_REALPATH_CHECK);
4194 }
4195
4196 /*
4197 * call-seq:
4198 * File.realpath(pathname [, dir_string]) -> real_pathname
4199 *
4200 * Returns the real (absolute) pathname of _pathname_ in the actual
4201 * filesystem not containing symlinks or useless dots.
4202 *
4203 * If _dir_string_ is given, it is used as a base directory
4204 * for interpreting relative pathname instead of the current directory.
4205 *
4206 * All components of the pathname must exist when this method is
4207 * called.
4208 */
4209 static VALUE
rb_file_s_realpath(int argc,VALUE * argv,VALUE klass)4210 rb_file_s_realpath(int argc, VALUE *argv, VALUE klass)
4211 {
4212 VALUE basedir = (rb_check_arity(argc, 1, 2) > 1) ? argv[1] : Qnil;
4213 VALUE path = argv[0];
4214 FilePathValue(path);
4215 return rb_realpath_internal(basedir, path, 1);
4216 }
4217
4218 /*
4219 * call-seq:
4220 * File.realdirpath(pathname [, dir_string]) -> real_pathname
4221 *
4222 * Returns the real (absolute) pathname of _pathname_ in the actual filesystem.
4223 * The real pathname doesn't contain symlinks or useless dots.
4224 *
4225 * If _dir_string_ is given, it is used as a base directory
4226 * for interpreting relative pathname instead of the current directory.
4227 *
4228 * The last component of the real pathname can be nonexistent.
4229 */
4230 static VALUE
rb_file_s_realdirpath(int argc,VALUE * argv,VALUE klass)4231 rb_file_s_realdirpath(int argc, VALUE *argv, VALUE klass)
4232 {
4233 VALUE basedir = (rb_check_arity(argc, 1, 2) > 1) ? argv[1] : Qnil;
4234 VALUE path = argv[0];
4235 FilePathValue(path);
4236 return rb_realpath_internal(basedir, path, 0);
4237 }
4238
4239 static size_t
rmext(const char * p,long l0,long l1,const char * e,long l2,rb_encoding * enc)4240 rmext(const char *p, long l0, long l1, const char *e, long l2, rb_encoding *enc)
4241 {
4242 int len1, len2;
4243 unsigned int c;
4244 const char *s, *last;
4245
4246 if (!e || !l2) return 0;
4247
4248 c = rb_enc_codepoint_len(e, e + l2, &len1, enc);
4249 if (rb_enc_ascget(e + len1, e + l2, &len2, enc) == '*' && len1 + len2 == l2) {
4250 if (c == '.') return l0;
4251 s = p;
4252 e = p + l1;
4253 last = e;
4254 while (s < e) {
4255 if (rb_enc_codepoint_len(s, e, &len1, enc) == c) last = s;
4256 s += len1;
4257 }
4258 return last - p;
4259 }
4260 if (l1 < l2) return l1;
4261
4262 s = p+l1-l2;
4263 if (rb_enc_left_char_head(p, s, p+l1, enc) != s) return 0;
4264 #if CASEFOLD_FILESYSTEM
4265 #define fncomp strncasecmp
4266 #else
4267 #define fncomp strncmp
4268 #endif
4269 if (fncomp(s, e, l2) == 0) {
4270 return l1-l2;
4271 }
4272 return 0;
4273 }
4274
4275 const char *
ruby_enc_find_basename(const char * name,long * baselen,long * alllen,rb_encoding * enc)4276 ruby_enc_find_basename(const char *name, long *baselen, long *alllen, rb_encoding *enc)
4277 {
4278 const char *p, *q, *e, *end;
4279 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
4280 const char *root;
4281 #endif
4282 long f = 0, n = -1;
4283
4284 end = name + (alllen ? (size_t)*alllen : strlen(name));
4285 name = skipprefix(name, end, enc);
4286 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
4287 root = name;
4288 #endif
4289 while (isdirsep(*name))
4290 name++;
4291 if (!*name) {
4292 p = name - 1;
4293 f = 1;
4294 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
4295 if (name != root) {
4296 /* has slashes */
4297 }
4298 #ifdef DOSISH_DRIVE_LETTER
4299 else if (*p == ':') {
4300 p++;
4301 f = 0;
4302 }
4303 #endif
4304 #ifdef DOSISH_UNC
4305 else {
4306 p = "/";
4307 }
4308 #endif
4309 #endif
4310 }
4311 else {
4312 if (!(p = strrdirsep(name, end, enc))) {
4313 p = name;
4314 }
4315 else {
4316 while (isdirsep(*p)) p++; /* skip last / */
4317 }
4318 #if USE_NTFS
4319 n = ntfs_tail(p, end, enc) - p;
4320 #else
4321 n = chompdirsep(p, end, enc) - p;
4322 #endif
4323 for (q = p; q - p < n && *q == '.'; q++);
4324 for (e = 0; q - p < n; Inc(q, end, enc)) {
4325 if (*q == '.') e = q;
4326 }
4327 if (e) f = e - p;
4328 else f = n;
4329 }
4330
4331 if (baselen)
4332 *baselen = f;
4333 if (alllen)
4334 *alllen = n;
4335 return p;
4336 }
4337
4338 /*
4339 * call-seq:
4340 * File.basename(file_name [, suffix] ) -> base_name
4341 *
4342 * Returns the last component of the filename given in
4343 * <i>file_name</i> (after first stripping trailing separators),
4344 * which can be formed using both <code>File::SEPARATOR</code> and
4345 * <code>File::ALT_SEPARATOR</code> as the separator when
4346 * <code>File::ALT_SEPARATOR</code> is not <code>nil</code>. If
4347 * <i>suffix</i> is given and present at the end of <i>file_name</i>,
4348 * it is removed. If <i>suffix</i> is ".*", any extension will be
4349 * removed.
4350 *
4351 * File.basename("/home/gumby/work/ruby.rb") #=> "ruby.rb"
4352 * File.basename("/home/gumby/work/ruby.rb", ".rb") #=> "ruby"
4353 * File.basename("/home/gumby/work/ruby.rb", ".*") #=> "ruby"
4354 */
4355
4356 static VALUE
rb_file_s_basename(int argc,VALUE * argv)4357 rb_file_s_basename(int argc, VALUE *argv)
4358 {
4359 VALUE fname, fext, basename;
4360 const char *name, *p;
4361 long f, n;
4362 rb_encoding *enc;
4363
4364 fext = Qnil;
4365 if (rb_check_arity(argc, 1, 2) == 2) {
4366 fext = argv[1];
4367 StringValue(fext);
4368 enc = check_path_encoding(fext);
4369 }
4370 fname = argv[0];
4371 FilePathStringValue(fname);
4372 if (NIL_P(fext) || !(enc = rb_enc_compatible(fname, fext))) {
4373 enc = rb_enc_get(fname);
4374 fext = Qnil;
4375 }
4376 if ((n = RSTRING_LEN(fname)) == 0 || !*(name = RSTRING_PTR(fname)))
4377 return rb_str_new_shared(fname);
4378
4379 p = ruby_enc_find_basename(name, &f, &n, enc);
4380 if (n >= 0) {
4381 if (NIL_P(fext)) {
4382 f = n;
4383 }
4384 else {
4385 const char *fp;
4386 fp = StringValueCStr(fext);
4387 if (!(f = rmext(p, f, n, fp, RSTRING_LEN(fext), enc))) {
4388 f = n;
4389 }
4390 RB_GC_GUARD(fext);
4391 }
4392 if (f == RSTRING_LEN(fname)) return rb_str_new_shared(fname);
4393 }
4394
4395 basename = rb_str_new(p, f);
4396 rb_enc_copy(basename, fname);
4397 OBJ_INFECT(basename, fname);
4398 return basename;
4399 }
4400
4401 /*
4402 * call-seq:
4403 * File.dirname(file_name) -> dir_name
4404 *
4405 * Returns all components of the filename given in <i>file_name</i>
4406 * except the last one (after first stripping trailing separators).
4407 * The filename can be formed using both <code>File::SEPARATOR</code>
4408 * and <code>File::ALT_SEPARATOR</code> as the separator when
4409 * <code>File::ALT_SEPARATOR</code> is not <code>nil</code>.
4410 *
4411 * File.dirname("/home/gumby/work/ruby.rb") #=> "/home/gumby/work"
4412 */
4413
4414 static VALUE
rb_file_s_dirname(VALUE klass,VALUE fname)4415 rb_file_s_dirname(VALUE klass, VALUE fname)
4416 {
4417 return rb_file_dirname(fname);
4418 }
4419
4420 VALUE
rb_file_dirname(VALUE fname)4421 rb_file_dirname(VALUE fname)
4422 {
4423 const char *name, *root, *p, *end;
4424 VALUE dirname;
4425 rb_encoding *enc;
4426
4427 FilePathStringValue(fname);
4428 name = StringValueCStr(fname);
4429 end = name + RSTRING_LEN(fname);
4430 enc = rb_enc_get(fname);
4431 root = skiproot(name, end, enc);
4432 #ifdef DOSISH_UNC
4433 if (root > name + 1 && isdirsep(*name))
4434 root = skipprefix(name = root - 2, end, enc);
4435 #else
4436 if (root > name + 1)
4437 name = root - 1;
4438 #endif
4439 p = strrdirsep(root, end, enc);
4440 if (!p) {
4441 p = root;
4442 }
4443 if (p == name)
4444 return rb_usascii_str_new2(".");
4445 #ifdef DOSISH_DRIVE_LETTER
4446 if (has_drive_letter(name) && isdirsep(*(name + 2))) {
4447 const char *top = skiproot(name + 2, end, enc);
4448 dirname = rb_str_new(name, 3);
4449 rb_str_cat(dirname, top, p - top);
4450 }
4451 else
4452 #endif
4453 dirname = rb_str_new(name, p - name);
4454 #ifdef DOSISH_DRIVE_LETTER
4455 if (has_drive_letter(name) && root == name + 2 && p - name == 2)
4456 rb_str_cat(dirname, ".", 1);
4457 #endif
4458 rb_enc_copy(dirname, fname);
4459 OBJ_INFECT(dirname, fname);
4460 return dirname;
4461 }
4462
4463 /*
4464 * accept a String, and return the pointer of the extension.
4465 * if len is passed, set the length of extension to it.
4466 * returned pointer is in ``name'' or NULL.
4467 * returns *len
4468 * no dot NULL 0
4469 * dotfile top 0
4470 * end with dot dot 1
4471 * .ext dot len of .ext
4472 * .ext:stream dot len of .ext without :stream (NT only)
4473 *
4474 */
4475 const char *
ruby_enc_find_extname(const char * name,long * len,rb_encoding * enc)4476 ruby_enc_find_extname(const char *name, long *len, rb_encoding *enc)
4477 {
4478 const char *p, *e, *end = name + (len ? *len : (long)strlen(name));
4479
4480 p = strrdirsep(name, end, enc); /* get the last path component */
4481 if (!p)
4482 p = name;
4483 else
4484 do name = ++p; while (isdirsep(*p));
4485
4486 e = 0;
4487 while (*p && *p == '.') p++;
4488 while (*p) {
4489 if (*p == '.' || istrailinggarbage(*p)) {
4490 #if USE_NTFS
4491 const char *last = p++, *dot = last;
4492 while (istrailinggarbage(*p)) {
4493 if (*p == '.') dot = p;
4494 p++;
4495 }
4496 if (!*p || isADS(*p)) {
4497 p = last;
4498 break;
4499 }
4500 if (*last == '.' || dot > last) e = dot;
4501 continue;
4502 #else
4503 e = p; /* get the last dot of the last component */
4504 #endif
4505 }
4506 #if USE_NTFS
4507 else if (isADS(*p)) {
4508 break;
4509 }
4510 #endif
4511 else if (isdirsep(*p))
4512 break;
4513 Inc(p, end, enc);
4514 }
4515
4516 if (len) {
4517 /* no dot, or the only dot is first or end? */
4518 if (!e || e == name)
4519 *len = 0;
4520 else if (e+1 == p)
4521 *len = 1;
4522 else
4523 *len = p - e;
4524 }
4525 return e;
4526 }
4527
4528 /*
4529 * call-seq:
4530 * File.extname(path) -> string
4531 *
4532 * Returns the extension (the portion of file name in +path+
4533 * starting from the last period).
4534 *
4535 * If +path+ is a dotfile, or starts with a period, then the starting
4536 * dot is not dealt with the start of the extension.
4537 *
4538 * An empty string will also be returned when the period is the last character
4539 * in +path+.
4540 *
4541 * File.extname("test.rb") #=> ".rb"
4542 * File.extname("a/b/d/test.rb") #=> ".rb"
4543 * File.extname(".a/b/d/test.rb") #=> ".rb"
4544 * File.extname("foo.") #=> ""
4545 * File.extname("test") #=> ""
4546 * File.extname(".profile") #=> ""
4547 * File.extname(".profile.sh") #=> ".sh"
4548 *
4549 */
4550
4551 static VALUE
rb_file_s_extname(VALUE klass,VALUE fname)4552 rb_file_s_extname(VALUE klass, VALUE fname)
4553 {
4554 const char *name, *e;
4555 long len;
4556 VALUE extname;
4557
4558 FilePathStringValue(fname);
4559 name = StringValueCStr(fname);
4560 len = RSTRING_LEN(fname);
4561 e = ruby_enc_find_extname(name, &len, rb_enc_get(fname));
4562 if (len <= 1)
4563 return rb_str_new(0, 0);
4564 extname = rb_str_subseq(fname, e - name, len); /* keep the dot, too! */
4565 OBJ_INFECT(extname, fname);
4566 return extname;
4567 }
4568
4569 /*
4570 * call-seq:
4571 * File.path(path) -> string
4572 *
4573 * Returns the string representation of the path
4574 *
4575 * File.path("/dev/null") #=> "/dev/null"
4576 * File.path(Pathname.new("/tmp")) #=> "/tmp"
4577 *
4578 */
4579
4580 static VALUE
rb_file_s_path(VALUE klass,VALUE fname)4581 rb_file_s_path(VALUE klass, VALUE fname)
4582 {
4583 return rb_get_path(fname);
4584 }
4585
4586 /*
4587 * call-seq:
4588 * File.split(file_name) -> array
4589 *
4590 * Splits the given string into a directory and a file component and
4591 * returns them in a two-element array. See also
4592 * <code>File::dirname</code> and <code>File::basename</code>.
4593 *
4594 * File.split("/home/gumby/.profile") #=> ["/home/gumby", ".profile"]
4595 */
4596
4597 static VALUE
rb_file_s_split(VALUE klass,VALUE path)4598 rb_file_s_split(VALUE klass, VALUE path)
4599 {
4600 FilePathStringValue(path); /* get rid of converting twice */
4601 return rb_assoc_new(rb_file_dirname(path), rb_file_s_basename(1,&path));
4602 }
4603
4604 static VALUE rb_file_join(VALUE ary);
4605
4606 static VALUE
file_inspect_join(VALUE ary,VALUE arg,int recur)4607 file_inspect_join(VALUE ary, VALUE arg, int recur)
4608 {
4609 if (recur || ary == arg) rb_raise(rb_eArgError, "recursive array");
4610 return rb_file_join(arg);
4611 }
4612
4613 static VALUE
rb_file_join(VALUE ary)4614 rb_file_join(VALUE ary)
4615 {
4616 long len, i;
4617 VALUE result, tmp;
4618 const char *name, *tail;
4619 int checked = TRUE;
4620 rb_encoding *enc;
4621
4622 if (RARRAY_LEN(ary) == 0) return rb_str_new(0, 0);
4623
4624 len = 1;
4625 for (i=0; i<RARRAY_LEN(ary); i++) {
4626 tmp = RARRAY_AREF(ary, i);
4627 if (RB_TYPE_P(tmp, T_STRING)) {
4628 check_path_encoding(tmp);
4629 len += RSTRING_LEN(tmp);
4630 }
4631 else {
4632 len += 10;
4633 }
4634 }
4635 len += RARRAY_LEN(ary) - 1;
4636 result = rb_str_buf_new(len);
4637 RBASIC_CLEAR_CLASS(result);
4638 OBJ_INFECT(result, ary);
4639 for (i=0; i<RARRAY_LEN(ary); i++) {
4640 tmp = RARRAY_AREF(ary, i);
4641 switch (OBJ_BUILTIN_TYPE(tmp)) {
4642 case T_STRING:
4643 if (!checked) check_path_encoding(tmp);
4644 StringValueCStr(tmp);
4645 break;
4646 case T_ARRAY:
4647 if (ary == tmp) {
4648 rb_raise(rb_eArgError, "recursive array");
4649 }
4650 else {
4651 tmp = rb_exec_recursive(file_inspect_join, ary, tmp);
4652 }
4653 break;
4654 default:
4655 FilePathStringValue(tmp);
4656 checked = FALSE;
4657 }
4658 RSTRING_GETMEM(result, name, len);
4659 if (i == 0) {
4660 rb_enc_copy(result, tmp);
4661 }
4662 else {
4663 tail = chompdirsep(name, name + len, rb_enc_get(result));
4664 if (RSTRING_PTR(tmp) && isdirsep(RSTRING_PTR(tmp)[0])) {
4665 rb_str_set_len(result, tail - name);
4666 }
4667 else if (!*tail) {
4668 rb_str_cat(result, "/", 1);
4669 }
4670 }
4671 enc = rb_enc_check(result, tmp);
4672 rb_str_buf_append(result, tmp);
4673 rb_enc_associate(result, enc);
4674 }
4675 RBASIC_SET_CLASS_RAW(result, rb_cString);
4676
4677 return result;
4678 }
4679
4680 /*
4681 * call-seq:
4682 * File.join(string, ...) -> string
4683 *
4684 * Returns a new string formed by joining the strings using
4685 * <code>"/"</code>.
4686 *
4687 * File.join("usr", "mail", "gumby") #=> "usr/mail/gumby"
4688 *
4689 */
4690
4691 static VALUE
rb_file_s_join(VALUE klass,VALUE args)4692 rb_file_s_join(VALUE klass, VALUE args)
4693 {
4694 return rb_file_join(args);
4695 }
4696
4697 #if defined(HAVE_TRUNCATE) || defined(HAVE_CHSIZE)
4698 struct truncate_arg {
4699 const char *path;
4700 #if defined(HAVE_TRUNCATE)
4701 #define NUM2POS(n) NUM2OFFT(n)
4702 off_t pos;
4703 #else
4704 #define NUM2POS(n) NUM2LONG(n)
4705 long pos;
4706 #endif
4707 };
4708
4709 static void *
nogvl_truncate(void * ptr)4710 nogvl_truncate(void *ptr)
4711 {
4712 struct truncate_arg *ta = ptr;
4713 #ifdef HAVE_TRUNCATE
4714 return (void *)(VALUE)truncate(ta->path, ta->pos);
4715 #else /* defined(HAVE_CHSIZE) */
4716 {
4717 int tmpfd = rb_cloexec_open(ta->path, 0, 0);
4718
4719 if (tmpfd < 0)
4720 return (void *)-1;
4721 rb_update_max_fd(tmpfd);
4722 if (chsize(tmpfd, ta->pos) < 0) {
4723 int e = errno;
4724 close(tmpfd);
4725 errno = e;
4726 return (void *)-1;
4727 }
4728 close(tmpfd);
4729 return 0;
4730 }
4731 #endif
4732 }
4733
4734 /*
4735 * call-seq:
4736 * File.truncate(file_name, integer) -> 0
4737 *
4738 * Truncates the file <i>file_name</i> to be at most <i>integer</i>
4739 * bytes long. Not available on all platforms.
4740 *
4741 * f = File.new("out", "w")
4742 * f.write("1234567890") #=> 10
4743 * f.close #=> nil
4744 * File.truncate("out", 5) #=> 0
4745 * File.size("out") #=> 5
4746 *
4747 */
4748
4749 static VALUE
rb_file_s_truncate(VALUE klass,VALUE path,VALUE len)4750 rb_file_s_truncate(VALUE klass, VALUE path, VALUE len)
4751 {
4752 struct truncate_arg ta;
4753 int r;
4754
4755 ta.pos = NUM2POS(len);
4756 FilePathValue(path);
4757 path = rb_str_encode_ospath(path);
4758 ta.path = StringValueCStr(path);
4759
4760 r = (int)(VALUE)rb_thread_call_without_gvl(nogvl_truncate, &ta,
4761 RUBY_UBF_IO, NULL);
4762 if (r < 0)
4763 rb_sys_fail_path(path);
4764 return INT2FIX(0);
4765 #undef NUM2POS
4766 }
4767 #else
4768 #define rb_file_s_truncate rb_f_notimplement
4769 #endif
4770
4771 #if defined(HAVE_FTRUNCATE) || defined(HAVE_CHSIZE)
4772 struct ftruncate_arg {
4773 int fd;
4774 #if defined(HAVE_FTRUNCATE)
4775 #define NUM2POS(n) NUM2OFFT(n)
4776 off_t pos;
4777 #else
4778 #define NUM2POS(n) NUM2LONG(n)
4779 long pos;
4780 #endif
4781 };
4782
4783 static VALUE
nogvl_ftruncate(void * ptr)4784 nogvl_ftruncate(void *ptr)
4785 {
4786 struct ftruncate_arg *fa = ptr;
4787
4788 #ifdef HAVE_FTRUNCATE
4789 return (VALUE)ftruncate(fa->fd, fa->pos);
4790 #else /* defined(HAVE_CHSIZE) */
4791 return (VALUE)chsize(fa->fd, fa->pos);
4792 #endif
4793 }
4794
4795 /*
4796 * call-seq:
4797 * file.truncate(integer) -> 0
4798 *
4799 * Truncates <i>file</i> to at most <i>integer</i> bytes. The file
4800 * must be opened for writing. Not available on all platforms.
4801 *
4802 * f = File.new("out", "w")
4803 * f.syswrite("1234567890") #=> 10
4804 * f.truncate(5) #=> 0
4805 * f.close() #=> nil
4806 * File.size("out") #=> 5
4807 */
4808
4809 static VALUE
rb_file_truncate(VALUE obj,VALUE len)4810 rb_file_truncate(VALUE obj, VALUE len)
4811 {
4812 rb_io_t *fptr;
4813 struct ftruncate_arg fa;
4814
4815 fa.pos = NUM2POS(len);
4816 GetOpenFile(obj, fptr);
4817 if (!(fptr->mode & FMODE_WRITABLE)) {
4818 rb_raise(rb_eIOError, "not opened for writing");
4819 }
4820 rb_io_flush_raw(obj, 0);
4821 fa.fd = fptr->fd;
4822 if ((int)rb_thread_io_blocking_region(nogvl_ftruncate, &fa, fa.fd) < 0) {
4823 rb_sys_fail_path(fptr->pathv);
4824 }
4825 return INT2FIX(0);
4826 #undef NUM2POS
4827 }
4828 #else
4829 #define rb_file_truncate rb_f_notimplement
4830 #endif
4831
4832 # ifndef LOCK_SH
4833 # define LOCK_SH 1
4834 # endif
4835 # ifndef LOCK_EX
4836 # define LOCK_EX 2
4837 # endif
4838 # ifndef LOCK_NB
4839 # define LOCK_NB 4
4840 # endif
4841 # ifndef LOCK_UN
4842 # define LOCK_UN 8
4843 # endif
4844
4845 #ifdef __CYGWIN__
4846 #include <winerror.h>
4847 #endif
4848
4849 static VALUE
rb_thread_flock(void * data)4850 rb_thread_flock(void *data)
4851 {
4852 #ifdef __CYGWIN__
4853 int old_errno = errno;
4854 #endif
4855 int *op = data, ret = flock(op[0], op[1]);
4856
4857 #ifdef __CYGWIN__
4858 if (GetLastError() == ERROR_NOT_LOCKED) {
4859 ret = 0;
4860 errno = old_errno;
4861 }
4862 #endif
4863 return (VALUE)ret;
4864 }
4865
4866 /*
4867 * call-seq:
4868 * file.flock(locking_constant) -> 0 or false
4869 *
4870 * Locks or unlocks a file according to <i>locking_constant</i> (a
4871 * logical <em>or</em> of the values in the table below).
4872 * Returns <code>false</code> if <code>File::LOCK_NB</code> is
4873 * specified and the operation would otherwise have blocked. Not
4874 * available on all platforms.
4875 *
4876 * Locking constants (in class File):
4877 *
4878 * LOCK_EX | Exclusive lock. Only one process may hold an
4879 * | exclusive lock for a given file at a time.
4880 * ----------+------------------------------------------------
4881 * LOCK_NB | Don't block when locking. May be combined
4882 * | with other lock options using logical or.
4883 * ----------+------------------------------------------------
4884 * LOCK_SH | Shared lock. Multiple processes may each hold a
4885 * | shared lock for a given file at the same time.
4886 * ----------+------------------------------------------------
4887 * LOCK_UN | Unlock.
4888 *
4889 * Example:
4890 *
4891 * # update a counter using write lock
4892 * # don't use "w" because it truncates the file before lock.
4893 * File.open("counter", File::RDWR|File::CREAT, 0644) {|f|
4894 * f.flock(File::LOCK_EX)
4895 * value = f.read.to_i + 1
4896 * f.rewind
4897 * f.write("#{value}\n")
4898 * f.flush
4899 * f.truncate(f.pos)
4900 * }
4901 *
4902 * # read the counter using read lock
4903 * File.open("counter", "r") {|f|
4904 * f.flock(File::LOCK_SH)
4905 * p f.read
4906 * }
4907 *
4908 */
4909
4910 static VALUE
rb_file_flock(VALUE obj,VALUE operation)4911 rb_file_flock(VALUE obj, VALUE operation)
4912 {
4913 rb_io_t *fptr;
4914 int op[2], op1;
4915 struct timeval time;
4916
4917 op[1] = op1 = NUM2INT(operation);
4918 GetOpenFile(obj, fptr);
4919 op[0] = fptr->fd;
4920
4921 if (fptr->mode & FMODE_WRITABLE) {
4922 rb_io_flush_raw(obj, 0);
4923 }
4924 while ((int)rb_thread_io_blocking_region(rb_thread_flock, op, fptr->fd) < 0) {
4925 int e = errno;
4926 switch (e) {
4927 case EAGAIN:
4928 case EACCES:
4929 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
4930 case EWOULDBLOCK:
4931 #endif
4932 if (op1 & LOCK_NB) return Qfalse;
4933
4934 time.tv_sec = 0;
4935 time.tv_usec = 100 * 1000; /* 0.1 sec */
4936 rb_thread_wait_for(time);
4937 rb_io_check_closed(fptr);
4938 continue;
4939
4940 case EINTR:
4941 #if defined(ERESTART)
4942 case ERESTART:
4943 #endif
4944 break;
4945
4946 default:
4947 rb_syserr_fail_path(e, fptr->pathv);
4948 }
4949 }
4950 return INT2FIX(0);
4951 }
4952
4953 static void
test_check(int n,int argc,VALUE * argv)4954 test_check(int n, int argc, VALUE *argv)
4955 {
4956 int i;
4957
4958 n+=1;
4959 rb_check_arity(argc, n, n);
4960 for (i=1; i<n; i++) {
4961 if (!RB_TYPE_P(argv[i], T_FILE)) {
4962 FilePathValue(argv[i]);
4963 }
4964 }
4965 }
4966
4967 #define CHECK(n) test_check((n), argc, argv)
4968
4969 /*
4970 * call-seq:
4971 * test(cmd, file1 [, file2] ) -> obj
4972 *
4973 * Uses the character +cmd+ to perform various tests on +file1+ (first
4974 * table below) or on +file1+ and +file2+ (second table).
4975 *
4976 * File tests on a single file:
4977 *
4978 * Cmd Returns Meaning
4979 * "A" | Time | Last access time for file1
4980 * "b" | boolean | True if file1 is a block device
4981 * "c" | boolean | True if file1 is a character device
4982 * "C" | Time | Last change time for file1
4983 * "d" | boolean | True if file1 exists and is a directory
4984 * "e" | boolean | True if file1 exists
4985 * "f" | boolean | True if file1 exists and is a regular file
4986 * "g" | boolean | True if file1 has the \CF{setgid} bit
4987 * | | set (false under NT)
4988 * "G" | boolean | True if file1 exists and has a group
4989 * | | ownership equal to the caller's group
4990 * "k" | boolean | True if file1 exists and has the sticky bit set
4991 * "l" | boolean | True if file1 exists and is a symbolic link
4992 * "M" | Time | Last modification time for file1
4993 * "o" | boolean | True if file1 exists and is owned by
4994 * | | the caller's effective uid
4995 * "O" | boolean | True if file1 exists and is owned by
4996 * | | the caller's real uid
4997 * "p" | boolean | True if file1 exists and is a fifo
4998 * "r" | boolean | True if file1 is readable by the effective
4999 * | | uid/gid of the caller
5000 * "R" | boolean | True if file is readable by the real
5001 * | | uid/gid of the caller
5002 * "s" | int/nil | If file1 has nonzero size, return the size,
5003 * | | otherwise return nil
5004 * "S" | boolean | True if file1 exists and is a socket
5005 * "u" | boolean | True if file1 has the setuid bit set
5006 * "w" | boolean | True if file1 exists and is writable by
5007 * | | the effective uid/gid
5008 * "W" | boolean | True if file1 exists and is writable by
5009 * | | the real uid/gid
5010 * "x" | boolean | True if file1 exists and is executable by
5011 * | | the effective uid/gid
5012 * "X" | boolean | True if file1 exists and is executable by
5013 * | | the real uid/gid
5014 * "z" | boolean | True if file1 exists and has a zero length
5015 *
5016 * Tests that take two files:
5017 *
5018 * "-" | boolean | True if file1 and file2 are identical
5019 * "=" | boolean | True if the modification times of file1
5020 * | | and file2 are equal
5021 * "<" | boolean | True if the modification time of file1
5022 * | | is prior to that of file2
5023 * ">" | boolean | True if the modification time of file1
5024 * | | is after that of file2
5025 */
5026
5027 static VALUE
rb_f_test(int argc,VALUE * argv)5028 rb_f_test(int argc, VALUE *argv)
5029 {
5030 int cmd;
5031
5032 if (argc == 0) rb_check_arity(argc, 2, 3);
5033 cmd = NUM2CHR(argv[0]);
5034 if (cmd == 0) {
5035 unknown:
5036 /* unknown command */
5037 if (ISPRINT(cmd)) {
5038 rb_raise(rb_eArgError, "unknown command '%s%c'", cmd == '\'' || cmd == '\\' ? "\\" : "", cmd);
5039 }
5040 else {
5041 rb_raise(rb_eArgError, "unknown command \"\\x%02X\"", cmd);
5042 }
5043 }
5044 if (strchr("bcdefgGkloOprRsSuwWxXz", cmd)) {
5045 CHECK(1);
5046 switch (cmd) {
5047 case 'b':
5048 return rb_file_blockdev_p(0, argv[1]);
5049
5050 case 'c':
5051 return rb_file_chardev_p(0, argv[1]);
5052
5053 case 'd':
5054 return rb_file_directory_p(0, argv[1]);
5055
5056 case 'e':
5057 return rb_file_exist_p(0, argv[1]);
5058
5059 case 'f':
5060 return rb_file_file_p(0, argv[1]);
5061
5062 case 'g':
5063 return rb_file_sgid_p(0, argv[1]);
5064
5065 case 'G':
5066 return rb_file_grpowned_p(0, argv[1]);
5067
5068 case 'k':
5069 return rb_file_sticky_p(0, argv[1]);
5070
5071 case 'l':
5072 return rb_file_symlink_p(0, argv[1]);
5073
5074 case 'o':
5075 return rb_file_owned_p(0, argv[1]);
5076
5077 case 'O':
5078 return rb_file_rowned_p(0, argv[1]);
5079
5080 case 'p':
5081 return rb_file_pipe_p(0, argv[1]);
5082
5083 case 'r':
5084 return rb_file_readable_p(0, argv[1]);
5085
5086 case 'R':
5087 return rb_file_readable_real_p(0, argv[1]);
5088
5089 case 's':
5090 return rb_file_size_p(0, argv[1]);
5091
5092 case 'S':
5093 return rb_file_socket_p(0, argv[1]);
5094
5095 case 'u':
5096 return rb_file_suid_p(0, argv[1]);
5097
5098 case 'w':
5099 return rb_file_writable_p(0, argv[1]);
5100
5101 case 'W':
5102 return rb_file_writable_real_p(0, argv[1]);
5103
5104 case 'x':
5105 return rb_file_executable_p(0, argv[1]);
5106
5107 case 'X':
5108 return rb_file_executable_real_p(0, argv[1]);
5109
5110 case 'z':
5111 return rb_file_zero_p(0, argv[1]);
5112 }
5113 }
5114
5115 if (strchr("MAC", cmd)) {
5116 struct stat st;
5117 VALUE fname = argv[1];
5118
5119 CHECK(1);
5120 if (rb_stat(fname, &st) == -1) {
5121 int e = errno;
5122 FilePathValue(fname);
5123 rb_syserr_fail_path(e, fname);
5124 }
5125
5126 switch (cmd) {
5127 case 'A':
5128 return stat_atime(&st);
5129 case 'M':
5130 return stat_mtime(&st);
5131 case 'C':
5132 return stat_ctime(&st);
5133 }
5134 }
5135
5136 if (cmd == '-') {
5137 CHECK(2);
5138 return rb_file_identical_p(0, argv[1], argv[2]);
5139 }
5140
5141 if (strchr("=<>", cmd)) {
5142 struct stat st1, st2;
5143 struct timespec t1, t2;
5144
5145 CHECK(2);
5146 if (rb_stat(argv[1], &st1) < 0) return Qfalse;
5147 if (rb_stat(argv[2], &st2) < 0) return Qfalse;
5148
5149 t1 = stat_mtimespec(&st1);
5150 t2 = stat_mtimespec(&st2);
5151
5152 switch (cmd) {
5153 case '=':
5154 if (t1.tv_sec == t2.tv_sec && t1.tv_nsec == t2.tv_nsec) return Qtrue;
5155 return Qfalse;
5156
5157 case '>':
5158 if (t1.tv_sec > t2.tv_sec) return Qtrue;
5159 if (t1.tv_sec == t2.tv_sec && t1.tv_nsec > t2.tv_nsec) return Qtrue;
5160 return Qfalse;
5161
5162 case '<':
5163 if (t1.tv_sec < t2.tv_sec) return Qtrue;
5164 if (t1.tv_sec == t2.tv_sec && t1.tv_nsec < t2.tv_nsec) return Qtrue;
5165 return Qfalse;
5166 }
5167 }
5168 goto unknown;
5169 }
5170
5171
5172 /*
5173 * Document-class: File::Stat
5174 *
5175 * Objects of class <code>File::Stat</code> encapsulate common status
5176 * information for <code>File</code> objects. The information is
5177 * recorded at the moment the <code>File::Stat</code> object is
5178 * created; changes made to the file after that point will not be
5179 * reflected. <code>File::Stat</code> objects are returned by
5180 * <code>IO#stat</code>, <code>File::stat</code>,
5181 * <code>File#lstat</code>, and <code>File::lstat</code>. Many of these
5182 * methods return platform-specific values, and not all values are
5183 * meaningful on all systems. See also <code>Kernel#test</code>.
5184 */
5185
5186 static VALUE
rb_stat_s_alloc(VALUE klass)5187 rb_stat_s_alloc(VALUE klass)
5188 {
5189 return stat_new_0(klass, 0);
5190 }
5191
5192 /*
5193 * call-seq:
5194 *
5195 * File::Stat.new(file_name) -> stat
5196 *
5197 * Create a File::Stat object for the given file name (raising an
5198 * exception if the file doesn't exist).
5199 */
5200
5201 static VALUE
rb_stat_init(VALUE obj,VALUE fname)5202 rb_stat_init(VALUE obj, VALUE fname)
5203 {
5204 struct stat st, *nst;
5205
5206 FilePathValue(fname);
5207 fname = rb_str_encode_ospath(fname);
5208 if (STAT(StringValueCStr(fname), &st) == -1) {
5209 rb_sys_fail_path(fname);
5210 }
5211 if (DATA_PTR(obj)) {
5212 xfree(DATA_PTR(obj));
5213 DATA_PTR(obj) = NULL;
5214 }
5215 nst = ALLOC(struct stat);
5216 *nst = st;
5217 DATA_PTR(obj) = nst;
5218
5219 return Qnil;
5220 }
5221
5222 /* :nodoc: */
5223 static VALUE
rb_stat_init_copy(VALUE copy,VALUE orig)5224 rb_stat_init_copy(VALUE copy, VALUE orig)
5225 {
5226 struct stat *nst;
5227
5228 if (!OBJ_INIT_COPY(copy, orig)) return copy;
5229 if (DATA_PTR(copy)) {
5230 xfree(DATA_PTR(copy));
5231 DATA_PTR(copy) = 0;
5232 }
5233 if (DATA_PTR(orig)) {
5234 nst = ALLOC(struct stat);
5235 *nst = *(struct stat*)DATA_PTR(orig);
5236 DATA_PTR(copy) = nst;
5237 }
5238
5239 return copy;
5240 }
5241
5242 /*
5243 * call-seq:
5244 * stat.ftype -> string
5245 *
5246 * Identifies the type of <i>stat</i>. The return string is one of:
5247 * ``<code>file</code>'', ``<code>directory</code>'',
5248 * ``<code>characterSpecial</code>'', ``<code>blockSpecial</code>'',
5249 * ``<code>fifo</code>'', ``<code>link</code>'',
5250 * ``<code>socket</code>'', or ``<code>unknown</code>''.
5251 *
5252 * File.stat("/dev/tty").ftype #=> "characterSpecial"
5253 *
5254 */
5255
5256 static VALUE
rb_stat_ftype(VALUE obj)5257 rb_stat_ftype(VALUE obj)
5258 {
5259 return rb_file_ftype(get_stat(obj));
5260 }
5261
5262 /*
5263 * call-seq:
5264 * stat.directory? -> true or false
5265 *
5266 * Returns <code>true</code> if <i>stat</i> is a directory,
5267 * <code>false</code> otherwise.
5268 *
5269 * File.stat("testfile").directory? #=> false
5270 * File.stat(".").directory? #=> true
5271 */
5272
5273 static VALUE
rb_stat_d(VALUE obj)5274 rb_stat_d(VALUE obj)
5275 {
5276 if (S_ISDIR(get_stat(obj)->st_mode)) return Qtrue;
5277 return Qfalse;
5278 }
5279
5280 /*
5281 * call-seq:
5282 * stat.pipe? -> true or false
5283 *
5284 * Returns <code>true</code> if the operating system supports pipes and
5285 * <i>stat</i> is a pipe; <code>false</code> otherwise.
5286 */
5287
5288 static VALUE
rb_stat_p(VALUE obj)5289 rb_stat_p(VALUE obj)
5290 {
5291 #ifdef S_IFIFO
5292 if (S_ISFIFO(get_stat(obj)->st_mode)) return Qtrue;
5293
5294 #endif
5295 return Qfalse;
5296 }
5297
5298 /*
5299 * call-seq:
5300 * stat.symlink? -> true or false
5301 *
5302 * Returns <code>true</code> if <i>stat</i> is a symbolic link,
5303 * <code>false</code> if it isn't or if the operating system doesn't
5304 * support this feature. As <code>File::stat</code> automatically
5305 * follows symbolic links, <code>symlink?</code> will always be
5306 * <code>false</code> for an object returned by
5307 * <code>File::stat</code>.
5308 *
5309 * File.symlink("testfile", "alink") #=> 0
5310 * File.stat("alink").symlink? #=> false
5311 * File.lstat("alink").symlink? #=> true
5312 *
5313 */
5314
5315 static VALUE
rb_stat_l(VALUE obj)5316 rb_stat_l(VALUE obj)
5317 {
5318 #ifdef S_ISLNK
5319 if (S_ISLNK(get_stat(obj)->st_mode)) return Qtrue;
5320 #endif
5321 return Qfalse;
5322 }
5323
5324 /*
5325 * call-seq:
5326 * stat.socket? -> true or false
5327 *
5328 * Returns <code>true</code> if <i>stat</i> is a socket,
5329 * <code>false</code> if it isn't or if the operating system doesn't
5330 * support this feature.
5331 *
5332 * File.stat("testfile").socket? #=> false
5333 *
5334 */
5335
5336 static VALUE
rb_stat_S(VALUE obj)5337 rb_stat_S(VALUE obj)
5338 {
5339 #ifdef S_ISSOCK
5340 if (S_ISSOCK(get_stat(obj)->st_mode)) return Qtrue;
5341
5342 #endif
5343 return Qfalse;
5344 }
5345
5346 /*
5347 * call-seq:
5348 * stat.blockdev? -> true or false
5349 *
5350 * Returns <code>true</code> if the file is a block device,
5351 * <code>false</code> if it isn't or if the operating system doesn't
5352 * support this feature.
5353 *
5354 * File.stat("testfile").blockdev? #=> false
5355 * File.stat("/dev/hda1").blockdev? #=> true
5356 *
5357 */
5358
5359 static VALUE
rb_stat_b(VALUE obj)5360 rb_stat_b(VALUE obj)
5361 {
5362 #ifdef S_ISBLK
5363 if (S_ISBLK(get_stat(obj)->st_mode)) return Qtrue;
5364
5365 #endif
5366 return Qfalse;
5367 }
5368
5369 /*
5370 * call-seq:
5371 * stat.chardev? -> true or false
5372 *
5373 * Returns <code>true</code> if the file is a character device,
5374 * <code>false</code> if it isn't or if the operating system doesn't
5375 * support this feature.
5376 *
5377 * File.stat("/dev/tty").chardev? #=> true
5378 *
5379 */
5380
5381 static VALUE
rb_stat_c(VALUE obj)5382 rb_stat_c(VALUE obj)
5383 {
5384 if (S_ISCHR(get_stat(obj)->st_mode)) return Qtrue;
5385
5386 return Qfalse;
5387 }
5388
5389 /*
5390 * call-seq:
5391 * stat.owned? -> true or false
5392 *
5393 * Returns <code>true</code> if the effective user id of the process is
5394 * the same as the owner of <i>stat</i>.
5395 *
5396 * File.stat("testfile").owned? #=> true
5397 * File.stat("/etc/passwd").owned? #=> false
5398 *
5399 */
5400
5401 static VALUE
rb_stat_owned(VALUE obj)5402 rb_stat_owned(VALUE obj)
5403 {
5404 if (get_stat(obj)->st_uid == geteuid()) return Qtrue;
5405 return Qfalse;
5406 }
5407
5408 static VALUE
rb_stat_rowned(VALUE obj)5409 rb_stat_rowned(VALUE obj)
5410 {
5411 if (get_stat(obj)->st_uid == getuid()) return Qtrue;
5412 return Qfalse;
5413 }
5414
5415 /*
5416 * call-seq:
5417 * stat.grpowned? -> true or false
5418 *
5419 * Returns true if the effective group id of the process is the same as
5420 * the group id of <i>stat</i>. On Windows NT, returns <code>false</code>.
5421 *
5422 * File.stat("testfile").grpowned? #=> true
5423 * File.stat("/etc/passwd").grpowned? #=> false
5424 *
5425 */
5426
5427 static VALUE
rb_stat_grpowned(VALUE obj)5428 rb_stat_grpowned(VALUE obj)
5429 {
5430 #ifndef _WIN32
5431 if (rb_group_member(get_stat(obj)->st_gid)) return Qtrue;
5432 #endif
5433 return Qfalse;
5434 }
5435
5436 /*
5437 * call-seq:
5438 * stat.readable? -> true or false
5439 *
5440 * Returns <code>true</code> if <i>stat</i> is readable by the
5441 * effective user id of this process.
5442 *
5443 * File.stat("testfile").readable? #=> true
5444 *
5445 */
5446
5447 static VALUE
rb_stat_r(VALUE obj)5448 rb_stat_r(VALUE obj)
5449 {
5450 struct stat *st = get_stat(obj);
5451
5452 #ifdef USE_GETEUID
5453 if (geteuid() == 0) return Qtrue;
5454 #endif
5455 #ifdef S_IRUSR
5456 if (rb_stat_owned(obj))
5457 return st->st_mode & S_IRUSR ? Qtrue : Qfalse;
5458 #endif
5459 #ifdef S_IRGRP
5460 if (rb_stat_grpowned(obj))
5461 return st->st_mode & S_IRGRP ? Qtrue : Qfalse;
5462 #endif
5463 #ifdef S_IROTH
5464 if (!(st->st_mode & S_IROTH)) return Qfalse;
5465 #endif
5466 return Qtrue;
5467 }
5468
5469 /*
5470 * call-seq:
5471 * stat.readable_real? -> true or false
5472 *
5473 * Returns <code>true</code> if <i>stat</i> is readable by the real
5474 * user id of this process.
5475 *
5476 * File.stat("testfile").readable_real? #=> true
5477 *
5478 */
5479
5480 static VALUE
rb_stat_R(VALUE obj)5481 rb_stat_R(VALUE obj)
5482 {
5483 struct stat *st = get_stat(obj);
5484
5485 #ifdef USE_GETEUID
5486 if (getuid() == 0) return Qtrue;
5487 #endif
5488 #ifdef S_IRUSR
5489 if (rb_stat_rowned(obj))
5490 return st->st_mode & S_IRUSR ? Qtrue : Qfalse;
5491 #endif
5492 #ifdef S_IRGRP
5493 if (rb_group_member(get_stat(obj)->st_gid))
5494 return st->st_mode & S_IRGRP ? Qtrue : Qfalse;
5495 #endif
5496 #ifdef S_IROTH
5497 if (!(st->st_mode & S_IROTH)) return Qfalse;
5498 #endif
5499 return Qtrue;
5500 }
5501
5502 /*
5503 * call-seq:
5504 * stat.world_readable? -> integer or nil
5505 *
5506 * If <i>stat</i> is readable by others, returns an integer
5507 * representing the file permission bits of <i>stat</i>. Returns
5508 * <code>nil</code> otherwise. The meaning of the bits is platform
5509 * dependent; on Unix systems, see <code>stat(2)</code>.
5510 *
5511 * m = File.stat("/etc/passwd").world_readable? #=> 420
5512 * sprintf("%o", m) #=> "644"
5513 */
5514
5515 static VALUE
rb_stat_wr(VALUE obj)5516 rb_stat_wr(VALUE obj)
5517 {
5518 #ifdef S_IROTH
5519 struct stat *st = get_stat(obj);
5520 if ((st->st_mode & (S_IROTH)) == S_IROTH) {
5521 return UINT2NUM(st->st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
5522 }
5523 else {
5524 return Qnil;
5525 }
5526 #endif
5527 }
5528
5529 /*
5530 * call-seq:
5531 * stat.writable? -> true or false
5532 *
5533 * Returns <code>true</code> if <i>stat</i> is writable by the
5534 * effective user id of this process.
5535 *
5536 * File.stat("testfile").writable? #=> true
5537 *
5538 */
5539
5540 static VALUE
rb_stat_w(VALUE obj)5541 rb_stat_w(VALUE obj)
5542 {
5543 struct stat *st = get_stat(obj);
5544
5545 #ifdef USE_GETEUID
5546 if (geteuid() == 0) return Qtrue;
5547 #endif
5548 #ifdef S_IWUSR
5549 if (rb_stat_owned(obj))
5550 return st->st_mode & S_IWUSR ? Qtrue : Qfalse;
5551 #endif
5552 #ifdef S_IWGRP
5553 if (rb_stat_grpowned(obj))
5554 return st->st_mode & S_IWGRP ? Qtrue : Qfalse;
5555 #endif
5556 #ifdef S_IWOTH
5557 if (!(st->st_mode & S_IWOTH)) return Qfalse;
5558 #endif
5559 return Qtrue;
5560 }
5561
5562 /*
5563 * call-seq:
5564 * stat.writable_real? -> true or false
5565 *
5566 * Returns <code>true</code> if <i>stat</i> is writable by the real
5567 * user id of this process.
5568 *
5569 * File.stat("testfile").writable_real? #=> true
5570 *
5571 */
5572
5573 static VALUE
rb_stat_W(VALUE obj)5574 rb_stat_W(VALUE obj)
5575 {
5576 struct stat *st = get_stat(obj);
5577
5578 #ifdef USE_GETEUID
5579 if (getuid() == 0) return Qtrue;
5580 #endif
5581 #ifdef S_IWUSR
5582 if (rb_stat_rowned(obj))
5583 return st->st_mode & S_IWUSR ? Qtrue : Qfalse;
5584 #endif
5585 #ifdef S_IWGRP
5586 if (rb_group_member(get_stat(obj)->st_gid))
5587 return st->st_mode & S_IWGRP ? Qtrue : Qfalse;
5588 #endif
5589 #ifdef S_IWOTH
5590 if (!(st->st_mode & S_IWOTH)) return Qfalse;
5591 #endif
5592 return Qtrue;
5593 }
5594
5595 /*
5596 * call-seq:
5597 * stat.world_writable? -> integer or nil
5598 *
5599 * If <i>stat</i> is writable by others, returns an integer
5600 * representing the file permission bits of <i>stat</i>. Returns
5601 * <code>nil</code> otherwise. The meaning of the bits is platform
5602 * dependent; on Unix systems, see <code>stat(2)</code>.
5603 *
5604 * m = File.stat("/tmp").world_writable? #=> 511
5605 * sprintf("%o", m) #=> "777"
5606 */
5607
5608 static VALUE
rb_stat_ww(VALUE obj)5609 rb_stat_ww(VALUE obj)
5610 {
5611 #ifdef S_IROTH
5612 struct stat *st = get_stat(obj);
5613 if ((st->st_mode & (S_IWOTH)) == S_IWOTH) {
5614 return UINT2NUM(st->st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
5615 }
5616 else {
5617 return Qnil;
5618 }
5619 #endif
5620 }
5621
5622 /*
5623 * call-seq:
5624 * stat.executable? -> true or false
5625 *
5626 * Returns <code>true</code> if <i>stat</i> is executable or if the
5627 * operating system doesn't distinguish executable files from
5628 * nonexecutable files. The tests are made using the effective owner of
5629 * the process.
5630 *
5631 * File.stat("testfile").executable? #=> false
5632 *
5633 */
5634
5635 static VALUE
rb_stat_x(VALUE obj)5636 rb_stat_x(VALUE obj)
5637 {
5638 struct stat *st = get_stat(obj);
5639
5640 #ifdef USE_GETEUID
5641 if (geteuid() == 0) {
5642 return st->st_mode & S_IXUGO ? Qtrue : Qfalse;
5643 }
5644 #endif
5645 #ifdef S_IXUSR
5646 if (rb_stat_owned(obj))
5647 return st->st_mode & S_IXUSR ? Qtrue : Qfalse;
5648 #endif
5649 #ifdef S_IXGRP
5650 if (rb_stat_grpowned(obj))
5651 return st->st_mode & S_IXGRP ? Qtrue : Qfalse;
5652 #endif
5653 #ifdef S_IXOTH
5654 if (!(st->st_mode & S_IXOTH)) return Qfalse;
5655 #endif
5656 return Qtrue;
5657 }
5658
5659 /*
5660 * call-seq:
5661 * stat.executable_real? -> true or false
5662 *
5663 * Same as <code>executable?</code>, but tests using the real owner of
5664 * the process.
5665 */
5666
5667 static VALUE
rb_stat_X(VALUE obj)5668 rb_stat_X(VALUE obj)
5669 {
5670 struct stat *st = get_stat(obj);
5671
5672 #ifdef USE_GETEUID
5673 if (getuid() == 0) {
5674 return st->st_mode & S_IXUGO ? Qtrue : Qfalse;
5675 }
5676 #endif
5677 #ifdef S_IXUSR
5678 if (rb_stat_rowned(obj))
5679 return st->st_mode & S_IXUSR ? Qtrue : Qfalse;
5680 #endif
5681 #ifdef S_IXGRP
5682 if (rb_group_member(get_stat(obj)->st_gid))
5683 return st->st_mode & S_IXGRP ? Qtrue : Qfalse;
5684 #endif
5685 #ifdef S_IXOTH
5686 if (!(st->st_mode & S_IXOTH)) return Qfalse;
5687 #endif
5688 return Qtrue;
5689 }
5690
5691 /*
5692 * call-seq:
5693 * stat.file? -> true or false
5694 *
5695 * Returns <code>true</code> if <i>stat</i> is a regular file (not
5696 * a device file, pipe, socket, etc.).
5697 *
5698 * File.stat("testfile").file? #=> true
5699 *
5700 */
5701
5702 static VALUE
rb_stat_f(VALUE obj)5703 rb_stat_f(VALUE obj)
5704 {
5705 if (S_ISREG(get_stat(obj)->st_mode)) return Qtrue;
5706 return Qfalse;
5707 }
5708
5709 /*
5710 * call-seq:
5711 * stat.zero? -> true or false
5712 *
5713 * Returns <code>true</code> if <i>stat</i> is a zero-length file;
5714 * <code>false</code> otherwise.
5715 *
5716 * File.stat("testfile").zero? #=> false
5717 *
5718 */
5719
5720 static VALUE
rb_stat_z(VALUE obj)5721 rb_stat_z(VALUE obj)
5722 {
5723 if (get_stat(obj)->st_size == 0) return Qtrue;
5724 return Qfalse;
5725 }
5726
5727 /*
5728 * call-seq:
5729 * state.size -> integer
5730 *
5731 * Returns the size of <i>stat</i> in bytes.
5732 *
5733 * File.stat("testfile").size #=> 66
5734 *
5735 */
5736
5737 static VALUE
rb_stat_s(VALUE obj)5738 rb_stat_s(VALUE obj)
5739 {
5740 off_t size = get_stat(obj)->st_size;
5741
5742 if (size == 0) return Qnil;
5743 return OFFT2NUM(size);
5744 }
5745
5746 /*
5747 * call-seq:
5748 * stat.setuid? -> true or false
5749 *
5750 * Returns <code>true</code> if <i>stat</i> has the set-user-id
5751 * permission bit set, <code>false</code> if it doesn't or if the
5752 * operating system doesn't support this feature.
5753 *
5754 * File.stat("/bin/su").setuid? #=> true
5755 */
5756
5757 static VALUE
rb_stat_suid(VALUE obj)5758 rb_stat_suid(VALUE obj)
5759 {
5760 #ifdef S_ISUID
5761 if (get_stat(obj)->st_mode & S_ISUID) return Qtrue;
5762 #endif
5763 return Qfalse;
5764 }
5765
5766 /*
5767 * call-seq:
5768 * stat.setgid? -> true or false
5769 *
5770 * Returns <code>true</code> if <i>stat</i> has the set-group-id
5771 * permission bit set, <code>false</code> if it doesn't or if the
5772 * operating system doesn't support this feature.
5773 *
5774 * File.stat("/usr/sbin/lpc").setgid? #=> true
5775 *
5776 */
5777
5778 static VALUE
rb_stat_sgid(VALUE obj)5779 rb_stat_sgid(VALUE obj)
5780 {
5781 #ifdef S_ISGID
5782 if (get_stat(obj)->st_mode & S_ISGID) return Qtrue;
5783 #endif
5784 return Qfalse;
5785 }
5786
5787 /*
5788 * call-seq:
5789 * stat.sticky? -> true or false
5790 *
5791 * Returns <code>true</code> if <i>stat</i> has its sticky bit set,
5792 * <code>false</code> if it doesn't or if the operating system doesn't
5793 * support this feature.
5794 *
5795 * File.stat("testfile").sticky? #=> false
5796 *
5797 */
5798
5799 static VALUE
rb_stat_sticky(VALUE obj)5800 rb_stat_sticky(VALUE obj)
5801 {
5802 #ifdef S_ISVTX
5803 if (get_stat(obj)->st_mode & S_ISVTX) return Qtrue;
5804 #endif
5805 return Qfalse;
5806 }
5807
5808 #if !defined HAVE_MKFIFO && defined HAVE_MKNOD && defined S_IFIFO
5809 #define mkfifo(path, mode) mknod(path, (mode)&~S_IFMT|S_IFIFO, 0)
5810 #define HAVE_MKFIFO
5811 #endif
5812
5813 #ifdef HAVE_MKFIFO
5814 struct mkfifo_arg {
5815 const char *path;
5816 mode_t mode;
5817 };
5818
5819 static void *
nogvl_mkfifo(void * ptr)5820 nogvl_mkfifo(void *ptr)
5821 {
5822 struct mkfifo_arg *ma = ptr;
5823
5824 return (void *)(VALUE)mkfifo(ma->path, ma->mode);
5825 }
5826
5827 /*
5828 * call-seq:
5829 * File.mkfifo(file_name, mode=0666) => 0
5830 *
5831 * Creates a FIFO special file with name _file_name_. _mode_
5832 * specifies the FIFO's permissions. It is modified by the process's
5833 * umask in the usual way: the permissions of the created file are
5834 * (mode & ~umask).
5835 */
5836
5837 static VALUE
rb_file_s_mkfifo(int argc,VALUE * argv)5838 rb_file_s_mkfifo(int argc, VALUE *argv)
5839 {
5840 VALUE path;
5841 struct mkfifo_arg ma;
5842
5843 ma.mode = 0666;
5844 rb_check_arity(argc, 1, 2);
5845 if (argc > 1) {
5846 ma.mode = NUM2MODET(argv[1]);
5847 }
5848 path = argv[0];
5849 FilePathValue(path);
5850 path = rb_str_encode_ospath(path);
5851 ma.path = RSTRING_PTR(path);
5852 if (rb_thread_call_without_gvl(nogvl_mkfifo, &ma, RUBY_UBF_IO, 0)) {
5853 rb_sys_fail_path(path);
5854 }
5855 return INT2FIX(0);
5856 }
5857 #else
5858 #define rb_file_s_mkfifo rb_f_notimplement
5859 #endif
5860
5861 static VALUE rb_mFConst;
5862
5863 void
rb_file_const(const char * name,VALUE value)5864 rb_file_const(const char *name, VALUE value)
5865 {
5866 rb_define_const(rb_mFConst, name, value);
5867 }
5868
5869 int
rb_is_absolute_path(const char * path)5870 rb_is_absolute_path(const char *path)
5871 {
5872 #ifdef DOSISH_DRIVE_LETTER
5873 if (has_drive_letter(path) && isdirsep(path[2])) return 1;
5874 #endif
5875 #ifdef DOSISH_UNC
5876 if (isdirsep(path[0]) && isdirsep(path[1])) return 1;
5877 #endif
5878 #ifndef DOSISH
5879 if (path[0] == '/') return 1;
5880 #endif
5881 return 0;
5882 }
5883
5884 #ifndef ENABLE_PATH_CHECK
5885 # if defined DOSISH || defined __CYGWIN__
5886 # define ENABLE_PATH_CHECK 0
5887 # else
5888 # define ENABLE_PATH_CHECK 1
5889 # endif
5890 #endif
5891
5892 #if ENABLE_PATH_CHECK
5893 static int
path_check_0(VALUE path,int execpath)5894 path_check_0(VALUE path, int execpath)
5895 {
5896 struct stat st;
5897 const char *p0 = StringValueCStr(path);
5898 const char *e0;
5899 rb_encoding *enc;
5900 char *p = 0, *s;
5901
5902 if (!rb_is_absolute_path(p0)) {
5903 char *buf = ruby_getcwd();
5904 VALUE newpath;
5905
5906 newpath = rb_str_new2(buf);
5907 xfree(buf);
5908
5909 rb_str_cat2(newpath, "/");
5910 rb_str_cat2(newpath, p0);
5911 path = newpath;
5912 p0 = RSTRING_PTR(path);
5913 }
5914 e0 = p0 + RSTRING_LEN(path);
5915 enc = rb_enc_get(path);
5916 for (;;) {
5917 #ifndef S_IWOTH
5918 # define S_IWOTH 002
5919 #endif
5920 if (STAT(p0, &st) == 0 && S_ISDIR(st.st_mode) && (st.st_mode & S_IWOTH)
5921 #ifdef S_ISVTX
5922 && !(p && execpath && (st.st_mode & S_ISVTX))
5923 #endif
5924 && !access(p0, W_OK)) {
5925 rb_enc_warn(enc, "Insecure world writable dir %s in %sPATH, mode 0%"
5926 PRI_MODET_PREFIX"o",
5927 p0, (execpath ? "" : "LOAD_"), st.st_mode);
5928 if (p) *p = '/';
5929 RB_GC_GUARD(path);
5930 return 0;
5931 }
5932 s = strrdirsep(p0, e0, enc);
5933 if (p) *p = '/';
5934 if (!s || s == p0) return 1;
5935 p = s;
5936 e0 = p;
5937 *p = '\0';
5938 }
5939 }
5940 #endif
5941
5942 #if ENABLE_PATH_CHECK
5943 #define fpath_check(path) path_check_0((path), FALSE)
5944 #else
5945 #define fpath_check(path) 1
5946 #endif
5947
5948 int
rb_path_check(const char * path)5949 rb_path_check(const char *path)
5950 {
5951 #if ENABLE_PATH_CHECK
5952 const char *p0, *p, *pend;
5953 const char sep = PATH_SEP_CHAR;
5954
5955 if (!path) return 1;
5956
5957 pend = path + strlen(path);
5958 p0 = path;
5959 p = strchr(path, sep);
5960 if (!p) p = pend;
5961
5962 for (;;) {
5963 if (!path_check_0(rb_str_new(p0, p - p0), TRUE)) {
5964 return 0; /* not safe */
5965 }
5966 p0 = p + 1;
5967 if (p0 > pend) break;
5968 p = strchr(p0, sep);
5969 if (!p) p = pend;
5970 }
5971 #endif
5972 return 1;
5973 }
5974
5975 int
ruby_is_fd_loadable(int fd)5976 ruby_is_fd_loadable(int fd)
5977 {
5978 #ifdef _WIN32
5979 return 1;
5980 #else
5981 struct stat st;
5982
5983 if (fstat(fd, &st) < 0)
5984 return 0;
5985
5986 if (S_ISREG(st.st_mode))
5987 return 1;
5988
5989 if (S_ISFIFO(st.st_mode) || S_ISCHR(st.st_mode))
5990 return -1;
5991
5992 if (S_ISDIR(st.st_mode))
5993 errno = EISDIR;
5994 else
5995 errno = ENXIO;
5996
5997 return 0;
5998 #endif
5999 }
6000
6001 #ifndef _WIN32
6002 int
rb_file_load_ok(const char * path)6003 rb_file_load_ok(const char *path)
6004 {
6005 int ret = 1;
6006 /*
6007 open(2) may block if path is FIFO and it's empty. Let's use O_NONBLOCK.
6008 FIXME: Why O_NDELAY is checked?
6009 */
6010 int mode = (O_RDONLY |
6011 #if defined O_NONBLOCK
6012 O_NONBLOCK |
6013 #elif defined O_NDELAY
6014 O_NDELAY |
6015 #endif
6016 0);
6017 int fd = rb_cloexec_open(path, mode, 0);
6018 if (fd == -1) return 0;
6019 rb_update_max_fd(fd);
6020 ret = ruby_is_fd_loadable(fd);
6021 (void)close(fd);
6022 return ret;
6023 }
6024 #endif
6025
6026 static int
is_explicit_relative(const char * path)6027 is_explicit_relative(const char *path)
6028 {
6029 if (*path++ != '.') return 0;
6030 if (*path == '.') path++;
6031 return isdirsep(*path);
6032 }
6033
6034 static VALUE
copy_path_class(VALUE path,VALUE orig)6035 copy_path_class(VALUE path, VALUE orig)
6036 {
6037 str_shrink(path);
6038 RBASIC_SET_CLASS(path, rb_obj_class(orig));
6039 OBJ_FREEZE(path);
6040 return path;
6041 }
6042
6043 int
rb_find_file_ext(VALUE * filep,const char * const * ext)6044 rb_find_file_ext(VALUE *filep, const char *const *ext)
6045 {
6046 return rb_find_file_ext_safe(filep, ext, rb_safe_level());
6047 }
6048
6049 int
rb_find_file_ext_safe(VALUE * filep,const char * const * ext,int safe_level)6050 rb_find_file_ext_safe(VALUE *filep, const char *const *ext, int safe_level)
6051 {
6052 const char *f = StringValueCStr(*filep);
6053 VALUE fname = *filep, load_path, tmp;
6054 long i, j, fnlen;
6055 int expanded = 0;
6056
6057 if (!ext[0]) return 0;
6058
6059 if (f[0] == '~') {
6060 fname = file_expand_path_1(fname);
6061 if (safe_level >= 1 && OBJ_TAINTED(fname)) {
6062 rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
6063 }
6064 f = RSTRING_PTR(fname);
6065 *filep = fname;
6066 expanded = 1;
6067 }
6068
6069 if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) {
6070 if (safe_level >= 1 && !fpath_check(fname)) {
6071 rb_raise(rb_eSecurityError, "loading from unsafe path %s", f);
6072 }
6073 if (!expanded) fname = file_expand_path_1(fname);
6074 fnlen = RSTRING_LEN(fname);
6075 for (i=0; ext[i]; i++) {
6076 rb_str_cat2(fname, ext[i]);
6077 if (rb_file_load_ok(RSTRING_PTR(fname))) {
6078 *filep = copy_path_class(fname, *filep);
6079 return (int)(i+1);
6080 }
6081 rb_str_set_len(fname, fnlen);
6082 }
6083 return 0;
6084 }
6085
6086 RB_GC_GUARD(load_path) = rb_get_expanded_load_path();
6087 if (!load_path) return 0;
6088
6089 fname = rb_str_dup(*filep);
6090 RBASIC_CLEAR_CLASS(fname);
6091 fnlen = RSTRING_LEN(fname);
6092 tmp = rb_str_tmp_new(MAXPATHLEN + 2);
6093 rb_enc_associate_index(tmp, rb_usascii_encindex());
6094 for (j=0; ext[j]; j++) {
6095 rb_str_cat2(fname, ext[j]);
6096 for (i = 0; i < RARRAY_LEN(load_path); i++) {
6097 VALUE str = RARRAY_AREF(load_path, i);
6098
6099 RB_GC_GUARD(str) = rb_get_path_check(str, safe_level);
6100 if (RSTRING_LEN(str) == 0) continue;
6101 rb_file_expand_path_internal(fname, str, 0, 0, tmp);
6102 if (rb_file_load_ok(RSTRING_PTR(tmp))) {
6103 *filep = copy_path_class(tmp, *filep);
6104 return (int)(j+1);
6105 }
6106 FL_UNSET(tmp, FL_TAINT);
6107 }
6108 rb_str_set_len(fname, fnlen);
6109 }
6110 rb_str_resize(tmp, 0);
6111 RB_GC_GUARD(load_path);
6112 return 0;
6113 }
6114
6115 VALUE
rb_find_file(VALUE path)6116 rb_find_file(VALUE path)
6117 {
6118 return rb_find_file_safe(path, rb_safe_level());
6119 }
6120
6121 VALUE
rb_find_file_safe(VALUE path,int safe_level)6122 rb_find_file_safe(VALUE path, int safe_level)
6123 {
6124 VALUE tmp, load_path;
6125 const char *f = StringValueCStr(path);
6126 int expanded = 0;
6127
6128 if (f[0] == '~') {
6129 tmp = file_expand_path_1(path);
6130 if (safe_level >= 1 && OBJ_TAINTED(tmp)) {
6131 rb_raise(rb_eSecurityError, "loading from unsafe file %"PRIsVALUE, tmp);
6132 }
6133 path = copy_path_class(tmp, path);
6134 f = RSTRING_PTR(path);
6135 expanded = 1;
6136 }
6137
6138 if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) {
6139 if (safe_level >= 1 && !fpath_check(path)) {
6140 rb_raise(rb_eSecurityError, "loading from unsafe path %"PRIsVALUE, path);
6141 }
6142 if (!rb_file_load_ok(f)) return 0;
6143 if (!expanded)
6144 path = copy_path_class(file_expand_path_1(path), path);
6145 return path;
6146 }
6147
6148 RB_GC_GUARD(load_path) = rb_get_expanded_load_path();
6149 if (load_path) {
6150 long i;
6151
6152 tmp = rb_str_tmp_new(MAXPATHLEN + 2);
6153 rb_enc_associate_index(tmp, rb_usascii_encindex());
6154 for (i = 0; i < RARRAY_LEN(load_path); i++) {
6155 VALUE str = RARRAY_AREF(load_path, i);
6156 RB_GC_GUARD(str) = rb_get_path_check(str, safe_level);
6157 if (RSTRING_LEN(str) > 0) {
6158 rb_file_expand_path_internal(path, str, 0, 0, tmp);
6159 f = RSTRING_PTR(tmp);
6160 if (rb_file_load_ok(f)) goto found;
6161 }
6162 }
6163 rb_str_resize(tmp, 0);
6164 return 0;
6165 }
6166 else {
6167 return 0; /* no path, no load */
6168 }
6169
6170 found:
6171 if (safe_level >= 1 && !fpath_check(tmp)) {
6172 rb_raise(rb_eSecurityError, "loading from unsafe file %"PRIsVALUE, tmp);
6173 }
6174
6175 return copy_path_class(tmp, path);
6176 }
6177
6178 static void
define_filetest_function(const char * name,VALUE (* func)(ANYARGS),int argc)6179 define_filetest_function(const char *name, VALUE (*func)(ANYARGS), int argc)
6180 {
6181 rb_define_module_function(rb_mFileTest, name, func, argc);
6182 rb_define_singleton_method(rb_cFile, name, func, argc);
6183 }
6184
6185 const char ruby_null_device[] =
6186 #if defined DOSISH
6187 "NUL"
6188 #elif defined AMIGA || defined __amigaos__
6189 "NIL"
6190 #elif defined __VMS
6191 "NL:"
6192 #else
6193 "/dev/null"
6194 #endif
6195 ;
6196
6197 /*
6198 * A <code>File</code> is an abstraction of any file object accessible
6199 * by the program and is closely associated with class <code>IO</code>.
6200 * <code>File</code> includes the methods of module
6201 * <code>FileTest</code> as class methods, allowing you to write (for
6202 * example) <code>File.exist?("foo")</code>.
6203 *
6204 * In the description of File methods,
6205 * <em>permission bits</em> are a platform-specific
6206 * set of bits that indicate permissions of a file. On Unix-based
6207 * systems, permissions are viewed as a set of three octets, for the
6208 * owner, the group, and the rest of the world. For each of these
6209 * entities, permissions may be set to read, write, or execute the
6210 * file:
6211 *
6212 * The permission bits <code>0644</code> (in octal) would thus be
6213 * interpreted as read/write for owner, and read-only for group and
6214 * other. Higher-order bits may also be used to indicate the type of
6215 * file (plain, directory, pipe, socket, and so on) and various other
6216 * special features. If the permissions are for a directory, the
6217 * meaning of the execute bit changes; when set the directory can be
6218 * searched.
6219 *
6220 * On non-Posix operating systems, there may be only the ability to
6221 * make a file read-only or read-write. In this case, the remaining
6222 * permission bits will be synthesized to resemble typical values. For
6223 * instance, on Windows NT the default permission bits are
6224 * <code>0644</code>, which means read/write for owner, read-only for
6225 * all others. The only change that can be made is to make the file
6226 * read-only, which is reported as <code>0444</code>.
6227 *
6228 * Various constants for the methods in File can be found in File::Constants.
6229 */
6230
6231 void
Init_File(void)6232 Init_File(void)
6233 {
6234 VALUE separator;
6235
6236 rb_mFileTest = rb_define_module("FileTest");
6237 rb_cFile = rb_define_class("File", rb_cIO);
6238
6239 define_filetest_function("directory?", rb_file_directory_p, 1);
6240 define_filetest_function("exist?", rb_file_exist_p, 1);
6241 define_filetest_function("exists?", rb_file_exists_p, 1);
6242 define_filetest_function("readable?", rb_file_readable_p, 1);
6243 define_filetest_function("readable_real?", rb_file_readable_real_p, 1);
6244 define_filetest_function("world_readable?", rb_file_world_readable_p, 1);
6245 define_filetest_function("writable?", rb_file_writable_p, 1);
6246 define_filetest_function("writable_real?", rb_file_writable_real_p, 1);
6247 define_filetest_function("world_writable?", rb_file_world_writable_p, 1);
6248 define_filetest_function("executable?", rb_file_executable_p, 1);
6249 define_filetest_function("executable_real?", rb_file_executable_real_p, 1);
6250 define_filetest_function("file?", rb_file_file_p, 1);
6251 define_filetest_function("zero?", rb_file_zero_p, 1);
6252 define_filetest_function("empty?", rb_file_zero_p, 1);
6253 define_filetest_function("size?", rb_file_size_p, 1);
6254 define_filetest_function("size", rb_file_s_size, 1);
6255 define_filetest_function("owned?", rb_file_owned_p, 1);
6256 define_filetest_function("grpowned?", rb_file_grpowned_p, 1);
6257
6258 define_filetest_function("pipe?", rb_file_pipe_p, 1);
6259 define_filetest_function("symlink?", rb_file_symlink_p, 1);
6260 define_filetest_function("socket?", rb_file_socket_p, 1);
6261
6262 define_filetest_function("blockdev?", rb_file_blockdev_p, 1);
6263 define_filetest_function("chardev?", rb_file_chardev_p, 1);
6264
6265 define_filetest_function("setuid?", rb_file_suid_p, 1);
6266 define_filetest_function("setgid?", rb_file_sgid_p, 1);
6267 define_filetest_function("sticky?", rb_file_sticky_p, 1);
6268
6269 define_filetest_function("identical?", rb_file_identical_p, 2);
6270
6271 rb_define_singleton_method(rb_cFile, "stat", rb_file_s_stat, 1);
6272 rb_define_singleton_method(rb_cFile, "lstat", rb_file_s_lstat, 1);
6273 rb_define_singleton_method(rb_cFile, "ftype", rb_file_s_ftype, 1);
6274
6275 rb_define_singleton_method(rb_cFile, "atime", rb_file_s_atime, 1);
6276 rb_define_singleton_method(rb_cFile, "mtime", rb_file_s_mtime, 1);
6277 rb_define_singleton_method(rb_cFile, "ctime", rb_file_s_ctime, 1);
6278 rb_define_singleton_method(rb_cFile, "birthtime", rb_file_s_birthtime, 1);
6279
6280 rb_define_singleton_method(rb_cFile, "utime", rb_file_s_utime, -1);
6281 rb_define_singleton_method(rb_cFile, "chmod", rb_file_s_chmod, -1);
6282 rb_define_singleton_method(rb_cFile, "chown", rb_file_s_chown, -1);
6283 rb_define_singleton_method(rb_cFile, "lchmod", rb_file_s_lchmod, -1);
6284 rb_define_singleton_method(rb_cFile, "lchown", rb_file_s_lchown, -1);
6285 rb_define_singleton_method(rb_cFile, "lutime", rb_file_s_lutime, -1);
6286
6287 rb_define_singleton_method(rb_cFile, "link", rb_file_s_link, 2);
6288 rb_define_singleton_method(rb_cFile, "symlink", rb_file_s_symlink, 2);
6289 rb_define_singleton_method(rb_cFile, "readlink", rb_file_s_readlink, 1);
6290
6291 rb_define_singleton_method(rb_cFile, "unlink", rb_file_s_unlink, -1);
6292 rb_define_singleton_method(rb_cFile, "delete", rb_file_s_unlink, -1);
6293 rb_define_singleton_method(rb_cFile, "rename", rb_file_s_rename, 2);
6294 rb_define_singleton_method(rb_cFile, "umask", rb_file_s_umask, -1);
6295 rb_define_singleton_method(rb_cFile, "truncate", rb_file_s_truncate, 2);
6296 rb_define_singleton_method(rb_cFile, "mkfifo", rb_file_s_mkfifo, -1);
6297 rb_define_singleton_method(rb_cFile, "expand_path", rb_file_s_expand_path, -1);
6298 rb_define_singleton_method(rb_cFile, "absolute_path", rb_file_s_absolute_path, -1);
6299 rb_define_singleton_method(rb_cFile, "realpath", rb_file_s_realpath, -1);
6300 rb_define_singleton_method(rb_cFile, "realdirpath", rb_file_s_realdirpath, -1);
6301 rb_define_singleton_method(rb_cFile, "basename", rb_file_s_basename, -1);
6302 rb_define_singleton_method(rb_cFile, "dirname", rb_file_s_dirname, 1);
6303 rb_define_singleton_method(rb_cFile, "extname", rb_file_s_extname, 1);
6304 rb_define_singleton_method(rb_cFile, "path", rb_file_s_path, 1);
6305
6306 separator = rb_fstring_lit("/");
6307 /* separates directory parts in path */
6308 rb_define_const(rb_cFile, "Separator", separator);
6309 /* separates directory parts in path */
6310 rb_define_const(rb_cFile, "SEPARATOR", separator);
6311 rb_define_singleton_method(rb_cFile, "split", rb_file_s_split, 1);
6312 rb_define_singleton_method(rb_cFile, "join", rb_file_s_join, -2);
6313
6314 #ifdef DOSISH
6315 /* platform specific alternative separator */
6316 rb_define_const(rb_cFile, "ALT_SEPARATOR", rb_obj_freeze(rb_usascii_str_new2(file_alt_separator)));
6317 #else
6318 rb_define_const(rb_cFile, "ALT_SEPARATOR", Qnil);
6319 #endif
6320 /* path list separator */
6321 rb_define_const(rb_cFile, "PATH_SEPARATOR", rb_fstring_cstr(PATH_SEP));
6322
6323 rb_define_method(rb_cIO, "stat", rb_io_stat, 0); /* this is IO's method */
6324 rb_define_method(rb_cFile, "lstat", rb_file_lstat, 0);
6325
6326 rb_define_method(rb_cFile, "atime", rb_file_atime, 0);
6327 rb_define_method(rb_cFile, "mtime", rb_file_mtime, 0);
6328 rb_define_method(rb_cFile, "ctime", rb_file_ctime, 0);
6329 rb_define_method(rb_cFile, "birthtime", rb_file_birthtime, 0);
6330 rb_define_method(rb_cFile, "size", rb_file_size, 0);
6331
6332 rb_define_method(rb_cFile, "chmod", rb_file_chmod, 1);
6333 rb_define_method(rb_cFile, "chown", rb_file_chown, 2);
6334 rb_define_method(rb_cFile, "truncate", rb_file_truncate, 1);
6335
6336 rb_define_method(rb_cFile, "flock", rb_file_flock, 1);
6337
6338 /*
6339 * Document-module: File::Constants
6340 *
6341 * File::Constants provides file-related constants. All possible
6342 * file constants are listed in the documentation but they may not all
6343 * be present on your platform.
6344 *
6345 * If the underlying platform doesn't define a constant the corresponding
6346 * Ruby constant is not defined.
6347 *
6348 * Your platform documentations (e.g. man open(2)) may describe more
6349 * detailed information.
6350 */
6351 rb_mFConst = rb_define_module_under(rb_cFile, "Constants");
6352 rb_include_module(rb_cIO, rb_mFConst);
6353
6354 /* open for reading only */
6355 rb_define_const(rb_mFConst, "RDONLY", INT2FIX(O_RDONLY));
6356 /* open for writing only */
6357 rb_define_const(rb_mFConst, "WRONLY", INT2FIX(O_WRONLY));
6358 /* open for reading and writing */
6359 rb_define_const(rb_mFConst, "RDWR", INT2FIX(O_RDWR));
6360 /* append on each write */
6361 rb_define_const(rb_mFConst, "APPEND", INT2FIX(O_APPEND));
6362 /* create file if it does not exist */
6363 rb_define_const(rb_mFConst, "CREAT", INT2FIX(O_CREAT));
6364 /* error if CREAT and the file exists */
6365 rb_define_const(rb_mFConst, "EXCL", INT2FIX(O_EXCL));
6366 #if defined(O_NDELAY) || defined(O_NONBLOCK)
6367 # ifndef O_NONBLOCK
6368 # define O_NONBLOCK O_NDELAY
6369 # endif
6370 /* do not block on open or for data to become available */
6371 rb_define_const(rb_mFConst, "NONBLOCK", INT2FIX(O_NONBLOCK));
6372 #endif
6373 /* truncate size to 0 */
6374 rb_define_const(rb_mFConst, "TRUNC", INT2FIX(O_TRUNC));
6375 #ifdef O_NOCTTY
6376 /* not to make opened IO the controlling terminal device */
6377 rb_define_const(rb_mFConst, "NOCTTY", INT2FIX(O_NOCTTY));
6378 #endif
6379 #ifndef O_BINARY
6380 # define O_BINARY 0
6381 #endif
6382 /* disable line code conversion */
6383 rb_define_const(rb_mFConst, "BINARY", INT2FIX(O_BINARY));
6384 #ifndef O_SHARE_DELETE
6385 # define O_SHARE_DELETE 0
6386 #endif
6387 /* can delete opened file */
6388 rb_define_const(rb_mFConst, "SHARE_DELETE", INT2FIX(O_SHARE_DELETE));
6389 #ifdef O_SYNC
6390 /* any write operation perform synchronously */
6391 rb_define_const(rb_mFConst, "SYNC", INT2FIX(O_SYNC));
6392 #endif
6393 #ifdef O_DSYNC
6394 /* any write operation perform synchronously except some meta data */
6395 rb_define_const(rb_mFConst, "DSYNC", INT2FIX(O_DSYNC));
6396 #endif
6397 #ifdef O_RSYNC
6398 /* any read operation perform synchronously. used with SYNC or DSYNC. */
6399 rb_define_const(rb_mFConst, "RSYNC", INT2FIX(O_RSYNC));
6400 #endif
6401 #ifdef O_NOFOLLOW
6402 /* do not follow symlinks */
6403 rb_define_const(rb_mFConst, "NOFOLLOW", INT2FIX(O_NOFOLLOW)); /* FreeBSD, Linux */
6404 #endif
6405 #ifdef O_NOATIME
6406 /* do not change atime */
6407 rb_define_const(rb_mFConst, "NOATIME", INT2FIX(O_NOATIME)); /* Linux */
6408 #endif
6409 #ifdef O_DIRECT
6410 /* Try to minimize cache effects of the I/O to and from this file. */
6411 rb_define_const(rb_mFConst, "DIRECT", INT2FIX(O_DIRECT));
6412 #endif
6413 #ifdef O_TMPFILE
6414 /* Create an unnamed temporary file */
6415 rb_define_const(rb_mFConst, "TMPFILE", INT2FIX(O_TMPFILE));
6416 #endif
6417
6418 /* shared lock. see File#flock */
6419 rb_define_const(rb_mFConst, "LOCK_SH", INT2FIX(LOCK_SH));
6420 /* exclusive lock. see File#flock */
6421 rb_define_const(rb_mFConst, "LOCK_EX", INT2FIX(LOCK_EX));
6422 /* unlock. see File#flock */
6423 rb_define_const(rb_mFConst, "LOCK_UN", INT2FIX(LOCK_UN));
6424 /* non-blocking lock. used with LOCK_SH or LOCK_EX. see File#flock */
6425 rb_define_const(rb_mFConst, "LOCK_NB", INT2FIX(LOCK_NB));
6426
6427 /* Name of the null device */
6428 rb_define_const(rb_mFConst, "NULL", rb_fstring_cstr(ruby_null_device));
6429
6430 rb_define_method(rb_cFile, "path", rb_file_path, 0);
6431 rb_define_method(rb_cFile, "to_path", rb_file_path, 0);
6432 rb_define_global_function("test", rb_f_test, -1);
6433
6434 rb_cStat = rb_define_class_under(rb_cFile, "Stat", rb_cObject);
6435 rb_define_alloc_func(rb_cStat, rb_stat_s_alloc);
6436 rb_define_method(rb_cStat, "initialize", rb_stat_init, 1);
6437 rb_define_method(rb_cStat, "initialize_copy", rb_stat_init_copy, 1);
6438
6439 rb_include_module(rb_cStat, rb_mComparable);
6440
6441 rb_define_method(rb_cStat, "<=>", rb_stat_cmp, 1);
6442
6443 rb_define_method(rb_cStat, "dev", rb_stat_dev, 0);
6444 rb_define_method(rb_cStat, "dev_major", rb_stat_dev_major, 0);
6445 rb_define_method(rb_cStat, "dev_minor", rb_stat_dev_minor, 0);
6446 rb_define_method(rb_cStat, "ino", rb_stat_ino, 0);
6447 rb_define_method(rb_cStat, "mode", rb_stat_mode, 0);
6448 rb_define_method(rb_cStat, "nlink", rb_stat_nlink, 0);
6449 rb_define_method(rb_cStat, "uid", rb_stat_uid, 0);
6450 rb_define_method(rb_cStat, "gid", rb_stat_gid, 0);
6451 rb_define_method(rb_cStat, "rdev", rb_stat_rdev, 0);
6452 rb_define_method(rb_cStat, "rdev_major", rb_stat_rdev_major, 0);
6453 rb_define_method(rb_cStat, "rdev_minor", rb_stat_rdev_minor, 0);
6454 rb_define_method(rb_cStat, "size", rb_stat_size, 0);
6455 rb_define_method(rb_cStat, "blksize", rb_stat_blksize, 0);
6456 rb_define_method(rb_cStat, "blocks", rb_stat_blocks, 0);
6457 rb_define_method(rb_cStat, "atime", rb_stat_atime, 0);
6458 rb_define_method(rb_cStat, "mtime", rb_stat_mtime, 0);
6459 rb_define_method(rb_cStat, "ctime", rb_stat_ctime, 0);
6460 rb_define_method(rb_cStat, "birthtime", rb_stat_birthtime, 0);
6461
6462 rb_define_method(rb_cStat, "inspect", rb_stat_inspect, 0);
6463
6464 rb_define_method(rb_cStat, "ftype", rb_stat_ftype, 0);
6465
6466 rb_define_method(rb_cStat, "directory?", rb_stat_d, 0);
6467 rb_define_method(rb_cStat, "readable?", rb_stat_r, 0);
6468 rb_define_method(rb_cStat, "readable_real?", rb_stat_R, 0);
6469 rb_define_method(rb_cStat, "world_readable?", rb_stat_wr, 0);
6470 rb_define_method(rb_cStat, "writable?", rb_stat_w, 0);
6471 rb_define_method(rb_cStat, "writable_real?", rb_stat_W, 0);
6472 rb_define_method(rb_cStat, "world_writable?", rb_stat_ww, 0);
6473 rb_define_method(rb_cStat, "executable?", rb_stat_x, 0);
6474 rb_define_method(rb_cStat, "executable_real?", rb_stat_X, 0);
6475 rb_define_method(rb_cStat, "file?", rb_stat_f, 0);
6476 rb_define_method(rb_cStat, "zero?", rb_stat_z, 0);
6477 rb_define_method(rb_cStat, "size?", rb_stat_s, 0);
6478 rb_define_method(rb_cStat, "owned?", rb_stat_owned, 0);
6479 rb_define_method(rb_cStat, "grpowned?", rb_stat_grpowned, 0);
6480
6481 rb_define_method(rb_cStat, "pipe?", rb_stat_p, 0);
6482 rb_define_method(rb_cStat, "symlink?", rb_stat_l, 0);
6483 rb_define_method(rb_cStat, "socket?", rb_stat_S, 0);
6484
6485 rb_define_method(rb_cStat, "blockdev?", rb_stat_b, 0);
6486 rb_define_method(rb_cStat, "chardev?", rb_stat_c, 0);
6487
6488 rb_define_method(rb_cStat, "setuid?", rb_stat_suid, 0);
6489 rb_define_method(rb_cStat, "setgid?", rb_stat_sgid, 0);
6490 rb_define_method(rb_cStat, "sticky?", rb_stat_sticky, 0);
6491 }
6492