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