Ruby 3.5.0dev (2025-09-17 revision 9c85a94f5f1b553ccac3e7f232087424d1854f97)
file.c (9c85a94f5f1b553ccac3e7f232087424d1854f97)
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#include "ruby/internal/config.h"
16
17#ifdef _WIN32
18# include "missing/file.h"
19# include "ruby.h"
20#endif
21
22#include <ctype.h>
23#include <time.h>
24
25#ifdef __CYGWIN__
26# include <windows.h>
27# include <sys/cygwin.h>
28# include <wchar.h>
29#endif
30
31#ifdef __APPLE__
32# if !(defined(__has_feature) && defined(__has_attribute))
33/* Maybe a bug in SDK of Xcode 10.2.1 */
34/* In this condition, <os/availability.h> does not define
35 * API_AVAILABLE and similar, but __API_AVAILABLE and similar which
36 * are defined in <Availability.h> */
37# define API_AVAILABLE(...)
38# define API_DEPRECATED(...)
39# endif
40# include <CoreFoundation/CFString.h>
41#endif
42
43#ifdef HAVE_UNISTD_H
44# include <unistd.h>
45#endif
46
47#ifdef HAVE_SYS_TIME_H
48# include <sys/time.h>
49#endif
50
51#ifdef HAVE_SYS_FILE_H
52# include <sys/file.h>
53#else
54int flock(int, int);
55#endif
56
57#ifdef HAVE_SYS_PARAM_H
58# include <sys/param.h>
59#endif
60#ifndef MAXPATHLEN
61# define MAXPATHLEN 1024
62#endif
63
64#ifdef HAVE_UTIME_H
65# include <utime.h>
66#elif defined HAVE_SYS_UTIME_H
67# include <sys/utime.h>
68#endif
69
70#ifdef HAVE_PWD_H
71# include <pwd.h>
72#endif
73
74#ifdef HAVE_SYS_SYSMACROS_H
75# include <sys/sysmacros.h>
76#endif
77
78#include <sys/types.h>
79#include <sys/stat.h>
80
81#ifdef HAVE_SYS_MKDEV_H
82# include <sys/mkdev.h>
83#endif
84
85#if defined(HAVE_FCNTL_H)
86# include <fcntl.h>
87#endif
88
89#if defined(HAVE_SYS_TIME_H)
90# include <sys/time.h>
91#endif
92
93#if !defined HAVE_LSTAT && !defined lstat
94# define lstat stat
95#endif
96
97/* define system APIs */
98#ifdef _WIN32
99# include "win32/file.h"
100# define STAT(p, s) rb_w32_ustati128((p), (s))
101# undef lstat
102# define lstat(p, s) rb_w32_ulstati128((p), (s))
103# undef access
104# define access(p, m) rb_w32_uaccess((p), (m))
105# undef truncate
106# define truncate(p, n) rb_w32_utruncate((p), (n))
107# undef chmod
108# define chmod(p, m) rb_w32_uchmod((p), (m))
109# undef chown
110# define chown(p, o, g) rb_w32_uchown((p), (o), (g))
111# undef lchown
112# define lchown(p, o, g) rb_w32_ulchown((p), (o), (g))
113# undef utimensat
114# define utimensat(s, p, t, f) rb_w32_uutimensat((s), (p), (t), (f))
115# undef link
116# define link(f, t) rb_w32_ulink((f), (t))
117# undef unlink
118# define unlink(p) rb_w32_uunlink(p)
119# undef readlink
120# define readlink(f, t, l) rb_w32_ureadlink((f), (t), (l))
121# undef rename
122# define rename(f, t) rb_w32_urename((f), (t))
123# undef symlink
124# define symlink(s, l) rb_w32_usymlink((s), (l))
125
126# ifdef HAVE_REALPATH
127/* Don't use native realpath(3) on Windows, as the check for
128 absolute paths does not work for drive letters. */
129# undef HAVE_REALPATH
130# endif
131#else
132# define STAT(p, s) stat((p), (s))
133#endif /* _WIN32 */
134
135#ifdef HAVE_STRUCT_STATX_STX_BTIME
136# define ST_(name) stx_ ## name
137typedef struct statx_timestamp stat_timestamp;
138#else
139# define ST_(name) st_ ## name
140typedef struct timespec stat_timestamp;
141#endif
142
143#if defined _WIN32 || defined __APPLE__
144# define USE_OSPATH 1
145# define TO_OSPATH(str) rb_str_encode_ospath(str)
146#else
147# define USE_OSPATH 0
148# define TO_OSPATH(str) (str)
149#endif
150
151/* utime may fail if time is out-of-range for the FS [ruby-dev:38277] */
152#if defined DOSISH || defined __CYGWIN__
153# define UTIME_EINVAL
154#endif
155
156/* Solaris 10 realpath(3) doesn't support File.realpath */
157#if defined HAVE_REALPATH && defined __sun && defined __SVR4
158#undef HAVE_REALPATH
159#endif
160
161#ifdef HAVE_REALPATH
162# include <limits.h>
163# include <stdlib.h>
164#endif
165
166#include "dln.h"
167#include "encindex.h"
168#include "id.h"
169#include "internal.h"
170#include "internal/compilers.h"
171#include "internal/dir.h"
172#include "internal/error.h"
173#include "internal/file.h"
174#include "internal/io.h"
175#include "internal/load.h"
176#include "internal/object.h"
177#include "internal/process.h"
178#include "internal/thread.h"
179#include "internal/vm.h"
180#include "ruby/encoding.h"
181#include "ruby/thread.h"
182#include "ruby/util.h"
183
184#define UIANY2NUM(x) \
185 ((sizeof(x) <= sizeof(unsigned int)) ? \
186 UINT2NUM((unsigned)(x)) : \
187 (sizeof(x) <= sizeof(unsigned long)) ? \
188 ULONG2NUM((unsigned long)(x)) : \
189 ULL2NUM((unsigned LONG_LONG)(x)))
190
194
195static VALUE
196file_path_convert(VALUE name)
197{
198#ifndef _WIN32 /* non Windows == Unix */
199 int fname_encidx = ENCODING_GET(name);
200 int fs_encidx;
201 if (ENCINDEX_US_ASCII != fname_encidx &&
202 ENCINDEX_ASCII_8BIT != fname_encidx &&
203 (fs_encidx = rb_filesystem_encindex()) != fname_encidx &&
204 rb_default_internal_encoding() &&
205 !rb_enc_str_asciionly_p(name)) {
206 /* Don't call rb_filesystem_encoding() before US-ASCII and ASCII-8BIT */
207 /* fs_encoding should be ascii compatible */
208 rb_encoding *fname_encoding = rb_enc_from_index(fname_encidx);
209 rb_encoding *fs_encoding = rb_enc_from_index(fs_encidx);
210 name = rb_str_conv_enc(name, fname_encoding, fs_encoding);
211 }
212#endif
213 return name;
214}
215
216static rb_encoding *
217check_path_encoding(VALUE str)
218{
219 rb_encoding *enc = rb_enc_get(str);
220 if (!rb_enc_asciicompat(enc)) {
221 rb_raise(rb_eEncCompatError, "path name must be ASCII-compatible (%s): %"PRIsVALUE,
222 rb_enc_name(enc), rb_str_inspect(str));
223 }
224 return enc;
225}
226
227VALUE
228rb_get_path_check_to_string(VALUE obj)
229{
230 VALUE tmp;
231 ID to_path;
232
233 if (RB_TYPE_P(obj, T_STRING)) {
234 return obj;
235 }
236 CONST_ID(to_path, "to_path");
237 tmp = rb_check_funcall_default(obj, to_path, 0, 0, obj);
238 StringValue(tmp);
239 return tmp;
240}
241
242VALUE
243rb_get_path_check_convert(VALUE obj)
244{
245 obj = file_path_convert(obj);
246
247 check_path_encoding(obj);
248 if (!rb_str_to_cstr(obj)) {
249 rb_raise(rb_eArgError, "path name contains null byte");
250 }
251
252 return rb_str_new4(obj);
253}
254
255VALUE
256rb_get_path_no_checksafe(VALUE obj)
257{
258 return rb_get_path(obj);
259}
260
261VALUE
262rb_get_path(VALUE obj)
263{
264 return rb_get_path_check_convert(rb_get_path_check_to_string(obj));
265}
266
267VALUE
268rb_str_encode_ospath(VALUE path)
269{
270#if USE_OSPATH
271 int encidx = ENCODING_GET(path);
272#if 0 && defined _WIN32
273 if (encidx == ENCINDEX_ASCII_8BIT) {
274 encidx = rb_filesystem_encindex();
275 }
276#endif
277 if (encidx != ENCINDEX_ASCII_8BIT && encidx != ENCINDEX_UTF_8) {
278 rb_encoding *enc = rb_enc_from_index(encidx);
279 rb_encoding *utf8 = rb_utf8_encoding();
280 path = rb_str_conv_enc(path, enc, utf8);
281 }
282#endif /* USE_OSPATH */
283 return path;
284}
285
286#ifdef __APPLE__
287# define NORMALIZE_UTF8PATH 1
288
289# ifdef HAVE_WORKING_FORK
290static CFMutableStringRef
291mutable_CFString_new(CFStringRef *s, const char *ptr, long len)
292{
293 const CFAllocatorRef alloc = kCFAllocatorDefault;
294 *s = CFStringCreateWithBytesNoCopy(alloc, (const UInt8 *)ptr, len,
295 kCFStringEncodingUTF8, FALSE,
296 kCFAllocatorNull);
297 return CFStringCreateMutableCopy(alloc, len, *s);
298}
299
300# define mutable_CFString_release(m, s) (CFRelease(m), CFRelease(s))
301
302static void
303rb_CFString_class_initialize_before_fork(void)
304{
305 /*
306 * Since macOS 13, CFString family API used in
307 * rb_str_append_normalized_ospath may internally use Objective-C classes
308 * (NSTaggedPointerString and NSPlaceholderMutableString) for small strings.
309 *
310 * On the other hand, Objective-C classes should not be used for the first
311 * time in a fork()'ed but not exec()'ed process. Violations for this rule
312 * can result deadlock during class initialization, so Objective-C runtime
313 * conservatively crashes on such cases by default.
314 *
315 * Therefore, we need to use CFString API to initialize Objective-C classes
316 * used internally *before* fork().
317 *
318 * For future changes, please note that this initialization process cannot
319 * be done in ctor because NSTaggedPointerString in CoreFoundation is enabled
320 * after CFStringInitializeTaggedStrings(), which is called during loading
321 * Objective-C runtime after ctor.
322 * For more details, see https://bugs.ruby-lang.org/issues/18912
323 */
324
325 /* Enough small but non-empty ASCII string to fit in NSTaggedPointerString. */
326 const char small_str[] = "/";
327 long len = sizeof(small_str) - 1;
328 CFStringRef s;
329 /*
330 * Touch `CFStringCreateWithBytesNoCopy` *twice* because the implementation
331 * shipped with macOS 15.0 24A5331b does not return `NSTaggedPointerString`
332 * instance for the first call (totally not sure why). CoreFoundation
333 * shipped with macOS 15.1 does not have this issue.
334 */
335 for (int i = 0; i < 2; i++) {
336 CFMutableStringRef m = mutable_CFString_new(&s, small_str, len);
337 mutable_CFString_release(m, s);
338 }
339}
340# endif /* HAVE_WORKING_FORK */
341
342static VALUE
343rb_str_append_normalized_ospath(VALUE str, const char *ptr, long len)
344{
345 CFIndex buflen = 0;
346 CFRange all;
347 CFStringRef s;
348 CFMutableStringRef m = mutable_CFString_new(&s, ptr, len);
349 long oldlen = RSTRING_LEN(str);
350
351 CFStringNormalize(m, kCFStringNormalizationFormC);
352 all = CFRangeMake(0, CFStringGetLength(m));
353 CFStringGetBytes(m, all, kCFStringEncodingUTF8, '?', FALSE, NULL, 0, &buflen);
354 rb_str_modify_expand(str, buflen);
355 CFStringGetBytes(m, all, kCFStringEncodingUTF8, '?', FALSE,
356 (UInt8 *)(RSTRING_PTR(str) + oldlen), buflen, &buflen);
357 rb_str_set_len(str, oldlen + buflen);
358 mutable_CFString_release(m, s);
359 return str;
360}
361
362VALUE
363rb_str_normalize_ospath(const char *ptr, long len)
364{
365 const char *p = ptr;
366 const char *e = ptr + len;
367 const char *p1 = p;
368 VALUE str = rb_str_buf_new(len);
369 rb_encoding *enc = rb_utf8_encoding();
370 rb_enc_associate(str, enc);
371
372 while (p < e) {
373 int l, c;
374 int r = rb_enc_precise_mbclen(p, e, enc);
375 if (!MBCLEN_CHARFOUND_P(r)) {
376 /* invalid byte shall not happen but */
377 RBIMPL_ATTR_NONSTRING() static const char invalid[3] = "\xEF\xBF\xBD";
378 rb_str_append_normalized_ospath(str, p1, p-p1);
379 rb_str_cat(str, invalid, sizeof(invalid));
380 p += 1;
381 p1 = p;
382 continue;
383 }
385 c = rb_enc_mbc_to_codepoint(p, e, enc);
386 if ((0x2000 <= c && c <= 0x2FFF) || (0xF900 <= c && c <= 0xFAFF) ||
387 (0x2F800 <= c && c <= 0x2FAFF)) {
388 if (p - p1 > 0) {
389 rb_str_append_normalized_ospath(str, p1, p-p1);
390 }
391 rb_str_cat(str, p, l);
392 p += l;
393 p1 = p;
394 }
395 else {
396 p += l;
397 }
398 }
399 if (p - p1 > 0) {
400 rb_str_append_normalized_ospath(str, p1, p-p1);
401 }
402
403 return str;
404}
405
406static int
407ignored_char_p(const char *p, const char *e, rb_encoding *enc)
408{
409 unsigned char c;
410 if (p+3 > e) return 0;
411 switch ((unsigned char)*p) {
412 case 0xe2:
413 switch ((unsigned char)p[1]) {
414 case 0x80:
415 c = (unsigned char)p[2];
416 /* c >= 0x200c && c <= 0x200f */
417 if (c >= 0x8c && c <= 0x8f) return 3;
418 /* c >= 0x202a && c <= 0x202e */
419 if (c >= 0xaa && c <= 0xae) return 3;
420 return 0;
421 case 0x81:
422 c = (unsigned char)p[2];
423 /* c >= 0x206a && c <= 0x206f */
424 if (c >= 0xaa && c <= 0xaf) return 3;
425 return 0;
426 }
427 break;
428 case 0xef:
429 /* c == 0xfeff */
430 if ((unsigned char)p[1] == 0xbb &&
431 (unsigned char)p[2] == 0xbf)
432 return 3;
433 break;
434 }
435 return 0;
436}
437#else /* !__APPLE__ */
438# define NORMALIZE_UTF8PATH 0
439#endif /* __APPLE__ */
440
441#define apply2args(n) (rb_check_arity(argc, n, UNLIMITED_ARGUMENTS), argc-=n)
442
444 const char *ptr;
445 VALUE path;
446};
447
448struct apply_arg {
449 int i;
450 int argc;
451 int errnum;
452 int (*func)(const char *, void *);
453 void *arg;
454 struct apply_filename fn[FLEX_ARY_LEN];
455};
456
457static void *
458no_gvl_apply2files(void *ptr)
459{
460 struct apply_arg *aa = ptr;
461
462 for (aa->i = 0; aa->i < aa->argc; aa->i++) {
463 if (aa->func(aa->fn[aa->i].ptr, aa->arg) < 0) {
464 aa->errnum = errno;
465 break;
466 }
467 }
468 return 0;
469}
470
471#ifdef UTIME_EINVAL
472NORETURN(static void utime_failed(struct apply_arg *));
473static int utime_internal(const char *, void *);
474#endif
475
476static VALUE
477apply2files(int (*func)(const char *, void *), int argc, VALUE *argv, void *arg)
478{
479 VALUE v;
480 const size_t size = sizeof(struct apply_filename);
481 const long len = (long)(offsetof(struct apply_arg, fn) + (size * argc));
482 struct apply_arg *aa = ALLOCV(v, len);
483
484 aa->errnum = 0;
485 aa->argc = argc;
486 aa->arg = arg;
487 aa->func = func;
488
489 for (aa->i = 0; aa->i < argc; aa->i++) {
490 VALUE path = rb_get_path(argv[aa->i]);
491
492 path = rb_str_encode_ospath(path);
493 aa->fn[aa->i].ptr = RSTRING_PTR(path);
494 aa->fn[aa->i].path = path;
495 }
496
497 IO_WITHOUT_GVL(no_gvl_apply2files, aa);
498 if (aa->errnum) {
499#ifdef UTIME_EINVAL
500 if (func == utime_internal) {
501 utime_failed(aa);
502 }
503#endif
504 rb_syserr_fail_path(aa->errnum, aa->fn[aa->i].path);
505 }
506 if (v) {
507 ALLOCV_END(v);
508 }
509 return LONG2FIX(argc);
510}
511
512static stat_timestamp stat_atimespec(const struct stat *st);
513static stat_timestamp stat_mtimespec(const struct stat *st);
514static stat_timestamp stat_ctimespec(const struct stat *st);
515
516static const rb_data_type_t stat_data_type = {
517 "stat",
518 {
519 NULL,
521 NULL, // No external memory to report
522 },
523 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
524};
525
526struct rb_stat {
527 rb_io_stat_data stat;
528 bool initialized;
529};
530
531static struct rb_stat *
532stat_alloc(VALUE klass, VALUE *obj)
533{
534 struct rb_stat *rb_st;
535 *obj = TypedData_Make_Struct(klass, struct rb_stat, &stat_data_type, rb_st);
536 return rb_st;
537}
538
539VALUE
540rb_stat_new(const struct stat *st)
541{
542 VALUE obj;
543 struct rb_stat *rb_st = stat_alloc(rb_cStat, &obj);
544 if (st) {
545#if RUBY_USE_STATX
546# define CP(m) .stx_ ## m = st->st_ ## m
547# define CP_32(m) .stx_ ## m = (uint32_t)st->st_ ## m
548# define CP_TS(m) .stx_ ## m = stat_ ## m ## spec(st)
549 rb_st->stat = (struct statx){
550 .stx_mask = STATX_BASIC_STATS,
551 CP(mode),
552 CP_32(nlink),
553 CP(uid),
554 CP(gid),
555 CP_TS(atime),
556 CP_TS(mtime),
557 CP_TS(ctime),
558 CP(ino),
559 CP(size),
560 CP(blocks),
561 };
562# undef CP
563# undef CP_TS
564#else
565 rb_st->stat = *st;
566#endif
567 rb_st->initialized = true;
568 }
569
570 return obj;
571}
572
573#ifndef rb_statx_new
574VALUE
575rb_statx_new(const rb_io_stat_data *st)
576{
577 VALUE obj;
578 struct rb_stat *rb_st = stat_alloc(rb_cStat, &obj);
579 if (st) {
580 rb_st->stat = *st;
581 rb_st->initialized = true;
582 }
583 return obj;
584}
585#endif
586
587static rb_io_stat_data*
588get_stat(VALUE self)
589{
590 struct rb_stat* rb_st;
591 TypedData_Get_Struct(self, struct rb_stat, &stat_data_type, rb_st);
592 if (!rb_st->initialized) rb_raise(rb_eTypeError, "uninitialized File::Stat");
593 return &rb_st->stat;
594}
595
596#if RUBY_USE_STATX
597static stat_timestamp
598statx_mtimespec(const rb_io_stat_data *st)
599{
600 return st->stx_mtime;
601}
602#else
603# define statx_mtimespec stat_mtimespec
604#endif
605
606/*
607 * call-seq:
608 * stat <=> other_stat -> -1, 0, 1, nil
609 *
610 * Compares File::Stat objects by comparing their respective modification
611 * times.
612 *
613 * +nil+ is returned if +other_stat+ is not a File::Stat object
614 *
615 * f1 = File.new("f1", "w")
616 * sleep 1
617 * f2 = File.new("f2", "w")
618 * f1.stat <=> f2.stat #=> -1
619 */
620
621static VALUE
622rb_stat_cmp(VALUE self, VALUE other)
623{
624 if (rb_obj_is_kind_of(other, rb_obj_class(self))) {
625 stat_timestamp ts1 = statx_mtimespec(get_stat(self));
626 stat_timestamp ts2 = statx_mtimespec(get_stat(other));
627 if (ts1.tv_sec == ts2.tv_sec) {
628 if (ts1.tv_nsec == ts2.tv_nsec) return INT2FIX(0);
629 if (ts1.tv_nsec < ts2.tv_nsec) return INT2FIX(-1);
630 return INT2FIX(1);
631 }
632 if (ts1.tv_sec < ts2.tv_sec) return INT2FIX(-1);
633 return INT2FIX(1);
634 }
635 return Qnil;
636}
637
638#define ST2UINT(val) ((val) & ~(~1UL << (sizeof(val) * CHAR_BIT - 1)))
639
640#ifndef NUM2DEVT
641# define NUM2DEVT(v) NUM2UINT(v)
642#endif
643#ifndef DEVT2NUM
644# define DEVT2NUM(v) UINT2NUM(v)
645#endif
646#ifndef PRI_DEVT_PREFIX
647# define PRI_DEVT_PREFIX ""
648#endif
649
650/*
651 * call-seq:
652 * stat.dev -> integer
653 *
654 * Returns an integer representing the device on which <i>stat</i>
655 * resides.
656 *
657 * File.stat("testfile").dev #=> 774
658 */
659
660static VALUE
661rb_stat_dev(VALUE self)
662{
663#if RUBY_USE_STATX
664 unsigned int m = get_stat(self)->stx_dev_major;
665 unsigned int n = get_stat(self)->stx_dev_minor;
666 return ULL2NUM(makedev(m, n));
667#elif SIZEOF_STRUCT_STAT_ST_DEV <= SIZEOF_DEV_T
668 return DEVT2NUM(get_stat(self)->st_dev);
669#elif SIZEOF_STRUCT_STAT_ST_DEV <= SIZEOF_LONG
670 return ULONG2NUM(get_stat(self)->st_dev);
671#else
672 return ULL2NUM(get_stat(self)->st_dev);
673#endif
674}
675
676/*
677 * call-seq:
678 * stat.dev_major -> integer
679 *
680 * Returns the major part of <code>File_Stat#dev</code> or
681 * <code>nil</code>.
682 *
683 * File.stat("/dev/fd1").dev_major #=> 2
684 * File.stat("/dev/tty").dev_major #=> 5
685 */
686
687static VALUE
688rb_stat_dev_major(VALUE self)
689{
690#if RUBY_USE_STATX
691 return UINT2NUM(get_stat(self)->stx_dev_major);
692#elif defined(major)
693 return UINT2NUM(major(get_stat(self)->st_dev));
694#else
695 return Qnil;
696#endif
697}
698
699/*
700 * call-seq:
701 * stat.dev_minor -> integer
702 *
703 * Returns the minor part of <code>File_Stat#dev</code> or
704 * <code>nil</code>.
705 *
706 * File.stat("/dev/fd1").dev_minor #=> 1
707 * File.stat("/dev/tty").dev_minor #=> 0
708 */
709
710static VALUE
711rb_stat_dev_minor(VALUE self)
712{
713#if RUBY_USE_STATX
714 return UINT2NUM(get_stat(self)->stx_dev_minor);
715#elif defined(minor)
716 return UINT2NUM(minor(get_stat(self)->st_dev));
717#else
718 return Qnil;
719#endif
720}
721
722/*
723 * call-seq:
724 * stat.ino -> integer
725 *
726 * Returns the inode number for <i>stat</i>.
727 *
728 * File.stat("testfile").ino #=> 1083669
729 *
730 */
731
732static VALUE
733rb_stat_ino(VALUE self)
734{
735 rb_io_stat_data *ptr = get_stat(self);
736#ifdef HAVE_STRUCT_STAT_ST_INOHIGH
737 /* assume INTEGER_PACK_LSWORD_FIRST and st_inohigh is just next of st_ino */
738 return rb_integer_unpack(&ptr->st_ino, 2,
739 SIZEOF_STRUCT_STAT_ST_INO, 0,
742#else
743 return UIANY2NUM(ptr->ST_(ino));
744#endif
745}
746
747/*
748 * call-seq:
749 * stat.mode -> integer
750 *
751 * Returns an integer representing the permission bits of
752 * <i>stat</i>. The meaning of the bits is platform dependent; on
753 * Unix systems, see <code>stat(2)</code>.
754 *
755 * File.chmod(0644, "testfile") #=> 1
756 * s = File.stat("testfile")
757 * sprintf("%o", s.mode) #=> "100644"
758 */
759
760static VALUE
761rb_stat_mode(VALUE self)
762{
763 return UINT2NUM(ST2UINT(get_stat(self)->ST_(mode)));
764}
765
766/*
767 * call-seq:
768 * stat.nlink -> integer
769 *
770 * Returns the number of hard links to <i>stat</i>.
771 *
772 * File.stat("testfile").nlink #=> 1
773 * File.link("testfile", "testfile.bak") #=> 0
774 * File.stat("testfile").nlink #=> 2
775 *
776 */
777
778static VALUE
779rb_stat_nlink(VALUE self)
780{
781 /* struct stat::st_nlink is nlink_t in POSIX. Not the case for Windows. */
782 const rb_io_stat_data *ptr = get_stat(self);
783
784 return UIANY2NUM(ptr->ST_(nlink));
785}
786
787/*
788 * call-seq:
789 * stat.uid -> integer
790 *
791 * Returns the numeric user id of the owner of <i>stat</i>.
792 *
793 * File.stat("testfile").uid #=> 501
794 *
795 */
796
797static VALUE
798rb_stat_uid(VALUE self)
799{
800 return UIDT2NUM(get_stat(self)->ST_(uid));
801}
802
803/*
804 * call-seq:
805 * stat.gid -> integer
806 *
807 * Returns the numeric group id of the owner of <i>stat</i>.
808 *
809 * File.stat("testfile").gid #=> 500
810 *
811 */
812
813static VALUE
814rb_stat_gid(VALUE self)
815{
816 return GIDT2NUM(get_stat(self)->ST_(gid));
817}
818
819/*
820 * call-seq:
821 * stat.rdev -> integer or nil
822 *
823 * Returns an integer representing the device type on which
824 * <i>stat</i> resides. Returns <code>nil</code> if the operating
825 * system doesn't support this feature.
826 *
827 * File.stat("/dev/fd1").rdev #=> 513
828 * File.stat("/dev/tty").rdev #=> 1280
829 */
830
831static VALUE
832rb_stat_rdev(VALUE self)
833{
834#if RUBY_USE_STATX
835 unsigned int m = get_stat(self)->stx_rdev_major;
836 unsigned int n = get_stat(self)->stx_rdev_minor;
837 return ULL2NUM(makedev(m, n));
838#elif !defined(HAVE_STRUCT_STAT_ST_RDEV)
839 return Qnil;
840#elif SIZEOF_STRUCT_STAT_ST_RDEV <= SIZEOF_DEV_T
841 return DEVT2NUM(get_stat(self)->ST_(rdev));
842#elif SIZEOF_STRUCT_STAT_ST_RDEV <= SIZEOF_LONG
843 return ULONG2NUM(get_stat(self)->ST_(rdev));
844#else
845 return ULL2NUM(get_stat(self)->ST_(rdev));
846#endif
847}
848
849/*
850 * call-seq:
851 * stat.rdev_major -> integer
852 *
853 * Returns the major part of <code>File_Stat#rdev</code> or
854 * <code>nil</code>.
855 *
856 * File.stat("/dev/fd1").rdev_major #=> 2
857 * File.stat("/dev/tty").rdev_major #=> 5
858 */
859
860static VALUE
861rb_stat_rdev_major(VALUE self)
862{
863#if RUBY_USE_STATX
864 return UINT2NUM(get_stat(self)->stx_rdev_major);
865#elif defined(HAVE_STRUCT_STAT_ST_RDEV) && defined(major)
866 return UINT2NUM(major(get_stat(self)->ST_(rdev)));
867#else
868 return Qnil;
869#endif
870}
871
872/*
873 * call-seq:
874 * stat.rdev_minor -> integer
875 *
876 * Returns the minor part of <code>File_Stat#rdev</code> or
877 * <code>nil</code>.
878 *
879 * File.stat("/dev/fd1").rdev_minor #=> 1
880 * File.stat("/dev/tty").rdev_minor #=> 0
881 */
882
883static VALUE
884rb_stat_rdev_minor(VALUE self)
885{
886#if RUBY_USE_STATX
887 return UINT2NUM(get_stat(self)->stx_rdev_minor);
888#elif defined(HAVE_STRUCT_STAT_ST_RDEV) && defined(minor)
889 return UINT2NUM(minor(get_stat(self)->ST_(rdev)));
890#else
891 return Qnil;
892#endif
893}
894
895/*
896 * call-seq:
897 * stat.size -> integer
898 *
899 * Returns the size of <i>stat</i> in bytes.
900 *
901 * File.stat("testfile").size #=> 66
902 */
903
904static VALUE
905rb_stat_size(VALUE self)
906{
907 return OFFT2NUM(get_stat(self)->ST_(size));
908}
909
910/*
911 * call-seq:
912 * stat.blksize -> integer or nil
913 *
914 * Returns the native file system's block size. Will return <code>nil</code>
915 * on platforms that don't support this information.
916 *
917 * File.stat("testfile").blksize #=> 4096
918 *
919 */
920
921static VALUE
922rb_stat_blksize(VALUE self)
923{
924#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
925 return ULONG2NUM(get_stat(self)->ST_(blksize));
926#else
927 return Qnil;
928#endif
929}
930
931/*
932 * call-seq:
933 * stat.blocks -> integer or nil
934 *
935 * Returns the number of native file system blocks allocated for this
936 * file, or <code>nil</code> if the operating system doesn't
937 * support this feature.
938 *
939 * File.stat("testfile").blocks #=> 2
940 */
941
942static VALUE
943rb_stat_blocks(VALUE self)
944{
945#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
946# if SIZEOF_STRUCT_STAT_ST_BLOCKS > SIZEOF_LONG
947 return ULL2NUM(get_stat(self)->ST_(blocks));
948# else
949 return ULONG2NUM(get_stat(self)->ST_(blocks));
950# endif
951#else
952 return Qnil;
953#endif
954}
955
956static stat_timestamp
957stat_atimespec(const struct stat *st)
958{
960 ts.tv_sec = st->st_atime;
961#if defined(HAVE_STRUCT_STAT_ST_ATIM)
962 ts.tv_nsec = (uint32_t)st->st_atim.tv_nsec;
963#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
964 ts.tv_nsec = (uint32_t)st->st_atimespec.tv_nsec;
965#elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC)
966 ts.tv_nsec = (uint32_t)st->st_atimensec;
967#else
968 ts.tv_nsec = 0
969#endif
970 return ts;
971}
972
973#if RUBY_USE_STATX
974static stat_timestamp
975statx_atimespec(const rb_io_stat_data *st)
976{
977 return st->stx_atime;
978}
979#else
980# define statx_atimespec stat_atimespec
981#endif
982
983static VALUE
984stat_time(const stat_timestamp ts)
985{
986 return rb_time_nano_new(ts.tv_sec, ts.tv_nsec);
987}
988
989static VALUE
990stat_atime(const struct stat *st)
991{
992 return stat_time(stat_atimespec(st));
993}
994
995static stat_timestamp
996stat_mtimespec(const struct stat *st)
997{
999 ts.tv_sec = st->st_mtime;
1000#if defined(HAVE_STRUCT_STAT_ST_MTIM)
1001 ts.tv_nsec = (uint32_t)st->st_mtim.tv_nsec;
1002#elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1003 ts.tv_nsec = (uint32_t)st->st_mtimespec.tv_nsec;
1004#elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
1005 ts.tv_nsec = (uint32_t)st->st_mtimensec;
1006#else
1007 ts.tv_nsec = 0;
1008#endif
1009 return ts;
1010}
1011
1012static VALUE
1013stat_mtime(const struct stat *st)
1014{
1015 return stat_time(stat_mtimespec(st));
1016}
1017
1018static stat_timestamp
1019stat_ctimespec(const struct stat *st)
1020{
1021 stat_timestamp ts;
1022 ts.tv_sec = st->st_ctime;
1023#if defined(HAVE_STRUCT_STAT_ST_CTIM)
1024 ts.tv_nsec = (uint32_t)st->st_ctim.tv_nsec;
1025#elif defined(HAVE_STRUCT_STAT_ST_CTIMESPEC)
1026 ts.tv_nsec = (uint32_t)st->st_ctimespec.tv_nsec;
1027#elif defined(HAVE_STRUCT_STAT_ST_CTIMENSEC)
1028 ts.tv_nsec = (uint32_t)st->st_ctimensec;
1029#else
1030 ts.tv_nsec = 0;
1031#endif
1032 return ts;
1033}
1034
1035#if RUBY_USE_STATX
1036static stat_timestamp
1037statx_ctimespec(const rb_io_stat_data *st)
1038{
1039 return st->stx_ctime;
1040}
1041#else
1042# define statx_ctimespec stat_ctimespec
1043#endif
1044
1045static VALUE
1046stat_ctime(const struct stat *st)
1047{
1048 return stat_time(stat_ctimespec(st));
1049}
1050
1051#define HAVE_STAT_BIRTHTIME
1052#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC)
1053static VALUE
1054statx_birthtime(const rb_io_stat_data *st)
1055{
1056 const stat_timestamp *ts = &st->ST_(birthtimespec);
1057 return rb_time_nano_new(ts->tv_sec, ts->tv_nsec);
1058}
1059#elif defined(HAVE_STRUCT_STATX_STX_BTIME)
1060static VALUE statx_birthtime(const rb_io_stat_data *st);
1061#elif defined(_WIN32)
1062# define statx_birthtime stat_ctime
1063#else
1064# undef HAVE_STAT_BIRTHTIME
1065#endif /* defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC) */
1066
1067/*
1068 * call-seq:
1069 * stat.atime -> time
1070 *
1071 * Returns the last access time for this file as an object of class
1072 * Time.
1073 *
1074 * File.stat("testfile").atime #=> Wed Dec 31 18:00:00 CST 1969
1075 *
1076 */
1077
1078static VALUE
1079rb_stat_atime(VALUE self)
1080{
1081 return stat_time(statx_atimespec(get_stat(self)));
1082}
1083
1084/*
1085 * call-seq:
1086 * stat.mtime -> time
1087 *
1088 * Returns the modification time of <i>stat</i>.
1089 *
1090 * File.stat("testfile").mtime #=> Wed Apr 09 08:53:14 CDT 2003
1091 *
1092 */
1093
1094static VALUE
1095rb_stat_mtime(VALUE self)
1096{
1097 return stat_time(statx_mtimespec(get_stat(self)));
1098}
1099
1100/*
1101 * call-seq:
1102 * stat.ctime -> time
1103 *
1104 * Returns the change time for <i>stat</i> (that is, the time
1105 * directory information about the file was changed, not the file
1106 * itself).
1107 *
1108 * Note that on Windows (NTFS), returns creation time (birth time).
1109 *
1110 * File.stat("testfile").ctime #=> Wed Apr 09 08:53:14 CDT 2003
1111 *
1112 */
1113
1114static VALUE
1115rb_stat_ctime(VALUE self)
1116{
1117 return stat_time(statx_ctimespec(get_stat(self)));
1118}
1119
1120#if defined(HAVE_STAT_BIRTHTIME)
1121/*
1122 * call-seq:
1123 * stat.birthtime -> time
1124 *
1125 * Returns the birth time for <i>stat</i>.
1126 *
1127 * If the platform doesn't have birthtime, raises NotImplementedError.
1128 *
1129 * File.write("testfile", "foo")
1130 * sleep 10
1131 * File.write("testfile", "bar")
1132 * sleep 10
1133 * File.chmod(0644, "testfile")
1134 * sleep 10
1135 * File.read("testfile")
1136 * File.stat("testfile").birthtime #=> 2014-02-24 11:19:17 +0900
1137 * File.stat("testfile").mtime #=> 2014-02-24 11:19:27 +0900
1138 * File.stat("testfile").ctime #=> 2014-02-24 11:19:37 +0900
1139 * File.stat("testfile").atime #=> 2014-02-24 11:19:47 +0900
1140 *
1141 */
1142
1143static VALUE
1144rb_stat_birthtime(VALUE self)
1145{
1146 return statx_birthtime(get_stat(self));
1147}
1148#else
1149# define rb_stat_birthtime rb_f_notimplement
1150#endif
1151
1152/*
1153 * call-seq:
1154 * stat.inspect -> string
1155 *
1156 * Produce a nicely formatted description of <i>stat</i>.
1157 *
1158 * File.stat("/etc/passwd").inspect
1159 * #=> "#<File::Stat dev=0xe000005, ino=1078078, mode=0100644,
1160 * # nlink=1, uid=0, gid=0, rdev=0x0, size=1374, blksize=4096,
1161 * # blocks=8, atime=Wed Dec 10 10:16:12 CST 2003,
1162 * # mtime=Fri Sep 12 15:41:41 CDT 2003,
1163 * # ctime=Mon Oct 27 11:20:27 CST 2003,
1164 * # birthtime=Mon Aug 04 08:13:49 CDT 2003>"
1165 */
1166
1167static VALUE
1168rb_stat_inspect(VALUE self)
1169{
1170 VALUE str;
1171 size_t i;
1172 static const struct {
1173 const char *name;
1174 VALUE (*func)(VALUE);
1175 } member[] = {
1176 {"dev", rb_stat_dev},
1177 {"ino", rb_stat_ino},
1178 {"mode", rb_stat_mode},
1179 {"nlink", rb_stat_nlink},
1180 {"uid", rb_stat_uid},
1181 {"gid", rb_stat_gid},
1182 {"rdev", rb_stat_rdev},
1183 {"size", rb_stat_size},
1184 {"blksize", rb_stat_blksize},
1185 {"blocks", rb_stat_blocks},
1186 {"atime", rb_stat_atime},
1187 {"mtime", rb_stat_mtime},
1188 {"ctime", rb_stat_ctime},
1189#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC)
1190 {"birthtime", rb_stat_birthtime},
1191#endif
1192 };
1193
1194 struct rb_stat* rb_st;
1195 TypedData_Get_Struct(self, struct rb_stat, &stat_data_type, rb_st);
1196 if (!rb_st->initialized) {
1197 return rb_sprintf("#<%s: uninitialized>", rb_obj_classname(self));
1198 }
1199
1200 str = rb_str_buf_new2("#<");
1202 rb_str_buf_cat2(str, " ");
1203
1204 for (i = 0; i < sizeof(member)/sizeof(member[0]); i++) {
1205 VALUE v;
1206
1207 if (i > 0) {
1208 rb_str_buf_cat2(str, ", ");
1209 }
1210 rb_str_buf_cat2(str, member[i].name);
1211 rb_str_buf_cat2(str, "=");
1212 v = (*member[i].func)(self);
1213 if (i == 2) { /* mode */
1214 rb_str_catf(str, "0%lo", (unsigned long)NUM2ULONG(v));
1215 }
1216 else if (i == 0 || i == 6) { /* dev/rdev */
1217 rb_str_catf(str, "0x%"PRI_DEVT_PREFIX"x", NUM2DEVT(v));
1218 }
1219 else {
1220 rb_str_append(str, rb_inspect(v));
1221 }
1222 }
1223 rb_str_buf_cat2(str, ">");
1224
1225 return str;
1226}
1227
1228typedef struct no_gvl_stat_data {
1229 struct stat *st;
1230 union {
1231 const char *path;
1232 int fd;
1233 } file;
1235
1236static VALUE
1237no_gvl_fstat(void *data)
1238{
1239 no_gvl_stat_data *arg = data;
1240 return (VALUE)fstat(arg->file.fd, arg->st);
1241}
1242
1243static int
1244fstat_without_gvl(rb_io_t *fptr, struct stat *st)
1245{
1246 no_gvl_stat_data data;
1247
1248 data.file.fd = fptr->fd;
1249 data.st = st;
1250
1251 return (int)rb_io_blocking_region(fptr, no_gvl_fstat, &data);
1252}
1253
1254static void *
1255no_gvl_stat(void * data)
1256{
1257 no_gvl_stat_data *arg = data;
1258 return (void *)(VALUE)STAT(arg->file.path, arg->st);
1259}
1260
1261static int
1262stat_without_gvl(const char *path, struct stat *st)
1263{
1264 no_gvl_stat_data data;
1265
1266 data.file.path = path;
1267 data.st = st;
1268
1269 return IO_WITHOUT_GVL_INT(no_gvl_stat, &data);
1270}
1271
1272#if !defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC) && \
1273 defined(HAVE_STRUCT_STATX_STX_BTIME)
1274
1275# define STATX(path, st, mask) statx(AT_FDCWD, path, 0, mask, st)
1276
1277# ifndef HAVE_STATX
1278# ifdef HAVE_SYSCALL_H
1279# include <syscall.h>
1280# elif defined HAVE_SYS_SYSCALL_H
1281# include <sys/syscall.h>
1282# endif
1283# if defined __linux__
1284# include <linux/stat.h>
1285static inline int
1286statx(int dirfd, const char *pathname, int flags,
1287 unsigned int mask, struct statx *statxbuf)
1288{
1289 return (int)syscall(__NR_statx, dirfd, pathname, flags, mask, statxbuf);
1290}
1291# endif /* __linux__ */
1292# endif /* HAVE_STATX */
1293
1294typedef struct no_gvl_rb_io_stat_data {
1295 struct statx *stx;
1296 int fd;
1297 const char *path;
1298 int flags;
1299 unsigned int mask;
1300} no_gvl_rb_io_stat_data;
1301
1302static VALUE
1303io_blocking_statx(void *data)
1304{
1305 no_gvl_rb_io_stat_data *arg = data;
1306 return (VALUE)statx(arg->fd, arg->path, arg->flags, arg->mask, arg->stx);
1307}
1308
1309static void *
1310no_gvl_statx(void *data)
1311{
1312 return (void *)io_blocking_statx(data);
1313}
1314
1315static int
1316statx_without_gvl(const char *path, rb_io_stat_data *stx, unsigned int mask)
1317{
1318 no_gvl_rb_io_stat_data data = {stx, AT_FDCWD, path, 0, mask};
1319
1320 /* call statx(2) with pathname */
1321 return IO_WITHOUT_GVL_INT(no_gvl_statx, &data);
1322}
1323
1324static int
1325lstatx_without_gvl(const char *path, rb_io_stat_data *stx, unsigned int mask)
1326{
1327 no_gvl_rb_io_stat_data data = {stx, AT_FDCWD, path, AT_SYMLINK_NOFOLLOW, mask};
1328
1329 /* call statx(2) with pathname */
1330 return IO_WITHOUT_GVL_INT(no_gvl_statx, &data);
1331}
1332
1333static int
1334fstatx_without_gvl(rb_io_t *fptr, rb_io_stat_data *stx, unsigned int mask)
1335{
1336 no_gvl_rb_io_stat_data data = {stx, fptr->fd, "", AT_EMPTY_PATH, mask};
1337
1338 /* call statx(2) with fd */
1339 return (int)rb_io_blocking_region(fptr, io_blocking_statx, &data);
1340}
1341
1342#define FSTATX(fd, st) statx(fd, "", AT_EMPTY_PATH, STATX_ALL, st)
1343
1344static int
1345rb_statx(VALUE file, struct statx *stx, unsigned int mask)
1346{
1347 VALUE tmp;
1348 int result;
1349
1350 tmp = rb_check_convert_type_with_id(file, T_FILE, "IO", idTo_io);
1351 if (!NIL_P(tmp)) {
1352 rb_io_t *fptr;
1353
1354 GetOpenFile(tmp, fptr);
1355 result = fstatx_without_gvl(fptr, stx, mask);
1356 file = tmp;
1357 }
1358 else {
1359 FilePathValue(file);
1360 file = rb_str_encode_ospath(file);
1361 result = statx_without_gvl(RSTRING_PTR(file), stx, mask);
1362 }
1363 RB_GC_GUARD(file);
1364 return result;
1365}
1366
1367# define statx_has_birthtime(st) ((st)->stx_mask & STATX_BTIME)
1368
1369NORETURN(static void statx_notimplement(const char *field_name));
1370
1371/* rb_notimplement() shows "function is unimplemented on this machine".
1372 It is not applicable to statx which behavior depends on the filesystem. */
1373static void
1374statx_notimplement(const char *field_name)
1375{
1376 rb_raise(rb_eNotImpError,
1377 "%s is unimplemented on this filesystem",
1378 field_name);
1379}
1380
1381static VALUE
1382statx_birthtime(const rb_io_stat_data *stx)
1383{
1384 if (!statx_has_birthtime(stx)) {
1385 /* birthtime is not supported on the filesystem */
1386 statx_notimplement("birthtime");
1387 }
1388 return rb_time_nano_new((time_t)stx->stx_btime.tv_sec, stx->stx_btime.tv_nsec);
1389}
1390
1391#else
1392
1393# define statx_without_gvl(path, st, mask) stat_without_gvl(path, st)
1394# define fstatx_without_gvl(fptr, st, mask) fstat_without_gvl(fptr, st)
1395# define lstatx_without_gvl(path, st, mask) lstat_without_gvl(path, st)
1396# define rb_statx(file, stx, mask) rb_stat(file, stx)
1397# define STATX(path, st, mask) STAT(path, st)
1398
1399#if defined(HAVE_STAT_BIRTHTIME)
1400# define statx_has_birthtime(st) 1
1401#else
1402# define statx_has_birthtime(st) 0
1403#endif
1404
1405#endif /* !defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC) && \
1406 defined(HAVE_STRUCT_STATX_STX_BTIME) */
1407
1408#ifndef FSTAT
1409# define FSTAT(fd, st) fstat(fd, st)
1410#endif
1411
1412static int
1413rb_stat(VALUE file, struct stat *st)
1414{
1415 VALUE tmp;
1416 int result;
1417
1418 tmp = rb_check_convert_type_with_id(file, T_FILE, "IO", idTo_io);
1419 if (!NIL_P(tmp)) {
1420 rb_io_t *fptr;
1421
1422 GetOpenFile(tmp, fptr);
1423 result = fstat_without_gvl(fptr, st);
1424 file = tmp;
1425 }
1426 else {
1427 FilePathValue(file);
1428 file = rb_str_encode_ospath(file);
1429 result = stat_without_gvl(RSTRING_PTR(file), st);
1430 }
1431 RB_GC_GUARD(file);
1432 return result;
1433}
1434
1435/*
1436 * call-seq:
1437 * File.stat(filepath) -> stat
1438 *
1439 * Returns a File::Stat object for the file at +filepath+ (see File::Stat):
1440 *
1441 * File.stat('t.txt').class # => File::Stat
1442 *
1443 */
1444
1445static VALUE
1446rb_file_s_stat(VALUE klass, VALUE fname)
1447{
1448 rb_io_stat_data st;
1449
1450 FilePathValue(fname);
1451 fname = rb_str_encode_ospath(fname);
1452 if (statx_without_gvl(RSTRING_PTR(fname), &st, STATX_ALL) < 0) {
1453 rb_sys_fail_path(fname);
1454 }
1455 return rb_statx_new(&st);
1456}
1457
1458/*
1459 * call-seq:
1460 * ios.stat -> stat
1461 *
1462 * Returns status information for <em>ios</em> as an object of type
1463 * File::Stat.
1464 *
1465 * f = File.new("testfile")
1466 * s = f.stat
1467 * "%o" % s.mode #=> "100644"
1468 * s.blksize #=> 4096
1469 * s.atime #=> Wed Apr 09 08:53:54 CDT 2003
1470 *
1471 */
1472
1473static VALUE
1474rb_io_stat(VALUE obj)
1475{
1476 rb_io_t *fptr;
1477 rb_io_stat_data st;
1478
1479 GetOpenFile(obj, fptr);
1480 if (fstatx_without_gvl(fptr, &st, STATX_ALL) == -1) {
1481 rb_sys_fail_path(fptr->pathv);
1482 }
1483 return rb_statx_new(&st);
1484}
1485
1486#ifdef HAVE_LSTAT
1487static void *
1488no_gvl_lstat(void *ptr)
1489{
1490 no_gvl_stat_data *arg = ptr;
1491 return (void *)(VALUE)lstat(arg->file.path, arg->st);
1492}
1493
1494static int
1495lstat_without_gvl(const char *path, struct stat *st)
1496{
1497 no_gvl_stat_data data;
1498
1499 data.file.path = path;
1500 data.st = st;
1501
1502 return IO_WITHOUT_GVL_INT(no_gvl_lstat, &data);
1503}
1504#endif /* HAVE_LSTAT */
1505
1506/*
1507 * call-seq:
1508 * File.lstat(filepath) -> stat
1509 *
1510 * Like File::stat, but does not follow the last symbolic link;
1511 * instead, returns a File::Stat object for the link itself.
1512 *
1513 * File.symlink('t.txt', 'symlink')
1514 * File.stat('symlink').size # => 47
1515 * File.lstat('symlink').size # => 5
1516 *
1517 */
1518
1519static VALUE
1520rb_file_s_lstat(VALUE klass, VALUE fname)
1521{
1522#ifdef HAVE_LSTAT
1523 rb_io_stat_data st;
1524
1525 FilePathValue(fname);
1526 fname = rb_str_encode_ospath(fname);
1527 if (lstatx_without_gvl(StringValueCStr(fname), &st, STATX_ALL) == -1) {
1528 rb_sys_fail_path(fname);
1529 }
1530 return rb_statx_new(&st);
1531#else
1532 return rb_file_s_stat(klass, fname);
1533#endif
1534}
1535
1536/*
1537 * call-seq:
1538 * lstat -> stat
1539 *
1540 * Like File#stat, but does not follow the last symbolic link;
1541 * instead, returns a File::Stat object for the link itself:
1542 *
1543 * File.symlink('t.txt', 'symlink')
1544 * f = File.new('symlink')
1545 * f.stat.size # => 47
1546 * f.lstat.size # => 11
1547 *
1548 */
1549
1550static VALUE
1551rb_file_lstat(VALUE obj)
1552{
1553#ifdef HAVE_LSTAT
1554 rb_io_t *fptr;
1555 rb_io_stat_data st;
1556 VALUE path;
1557
1558 GetOpenFile(obj, fptr);
1559 if (NIL_P(fptr->pathv)) return Qnil;
1560 path = rb_str_encode_ospath(fptr->pathv);
1561 if (lstatx_without_gvl(RSTRING_PTR(path), &st, STATX_ALL) == -1) {
1562 rb_sys_fail_path(fptr->pathv);
1563 }
1564 return rb_statx_new(&st);
1565#else
1566 return rb_io_stat(obj);
1567#endif
1568}
1569
1570static int
1571rb_group_member(GETGROUPS_T gid)
1572{
1573#if defined(_WIN32) || !defined(HAVE_GETGROUPS)
1574 return FALSE;
1575#else
1576 int rv = FALSE;
1577 int groups;
1578 VALUE v = 0;
1579 GETGROUPS_T *gary;
1580 int anum = -1;
1581
1582 if (getgid() == gid || getegid() == gid)
1583 return TRUE;
1584
1585 groups = getgroups(0, NULL);
1586 gary = ALLOCV_N(GETGROUPS_T, v, groups);
1587 anum = getgroups(groups, gary);
1588 while (--anum >= 0) {
1589 if (gary[anum] == gid) {
1590 rv = TRUE;
1591 break;
1592 }
1593 }
1594 if (v)
1595 ALLOCV_END(v);
1596
1597 return rv;
1598#endif /* defined(_WIN32) || !defined(HAVE_GETGROUPS) */
1599}
1600
1601#ifndef S_IXUGO
1602# define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
1603#endif
1604
1605#if defined(S_IXGRP) && !defined(_WIN32) && !defined(__CYGWIN__)
1606#define USE_GETEUID 1
1607#endif
1608
1609#ifndef HAVE_EACCESS
1610int
1611eaccess(const char *path, int mode)
1612{
1613#ifdef USE_GETEUID
1614 struct stat st;
1615 rb_uid_t euid;
1616
1617 euid = geteuid();
1618
1619 /* no setuid nor setgid. run shortcut. */
1620 if (getuid() == euid && getgid() == getegid())
1621 return access(path, mode);
1622
1623 if (STAT(path, &st) < 0)
1624 return -1;
1625
1626 if (euid == 0) {
1627 /* Root can read or write any file. */
1628 if (!(mode & X_OK))
1629 return 0;
1630
1631 /* Root can execute any file that has any one of the execute
1632 bits set. */
1633 if (st.st_mode & S_IXUGO)
1634 return 0;
1635
1636 return -1;
1637 }
1638
1639 if (st.st_uid == euid) /* owner */
1640 mode <<= 6;
1641 else if (rb_group_member(st.st_gid))
1642 mode <<= 3;
1643
1644 if ((int)(st.st_mode & mode) == mode) return 0;
1645
1646 return -1;
1647#else
1648 return access(path, mode);
1649#endif /* USE_GETEUID */
1650}
1651#endif /* HAVE_EACCESS */
1652
1654 const char *path;
1655 int mode;
1656};
1657
1658static void *
1659nogvl_eaccess(void *ptr)
1660{
1661 struct access_arg *aa = ptr;
1662
1663 return (void *)(VALUE)eaccess(aa->path, aa->mode);
1664}
1665
1666static int
1667rb_eaccess(VALUE fname, int mode)
1668{
1669 struct access_arg aa;
1670
1671 FilePathValue(fname);
1672 fname = rb_str_encode_ospath(fname);
1673 aa.path = StringValueCStr(fname);
1674 aa.mode = mode;
1675
1676 return IO_WITHOUT_GVL_INT(nogvl_eaccess, &aa);
1677}
1678
1679static void *
1680nogvl_access(void *ptr)
1681{
1682 struct access_arg *aa = ptr;
1683
1684 return (void *)(VALUE)access(aa->path, aa->mode);
1685}
1686
1687static int
1688rb_access(VALUE fname, int mode)
1689{
1690 struct access_arg aa;
1691
1692 FilePathValue(fname);
1693 fname = rb_str_encode_ospath(fname);
1694 aa.path = StringValueCStr(fname);
1695 aa.mode = mode;
1696
1697 return IO_WITHOUT_GVL_INT(nogvl_access, &aa);
1698}
1699
1700/*
1701 * Document-class: FileTest
1702 *
1703 * FileTest implements file test operations similar to those used in
1704 * File::Stat. It exists as a standalone module, and its methods are
1705 * also insinuated into the File class. (Note that this is not done
1706 * by inclusion: the interpreter cheats).
1707 *
1708 */
1709
1710/*
1711 * call-seq:
1712 * File.directory?(path) -> true or false
1713 *
1714 * With string +object+ given, returns +true+ if +path+ is a string path
1715 * leading to a directory, or to a symbolic link to a directory; +false+ otherwise:
1716 *
1717 * File.directory?('.') # => true
1718 * File.directory?('foo') # => false
1719 * File.symlink('.', 'dirlink') # => 0
1720 * File.directory?('dirlink') # => true
1721 * File.symlink('t,txt', 'filelink') # => 0
1722 * File.directory?('filelink') # => false
1723 *
1724 * Argument +path+ can be an IO object.
1725 *
1726 */
1727
1728VALUE
1729rb_file_directory_p(VALUE obj, VALUE fname)
1730{
1731#ifndef S_ISDIR
1732# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
1733#endif
1734
1735 struct stat st;
1736
1737 if (rb_stat(fname, &st) < 0) return Qfalse;
1738 if (S_ISDIR(st.st_mode)) return Qtrue;
1739 return Qfalse;
1740}
1741
1742/*
1743 * call-seq:
1744 * File.pipe?(filepath) -> true or false
1745 *
1746 * Returns +true+ if +filepath+ points to a pipe, +false+ otherwise:
1747 *
1748 * File.mkfifo('tmp/fifo')
1749 * File.pipe?('tmp/fifo') # => true
1750 * File.pipe?('t.txt') # => false
1751 *
1752 */
1753
1754static VALUE
1755rb_file_pipe_p(VALUE obj, VALUE fname)
1756{
1757#ifdef S_IFIFO
1758# ifndef S_ISFIFO
1759# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
1760# endif
1761
1762 struct stat st;
1763
1764 if (rb_stat(fname, &st) < 0) return Qfalse;
1765 if (S_ISFIFO(st.st_mode)) return Qtrue;
1766
1767#endif
1768 return Qfalse;
1769}
1770
1771/*
1772 * call-seq:
1773 * File.symlink?(filepath) -> true or false
1774 *
1775 * Returns +true+ if +filepath+ points to a symbolic link, +false+ otherwise:
1776 *
1777 * symlink = File.symlink('t.txt', 'symlink')
1778 * File.symlink?('symlink') # => true
1779 * File.symlink?('t.txt') # => false
1780 *
1781 */
1782
1783static VALUE
1784rb_file_symlink_p(VALUE obj, VALUE fname)
1785{
1786#ifndef S_ISLNK
1787# ifdef _S_ISLNK
1788# define S_ISLNK(m) _S_ISLNK(m)
1789# else
1790# ifdef _S_IFLNK
1791# define S_ISLNK(m) (((m) & S_IFMT) == _S_IFLNK)
1792# else
1793# ifdef S_IFLNK
1794# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
1795# endif
1796# endif
1797# endif
1798#endif
1799
1800#ifdef S_ISLNK
1801 struct stat st;
1802
1803 FilePathValue(fname);
1804 fname = rb_str_encode_ospath(fname);
1805 if (lstat_without_gvl(StringValueCStr(fname), &st) < 0) return Qfalse;
1806 if (S_ISLNK(st.st_mode)) return Qtrue;
1807#endif
1808
1809 return Qfalse;
1810}
1811
1812/*
1813 * call-seq:
1814 * File.socket?(filepath) -> true or false
1815 *
1816 * Returns +true+ if +filepath+ points to a socket, +false+ otherwise:
1817 *
1818 * require 'socket'
1819 * File.socket?(Socket.new(:INET, :STREAM)) # => true
1820 * File.socket?(File.new('t.txt')) # => false
1821 *
1822 */
1823
1824static VALUE
1825rb_file_socket_p(VALUE obj, VALUE fname)
1826{
1827#ifndef S_ISSOCK
1828# ifdef _S_ISSOCK
1829# define S_ISSOCK(m) _S_ISSOCK(m)
1830# else
1831# ifdef _S_IFSOCK
1832# define S_ISSOCK(m) (((m) & S_IFMT) == _S_IFSOCK)
1833# else
1834# ifdef S_IFSOCK
1835# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
1836# endif
1837# endif
1838# endif
1839#endif
1840
1841#ifdef S_ISSOCK
1842 struct stat st;
1843
1844 if (rb_stat(fname, &st) < 0) return Qfalse;
1845 if (S_ISSOCK(st.st_mode)) return Qtrue;
1846#endif
1847
1848 return Qfalse;
1849}
1850
1851/*
1852 * call-seq:
1853 * File.blockdev?(filepath) -> true or false
1854 *
1855 * Returns +true+ if +filepath+ points to a block device, +false+ otherwise:
1856 *
1857 * File.blockdev?('/dev/sda1') # => true
1858 * File.blockdev?(File.new('t.tmp')) # => false
1859 *
1860 */
1861
1862static VALUE
1863rb_file_blockdev_p(VALUE obj, VALUE fname)
1864{
1865#ifndef S_ISBLK
1866# ifdef S_IFBLK
1867# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
1868# else
1869# define S_ISBLK(m) (0) /* anytime false */
1870# endif
1871#endif
1872
1873#ifdef S_ISBLK
1874 struct stat st;
1875
1876 if (rb_stat(fname, &st) < 0) return Qfalse;
1877 if (S_ISBLK(st.st_mode)) return Qtrue;
1878
1879#endif
1880 return Qfalse;
1881}
1882
1883/*
1884 * call-seq:
1885 * File.chardev?(filepath) -> true or false
1886 *
1887 * Returns +true+ if +filepath+ points to a character device, +false+ otherwise.
1888 *
1889 * File.chardev?($stdin) # => true
1890 * File.chardev?('t.txt') # => false
1891 *
1892 */
1893static VALUE
1894rb_file_chardev_p(VALUE obj, VALUE fname)
1895{
1896#ifndef S_ISCHR
1897# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
1898#endif
1899
1900 struct stat st;
1901
1902 if (rb_stat(fname, &st) < 0) return Qfalse;
1903 if (S_ISCHR(st.st_mode)) return Qtrue;
1904
1905 return Qfalse;
1906}
1907
1908/*
1909 * call-seq:
1910 * File.exist?(file_name) -> true or false
1911 *
1912 * Return <code>true</code> if the named file exists.
1913 *
1914 * _file_name_ can be an IO object.
1915 *
1916 * "file exists" means that stat() or fstat() system call is successful.
1917 */
1918
1919static VALUE
1920rb_file_exist_p(VALUE obj, VALUE fname)
1921{
1922 struct stat st;
1923
1924 if (rb_stat(fname, &st) < 0) return Qfalse;
1925 return Qtrue;
1926}
1927
1928/*
1929 * call-seq:
1930 * File.readable?(file_name) -> true or false
1931 *
1932 * Returns <code>true</code> if the named file is readable by the effective
1933 * user and group id of this process. See eaccess(3).
1934 *
1935 * Note that some OS-level security features may cause this to return true
1936 * even though the file is not readable by the effective user/group.
1937 */
1938
1939static VALUE
1940rb_file_readable_p(VALUE obj, VALUE fname)
1941{
1942 return RBOOL(rb_eaccess(fname, R_OK) >= 0);
1943}
1944
1945/*
1946 * call-seq:
1947 * File.readable_real?(file_name) -> true or false
1948 *
1949 * Returns <code>true</code> if the named file is readable by the real
1950 * user and group id of this process. See access(3).
1951 *
1952 * Note that some OS-level security features may cause this to return true
1953 * even though the file is not readable by the real user/group.
1954 */
1955
1956static VALUE
1957rb_file_readable_real_p(VALUE obj, VALUE fname)
1958{
1959 return RBOOL(rb_access(fname, R_OK) >= 0);
1960}
1961
1962#ifndef S_IRUGO
1963# define S_IRUGO (S_IRUSR | S_IRGRP | S_IROTH)
1964#endif
1965
1966#ifndef S_IWUGO
1967# define S_IWUGO (S_IWUSR | S_IWGRP | S_IWOTH)
1968#endif
1969
1970/*
1971 * call-seq:
1972 * File.world_readable?(file_name) -> integer or nil
1973 *
1974 * If <i>file_name</i> is readable by others, returns an integer
1975 * representing the file permission bits of <i>file_name</i>. Returns
1976 * <code>nil</code> otherwise. The meaning of the bits is platform
1977 * dependent; on Unix systems, see <code>stat(2)</code>.
1978 *
1979 * _file_name_ can be an IO object.
1980 *
1981 * File.world_readable?("/etc/passwd") #=> 420
1982 * m = File.world_readable?("/etc/passwd")
1983 * sprintf("%o", m) #=> "644"
1984 */
1985
1986static VALUE
1987rb_file_world_readable_p(VALUE obj, VALUE fname)
1988{
1989#ifdef S_IROTH
1990 struct stat st;
1991
1992 if (rb_stat(fname, &st) < 0) return Qnil;
1993 if ((st.st_mode & (S_IROTH)) == S_IROTH) {
1994 return UINT2NUM(st.st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
1995 }
1996#endif
1997 return Qnil;
1998}
1999
2000/*
2001 * call-seq:
2002 * File.writable?(file_name) -> true or false
2003 *
2004 * Returns <code>true</code> if the named file is writable by the effective
2005 * user and group id of this process. See eaccess(3).
2006 *
2007 * Note that some OS-level security features may cause this to return true
2008 * even though the file is not writable by the effective user/group.
2009 */
2010
2011static VALUE
2012rb_file_writable_p(VALUE obj, VALUE fname)
2013{
2014 return RBOOL(rb_eaccess(fname, W_OK) >= 0);
2015}
2016
2017/*
2018 * call-seq:
2019 * File.writable_real?(file_name) -> true or false
2020 *
2021 * Returns <code>true</code> if the named file is writable by the real
2022 * user and group id of this process. See access(3).
2023 *
2024 * Note that some OS-level security features may cause this to return true
2025 * even though the file is not writable by the real user/group.
2026 */
2027
2028static VALUE
2029rb_file_writable_real_p(VALUE obj, VALUE fname)
2030{
2031 return RBOOL(rb_access(fname, W_OK) >= 0);
2032}
2033
2034/*
2035 * call-seq:
2036 * File.world_writable?(file_name) -> integer or nil
2037 *
2038 * If <i>file_name</i> is writable by others, returns an integer
2039 * representing the file permission bits of <i>file_name</i>. Returns
2040 * <code>nil</code> otherwise. The meaning of the bits is platform
2041 * dependent; on Unix systems, see <code>stat(2)</code>.
2042 *
2043 * _file_name_ can be an IO object.
2044 *
2045 * File.world_writable?("/tmp") #=> 511
2046 * m = File.world_writable?("/tmp")
2047 * sprintf("%o", m) #=> "777"
2048 */
2049
2050static VALUE
2051rb_file_world_writable_p(VALUE obj, VALUE fname)
2052{
2053#ifdef S_IWOTH
2054 struct stat st;
2055
2056 if (rb_stat(fname, &st) < 0) return Qnil;
2057 if ((st.st_mode & (S_IWOTH)) == S_IWOTH) {
2058 return UINT2NUM(st.st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
2059 }
2060#endif
2061 return Qnil;
2062}
2063
2064/*
2065 * call-seq:
2066 * File.executable?(file_name) -> true or false
2067 *
2068 * Returns <code>true</code> if the named file is executable by the effective
2069 * user and group id of this process. See eaccess(3).
2070 *
2071 * Windows does not support execute permissions separately from read
2072 * permissions. On Windows, a file is only considered executable if it ends in
2073 * .bat, .cmd, .com, or .exe.
2074 *
2075 * Note that some OS-level security features may cause this to return true
2076 * even though the file is not executable by the effective user/group.
2077 */
2078
2079static VALUE
2080rb_file_executable_p(VALUE obj, VALUE fname)
2081{
2082 return RBOOL(rb_eaccess(fname, X_OK) >= 0);
2083}
2084
2085/*
2086 * call-seq:
2087 * File.executable_real?(file_name) -> true or false
2088 *
2089 * Returns <code>true</code> if the named file is executable by the real
2090 * user and group id of this process. See access(3).
2091 *
2092 * Windows does not support execute permissions separately from read
2093 * permissions. On Windows, a file is only considered executable if it ends in
2094 * .bat, .cmd, .com, or .exe.
2095 *
2096 * Note that some OS-level security features may cause this to return true
2097 * even though the file is not executable by the real user/group.
2098 */
2099
2100static VALUE
2101rb_file_executable_real_p(VALUE obj, VALUE fname)
2102{
2103 return RBOOL(rb_access(fname, X_OK) >= 0);
2104}
2105
2106#ifndef S_ISREG
2107# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
2108#endif
2109
2110/*
2111 * call-seq:
2112 * File.file?(file) -> true or false
2113 *
2114 * Returns +true+ if the named +file+ exists and is a regular file.
2115 *
2116 * +file+ can be an IO object.
2117 *
2118 * If the +file+ argument is a symbolic link, it will resolve the symbolic link
2119 * and use the file referenced by the link.
2120 */
2121
2122static VALUE
2123rb_file_file_p(VALUE obj, VALUE fname)
2124{
2125 struct stat st;
2126
2127 if (rb_stat(fname, &st) < 0) return Qfalse;
2128 return RBOOL(S_ISREG(st.st_mode));
2129}
2130
2131/*
2132 * call-seq:
2133 * File.zero?(file_name) -> true or false
2134 *
2135 * Returns <code>true</code> if the named file exists and has
2136 * a zero size.
2137 *
2138 * _file_name_ can be an IO object.
2139 */
2140
2141static VALUE
2142rb_file_zero_p(VALUE obj, VALUE fname)
2143{
2144 struct stat st;
2145
2146 if (rb_stat(fname, &st) < 0) return Qfalse;
2147 return RBOOL(st.st_size == 0);
2148}
2149
2150/*
2151 * call-seq:
2152 * File.size?(file_name) -> Integer or nil
2153 *
2154 * Returns +nil+ if +file_name+ doesn't exist or has zero size, the size of the
2155 * file otherwise.
2156 *
2157 * _file_name_ can be an IO object.
2158 */
2159
2160static VALUE
2161rb_file_size_p(VALUE obj, VALUE fname)
2162{
2163 struct stat st;
2164
2165 if (rb_stat(fname, &st) < 0) return Qnil;
2166 if (st.st_size == 0) return Qnil;
2167 return OFFT2NUM(st.st_size);
2168}
2169
2170/*
2171 * call-seq:
2172 * File.owned?(file_name) -> true or false
2173 *
2174 * Returns <code>true</code> if the named file exists and the
2175 * effective used id of the calling process is the owner of
2176 * the file.
2177 *
2178 * _file_name_ can be an IO object.
2179 */
2180
2181static VALUE
2182rb_file_owned_p(VALUE obj, VALUE fname)
2183{
2184 struct stat st;
2185
2186 if (rb_stat(fname, &st) < 0) return Qfalse;
2187 return RBOOL(st.st_uid == geteuid());
2188}
2189
2190static VALUE
2191rb_file_rowned_p(VALUE obj, VALUE fname)
2192{
2193 struct stat st;
2194
2195 if (rb_stat(fname, &st) < 0) return Qfalse;
2196 return RBOOL(st.st_uid == getuid());
2197}
2198
2199/*
2200 * call-seq:
2201 * File.grpowned?(file_name) -> true or false
2202 *
2203 * Returns <code>true</code> if the named file exists and the
2204 * effective group id of the calling process is the owner of
2205 * the file. Returns <code>false</code> on Windows.
2206 *
2207 * _file_name_ can be an IO object.
2208 */
2209
2210static VALUE
2211rb_file_grpowned_p(VALUE obj, VALUE fname)
2212{
2213#ifndef _WIN32
2214 struct stat st;
2215
2216 if (rb_stat(fname, &st) < 0) return Qfalse;
2217 if (rb_group_member(st.st_gid)) return Qtrue;
2218#endif
2219 return Qfalse;
2220}
2221
2222#if defined(S_ISUID) || defined(S_ISGID) || defined(S_ISVTX)
2223static VALUE
2224check3rdbyte(VALUE fname, int mode)
2225{
2226 struct stat st;
2227
2228 if (rb_stat(fname, &st) < 0) return Qfalse;
2229 return RBOOL(st.st_mode & mode);
2230}
2231#endif
2232
2233/*
2234 * call-seq:
2235 * File.setuid?(file_name) -> true or false
2236 *
2237 * Returns <code>true</code> if the named file has the setuid bit set.
2238 *
2239 * _file_name_ can be an IO object.
2240 */
2241
2242static VALUE
2243rb_file_suid_p(VALUE obj, VALUE fname)
2244{
2245#ifdef S_ISUID
2246 return check3rdbyte(fname, S_ISUID);
2247#else
2248 return Qfalse;
2249#endif
2250}
2251
2252/*
2253 * call-seq:
2254 * File.setgid?(file_name) -> true or false
2255 *
2256 * Returns <code>true</code> if the named file has the setgid bit set.
2257 *
2258 * _file_name_ can be an IO object.
2259 */
2260
2261static VALUE
2262rb_file_sgid_p(VALUE obj, VALUE fname)
2263{
2264#ifdef S_ISGID
2265 return check3rdbyte(fname, S_ISGID);
2266#else
2267 return Qfalse;
2268#endif
2269}
2270
2271/*
2272 * call-seq:
2273 * File.sticky?(file_name) -> true or false
2274 *
2275 * Returns <code>true</code> if the named file has the sticky bit set.
2276 *
2277 * _file_name_ can be an IO object.
2278 */
2279
2280static VALUE
2281rb_file_sticky_p(VALUE obj, VALUE fname)
2282{
2283#ifdef S_ISVTX
2284 return check3rdbyte(fname, S_ISVTX);
2285#else
2286 return Qfalse;
2287#endif
2288}
2289
2290/*
2291 * call-seq:
2292 * File.identical?(file_1, file_2) -> true or false
2293 *
2294 * Returns <code>true</code> if the named files are identical.
2295 *
2296 * _file_1_ and _file_2_ can be an IO object.
2297 *
2298 * open("a", "w") {}
2299 * p File.identical?("a", "a") #=> true
2300 * p File.identical?("a", "./a") #=> true
2301 * File.link("a", "b")
2302 * p File.identical?("a", "b") #=> true
2303 * File.symlink("a", "c")
2304 * p File.identical?("a", "c") #=> true
2305 * open("d", "w") {}
2306 * p File.identical?("a", "d") #=> false
2307 */
2308
2309static VALUE
2310rb_file_identical_p(VALUE obj, VALUE fname1, VALUE fname2)
2311{
2312#ifndef _WIN32
2313 struct stat st1, st2;
2314
2315 if (rb_stat(fname1, &st1) < 0) return Qfalse;
2316 if (rb_stat(fname2, &st2) < 0) return Qfalse;
2317 if (st1.st_dev != st2.st_dev) return Qfalse;
2318 if (st1.st_ino != st2.st_ino) return Qfalse;
2319 return Qtrue;
2320#else
2321 extern VALUE rb_w32_file_identical_p(VALUE, VALUE);
2322 return rb_w32_file_identical_p(fname1, fname2);
2323#endif
2324}
2325
2326/*
2327 * call-seq:
2328 * File.size(file_name) -> integer
2329 *
2330 * Returns the size of <code>file_name</code>.
2331 *
2332 * _file_name_ can be an IO object.
2333 */
2334
2335static VALUE
2336rb_file_s_size(VALUE klass, VALUE fname)
2337{
2338 struct stat st;
2339
2340 if (rb_stat(fname, &st) < 0) {
2341 int e = errno;
2342 FilePathValue(fname);
2343 rb_syserr_fail_path(e, fname);
2344 }
2345 return OFFT2NUM(st.st_size);
2346}
2347
2348static VALUE
2349rb_file_ftype(mode_t mode)
2350{
2351 const char *t;
2352
2353 if (S_ISREG(mode)) {
2354 t = "file";
2355 }
2356 else if (S_ISDIR(mode)) {
2357 t = "directory";
2358 }
2359 else if (S_ISCHR(mode)) {
2360 t = "characterSpecial";
2361 }
2362#ifdef S_ISBLK
2363 else if (S_ISBLK(mode)) {
2364 t = "blockSpecial";
2365 }
2366#endif
2367#ifdef S_ISFIFO
2368 else if (S_ISFIFO(mode)) {
2369 t = "fifo";
2370 }
2371#endif
2372#ifdef S_ISLNK
2373 else if (S_ISLNK(mode)) {
2374 t = "link";
2375 }
2376#endif
2377#ifdef S_ISSOCK
2378 else if (S_ISSOCK(mode)) {
2379 t = "socket";
2380 }
2381#endif
2382 else {
2383 t = "unknown";
2384 }
2385
2386 return rb_usascii_str_new2(t);
2387}
2388
2389/*
2390 * call-seq:
2391 * File.ftype(file_name) -> string
2392 *
2393 * Identifies the type of the named file; the return string is one of
2394 * ``<code>file</code>'', ``<code>directory</code>'',
2395 * ``<code>characterSpecial</code>'', ``<code>blockSpecial</code>'',
2396 * ``<code>fifo</code>'', ``<code>link</code>'',
2397 * ``<code>socket</code>'', or ``<code>unknown</code>''.
2398 *
2399 * File.ftype("testfile") #=> "file"
2400 * File.ftype("/dev/tty") #=> "characterSpecial"
2401 * File.ftype("/tmp/.X11-unix/X0") #=> "socket"
2402 */
2403
2404static VALUE
2405rb_file_s_ftype(VALUE klass, VALUE fname)
2406{
2407 struct stat st;
2408
2409 FilePathValue(fname);
2410 fname = rb_str_encode_ospath(fname);
2411 if (lstat_without_gvl(StringValueCStr(fname), &st) == -1) {
2412 rb_sys_fail_path(fname);
2413 }
2414
2415 return rb_file_ftype(st.st_mode);
2416}
2417
2418/*
2419 * call-seq:
2420 * File.atime(file_name) -> time
2421 *
2422 * Returns the last access time for the named file as a Time object.
2423 *
2424 * _file_name_ can be an IO object.
2425 *
2426 * File.atime("testfile") #=> Wed Apr 09 08:51:48 CDT 2003
2427 *
2428 */
2429
2430static VALUE
2431rb_file_s_atime(VALUE klass, VALUE fname)
2432{
2433 struct stat st;
2434
2435 if (rb_stat(fname, &st) < 0) {
2436 int e = errno;
2437 FilePathValue(fname);
2438 rb_syserr_fail_path(e, fname);
2439 }
2440 return stat_time(stat_atimespec(&st));
2441}
2442
2443/*
2444 * call-seq:
2445 * file.atime -> time
2446 *
2447 * Returns the last access time (a Time object) for <i>file</i>, or
2448 * epoch if <i>file</i> has not been accessed.
2449 *
2450 * File.new("testfile").atime #=> Wed Dec 31 18:00:00 CST 1969
2451 *
2452 */
2453
2454static VALUE
2455rb_file_atime(VALUE obj)
2456{
2457 rb_io_t *fptr;
2458 struct stat st;
2459
2460 GetOpenFile(obj, fptr);
2461 if (fstat(fptr->fd, &st) == -1) {
2462 rb_sys_fail_path(fptr->pathv);
2463 }
2464 return stat_time(stat_atimespec(&st));
2465}
2466
2467/*
2468 * call-seq:
2469 * File.mtime(file_name) -> time
2470 *
2471 * Returns the modification time for the named file as a Time object.
2472 *
2473 * _file_name_ can be an IO object.
2474 *
2475 * File.mtime("testfile") #=> Tue Apr 08 12:58:04 CDT 2003
2476 *
2477 */
2478
2479static VALUE
2480rb_file_s_mtime(VALUE klass, VALUE fname)
2481{
2482 struct stat st;
2483
2484 if (rb_stat(fname, &st) < 0) {
2485 int e = errno;
2486 FilePathValue(fname);
2487 rb_syserr_fail_path(e, fname);
2488 }
2489 return stat_time(stat_mtimespec(&st));
2490}
2491
2492/*
2493 * call-seq:
2494 * file.mtime -> time
2495 *
2496 * Returns the modification time for <i>file</i>.
2497 *
2498 * File.new("testfile").mtime #=> Wed Apr 09 08:53:14 CDT 2003
2499 *
2500 */
2501
2502static VALUE
2503rb_file_mtime(VALUE obj)
2504{
2505 rb_io_t *fptr;
2506 struct stat st;
2507
2508 GetOpenFile(obj, fptr);
2509 if (fstat(fptr->fd, &st) == -1) {
2510 rb_sys_fail_path(fptr->pathv);
2511 }
2512 return stat_time(stat_mtimespec(&st));
2513}
2514
2515/*
2516 * call-seq:
2517 * File.ctime(file_name) -> time
2518 *
2519 * Returns the change time for the named file (the time at which
2520 * directory information about the file was changed, not the file
2521 * itself).
2522 *
2523 * _file_name_ can be an IO object.
2524 *
2525 * Note that on Windows (NTFS), returns creation time (birth time).
2526 *
2527 * File.ctime("testfile") #=> Wed Apr 09 08:53:13 CDT 2003
2528 *
2529 */
2530
2531static VALUE
2532rb_file_s_ctime(VALUE klass, VALUE fname)
2533{
2534 struct stat st;
2535
2536 if (rb_stat(fname, &st) < 0) {
2537 int e = errno;
2538 FilePathValue(fname);
2539 rb_syserr_fail_path(e, fname);
2540 }
2541 return stat_time(stat_ctimespec(&st));
2542}
2543
2544/*
2545 * call-seq:
2546 * file.ctime -> time
2547 *
2548 * Returns the change time for <i>file</i> (that is, the time directory
2549 * information about the file was changed, not the file itself).
2550 *
2551 * Note that on Windows (NTFS), returns creation time (birth time).
2552 *
2553 * File.new("testfile").ctime #=> Wed Apr 09 08:53:14 CDT 2003
2554 *
2555 */
2556
2557static VALUE
2558rb_file_ctime(VALUE obj)
2559{
2560 rb_io_t *fptr;
2561 struct stat st;
2562
2563 GetOpenFile(obj, fptr);
2564 if (fstat(fptr->fd, &st) == -1) {
2565 rb_sys_fail_path(fptr->pathv);
2566 }
2567 return stat_time(stat_ctimespec(&st));
2568}
2569
2570#if defined(HAVE_STAT_BIRTHTIME)
2571/*
2572 * call-seq:
2573 * File.birthtime(file_name) -> time
2574 *
2575 * Returns the birth time for the named file.
2576 *
2577 * _file_name_ can be an IO object.
2578 *
2579 * File.birthtime("testfile") #=> Wed Apr 09 08:53:13 CDT 2003
2580 *
2581 * If the platform doesn't have birthtime, raises NotImplementedError.
2582 *
2583 */
2584
2585VALUE
2586rb_file_s_birthtime(VALUE klass, VALUE fname)
2587{
2588 rb_io_stat_data st;
2589
2590 if (rb_statx(fname, &st, STATX_BTIME) < 0) {
2591 int e = errno;
2592 FilePathValue(fname);
2593 rb_syserr_fail_path(e, fname);
2594 }
2595 return statx_birthtime(&st);
2596}
2597#else
2598# define rb_file_s_birthtime rb_f_notimplement
2599#endif
2600
2601#if defined(HAVE_STAT_BIRTHTIME)
2602/*
2603 * call-seq:
2604 * file.birthtime -> time
2605 *
2606 * Returns the birth time for <i>file</i>.
2607 *
2608 * File.new("testfile").birthtime #=> Wed Apr 09 08:53:14 CDT 2003
2609 *
2610 * If the platform doesn't have birthtime, raises NotImplementedError.
2611 *
2612 */
2613
2614static VALUE
2615rb_file_birthtime(VALUE obj)
2616{
2617 rb_io_t *fptr;
2618 rb_io_stat_data st;
2619
2620 GetOpenFile(obj, fptr);
2621 if (fstatx_without_gvl(fptr, &st, STATX_BTIME) == -1) {
2622 rb_sys_fail_path(fptr->pathv);
2623 }
2624 return statx_birthtime(&st);
2625}
2626#else
2627# define rb_file_birthtime rb_f_notimplement
2628#endif
2629
2630rb_off_t
2631rb_file_size(VALUE file)
2632{
2633 if (RB_TYPE_P(file, T_FILE)) {
2634 rb_io_t *fptr;
2635 struct stat st;
2636
2637 RB_IO_POINTER(file, fptr);
2638 if (fptr->mode & FMODE_WRITABLE) {
2639 rb_io_flush_raw(file, 0);
2640 }
2641
2642 if (fstat(fptr->fd, &st) == -1) {
2643 rb_sys_fail_path(fptr->pathv);
2644 }
2645
2646 return st.st_size;
2647 }
2648 else {
2649 return NUM2OFFT(rb_funcall(file, idSize, 0));
2650 }
2651}
2652
2653/*
2654 * call-seq:
2655 * file.size -> integer
2656 *
2657 * Returns the size of <i>file</i> in bytes.
2658 *
2659 * File.new("testfile").size #=> 66
2660 *
2661 */
2662
2663static VALUE
2664file_size(VALUE self)
2665{
2666 return OFFT2NUM(rb_file_size(self));
2667}
2668
2670 const char *path;
2671 mode_t mode;
2672};
2673
2674static void *
2675nogvl_chmod(void *ptr)
2676{
2677 struct nogvl_chmod_data *data = ptr;
2678 int ret = chmod(data->path, data->mode);
2679 return (void *)(VALUE)ret;
2680}
2681
2682static int
2683rb_chmod(const char *path, mode_t mode)
2684{
2685 struct nogvl_chmod_data data = {
2686 .path = path,
2687 .mode = mode,
2688 };
2689 return IO_WITHOUT_GVL_INT(nogvl_chmod, &data);
2690}
2691
2692static int
2693chmod_internal(const char *path, void *mode)
2694{
2695 return chmod(path, *(mode_t *)mode);
2696}
2697
2698/*
2699 * call-seq:
2700 * File.chmod(mode_int, file_name, ... ) -> integer
2701 *
2702 * Changes permission bits on the named file(s) to the bit pattern
2703 * represented by <i>mode_int</i>. Actual effects are operating system
2704 * dependent (see the beginning of this section). On Unix systems, see
2705 * <code>chmod(2)</code> for details. Returns the number of files
2706 * processed.
2707 *
2708 * File.chmod(0644, "testfile", "out") #=> 2
2709 */
2710
2711static VALUE
2712rb_file_s_chmod(int argc, VALUE *argv, VALUE _)
2713{
2714 mode_t mode;
2715
2716 apply2args(1);
2717 mode = NUM2MODET(*argv++);
2718
2719 return apply2files(chmod_internal, argc, argv, &mode);
2720}
2721
2722#ifdef HAVE_FCHMOD
2723struct nogvl_fchmod_data {
2724 int fd;
2725 mode_t mode;
2726};
2727
2728static VALUE
2729io_blocking_fchmod(void *ptr)
2730{
2731 struct nogvl_fchmod_data *data = ptr;
2732 int ret = fchmod(data->fd, data->mode);
2733 return (VALUE)ret;
2734}
2735
2736static int
2737rb_fchmod(struct rb_io* io, mode_t mode)
2738{
2739 (void)rb_chmod; /* suppress unused-function warning when HAVE_FCHMOD */
2740 struct nogvl_fchmod_data data = {.fd = io->fd, .mode = mode};
2741 return (int)rb_thread_io_blocking_region(io, io_blocking_fchmod, &data);
2742}
2743#endif
2744
2745/*
2746 * call-seq:
2747 * file.chmod(mode_int) -> 0
2748 *
2749 * Changes permission bits on <i>file</i> to the bit pattern
2750 * represented by <i>mode_int</i>. Actual effects are platform
2751 * dependent; on Unix systems, see <code>chmod(2)</code> for details.
2752 * Follows symbolic links. Also see File#lchmod.
2753 *
2754 * f = File.new("out", "w");
2755 * f.chmod(0644) #=> 0
2756 */
2757
2758static VALUE
2759rb_file_chmod(VALUE obj, VALUE vmode)
2760{
2761 rb_io_t *fptr;
2762 mode_t mode;
2763#if !defined HAVE_FCHMOD || !HAVE_FCHMOD
2764 VALUE path;
2765#endif
2766
2767 mode = NUM2MODET(vmode);
2768
2769 GetOpenFile(obj, fptr);
2770#ifdef HAVE_FCHMOD
2771 if (rb_fchmod(fptr, mode) == -1) {
2772 if (HAVE_FCHMOD || errno != ENOSYS)
2773 rb_sys_fail_path(fptr->pathv);
2774 }
2775 else {
2776 if (!HAVE_FCHMOD) return INT2FIX(0);
2777 }
2778#endif
2779#if !defined HAVE_FCHMOD || !HAVE_FCHMOD
2780 if (NIL_P(fptr->pathv)) return Qnil;
2781 path = rb_str_encode_ospath(fptr->pathv);
2782 if (rb_chmod(RSTRING_PTR(path), mode) == -1)
2783 rb_sys_fail_path(fptr->pathv);
2784#endif
2785
2786 return INT2FIX(0);
2787}
2788
2789#if defined(HAVE_LCHMOD)
2790static int
2791lchmod_internal(const char *path, void *mode)
2792{
2793 return lchmod(path, *(mode_t *)mode);
2794}
2795
2796/*
2797 * call-seq:
2798 * File.lchmod(mode_int, file_name, ...) -> integer
2799 *
2800 * Equivalent to File::chmod, but does not follow symbolic links (so
2801 * it will change the permissions associated with the link, not the
2802 * file referenced by the link). Often not available.
2803 *
2804 */
2805
2806static VALUE
2807rb_file_s_lchmod(int argc, VALUE *argv, VALUE _)
2808{
2809 mode_t mode;
2810
2811 apply2args(1);
2812 mode = NUM2MODET(*argv++);
2813
2814 return apply2files(lchmod_internal, argc, argv, &mode);
2815}
2816#else
2817#define rb_file_s_lchmod rb_f_notimplement
2818#endif
2819
2820static inline rb_uid_t
2821to_uid(VALUE u)
2822{
2823 if (NIL_P(u)) {
2824 return (rb_uid_t)-1;
2825 }
2826 return NUM2UIDT(u);
2827}
2828
2829static inline rb_gid_t
2830to_gid(VALUE g)
2831{
2832 if (NIL_P(g)) {
2833 return (rb_gid_t)-1;
2834 }
2835 return NUM2GIDT(g);
2836}
2837
2839 rb_uid_t owner;
2840 rb_gid_t group;
2841};
2842
2843static int
2844chown_internal(const char *path, void *arg)
2845{
2846 struct chown_args *args = arg;
2847 return chown(path, args->owner, args->group);
2848}
2849
2850/*
2851 * call-seq:
2852 * File.chown(owner_int, group_int, file_name, ...) -> integer
2853 *
2854 * Changes the owner and group of the named file(s) to the given
2855 * numeric owner and group id's. Only a process with superuser
2856 * privileges may change the owner of a file. The current owner of a
2857 * file may change the file's group to any group to which the owner
2858 * belongs. A <code>nil</code> or -1 owner or group id is ignored.
2859 * Returns the number of files processed.
2860 *
2861 * File.chown(nil, 100, "testfile")
2862 *
2863 */
2864
2865static VALUE
2866rb_file_s_chown(int argc, VALUE *argv, VALUE _)
2867{
2868 struct chown_args arg;
2869
2870 apply2args(2);
2871 arg.owner = to_uid(*argv++);
2872 arg.group = to_gid(*argv++);
2873
2874 return apply2files(chown_internal, argc, argv, &arg);
2875}
2876
2878 union {
2879 const char *path;
2880 int fd;
2881 } as;
2882 struct chown_args new;
2883};
2884
2885static void *
2886nogvl_chown(void *ptr)
2887{
2888 struct nogvl_chown_data *data = ptr;
2889 return (void *)(VALUE)chown(data->as.path, data->new.owner, data->new.group);
2890}
2891
2892static int
2893rb_chown(const char *path, rb_uid_t owner, rb_gid_t group)
2894{
2895 struct nogvl_chown_data data = {
2896 .as = {.path = path},
2897 .new = {.owner = owner, .group = group},
2898 };
2899 return IO_WITHOUT_GVL_INT(nogvl_chown, &data);
2900}
2901
2902#ifdef HAVE_FCHOWN
2903static void *
2904nogvl_fchown(void *ptr)
2905{
2906 struct nogvl_chown_data *data = ptr;
2907 return (void *)(VALUE)fchown(data->as.fd, data->new.owner, data->new.group);
2908}
2909
2910static int
2911rb_fchown(int fd, rb_uid_t owner, rb_gid_t group)
2912{
2913 (void)rb_chown; /* suppress unused-function warning when HAVE_FCHMOD */
2914 struct nogvl_chown_data data = {
2915 .as = {.fd = fd},
2916 .new = {.owner = owner, .group = group},
2917 };
2918 return IO_WITHOUT_GVL_INT(nogvl_fchown, &data);
2919}
2920#endif
2921
2922/*
2923 * call-seq:
2924 * file.chown(owner_int, group_int ) -> 0
2925 *
2926 * Changes the owner and group of <i>file</i> to the given numeric
2927 * owner and group id's. Only a process with superuser privileges may
2928 * change the owner of a file. The current owner of a file may change
2929 * the file's group to any group to which the owner belongs. A
2930 * <code>nil</code> or -1 owner or group id is ignored. Follows
2931 * symbolic links. See also File#lchown.
2932 *
2933 * File.new("testfile").chown(502, 1000)
2934 *
2935 */
2936
2937static VALUE
2938rb_file_chown(VALUE obj, VALUE owner, VALUE group)
2939{
2940 rb_io_t *fptr;
2941 rb_uid_t o;
2942 rb_gid_t g;
2943#ifndef HAVE_FCHOWN
2944 VALUE path;
2945#endif
2946
2947 o = to_uid(owner);
2948 g = to_gid(group);
2949 GetOpenFile(obj, fptr);
2950#ifndef HAVE_FCHOWN
2951 if (NIL_P(fptr->pathv)) return Qnil;
2952 path = rb_str_encode_ospath(fptr->pathv);
2953 if (rb_chown(RSTRING_PTR(path), o, g) == -1)
2954 rb_sys_fail_path(fptr->pathv);
2955#else
2956 if (rb_fchown(fptr->fd, o, g) == -1)
2957 rb_sys_fail_path(fptr->pathv);
2958#endif
2959
2960 return INT2FIX(0);
2961}
2962
2963#if defined(HAVE_LCHOWN)
2964static int
2965lchown_internal(const char *path, void *arg)
2966{
2967 struct chown_args *args = arg;
2968 return lchown(path, args->owner, args->group);
2969}
2970
2971/*
2972 * call-seq:
2973 * File.lchown(owner_int, group_int, file_name,..) -> integer
2974 *
2975 * Equivalent to File::chown, but does not follow symbolic
2976 * links (so it will change the owner associated with the link, not the
2977 * file referenced by the link). Often not available. Returns number
2978 * of files in the argument list.
2979 *
2980 */
2981
2982static VALUE
2983rb_file_s_lchown(int argc, VALUE *argv, VALUE _)
2984{
2985 struct chown_args arg;
2986
2987 apply2args(2);
2988 arg.owner = to_uid(*argv++);
2989 arg.group = to_gid(*argv++);
2990
2991 return apply2files(lchown_internal, argc, argv, &arg);
2992}
2993#else
2994#define rb_file_s_lchown rb_f_notimplement
2995#endif
2996
2998 const struct timespec* tsp;
2999 VALUE atime, mtime;
3000 int follow; /* Whether to act on symlinks (1) or their referent (0) */
3001};
3002
3003#ifdef UTIME_EINVAL
3004NORETURN(static void utime_failed(struct apply_arg *));
3005
3006static void
3007utime_failed(struct apply_arg *aa)
3008{
3009 int e = aa->errnum;
3010 VALUE path = aa->fn[aa->i].path;
3011 struct utime_args *ua = aa->arg;
3012
3013 if (ua->tsp && e == EINVAL) {
3014 VALUE e[2], a = Qnil, m = Qnil;
3015 int d = 0;
3016 VALUE atime = ua->atime;
3017 VALUE mtime = ua->mtime;
3018
3019 if (!NIL_P(atime)) {
3020 a = rb_inspect(atime);
3021 }
3022 if (!NIL_P(mtime) && mtime != atime && !rb_equal(atime, mtime)) {
3023 m = rb_inspect(mtime);
3024 }
3025 if (NIL_P(a)) e[0] = m;
3026 else if (NIL_P(m) || rb_str_cmp(a, m) == 0) e[0] = a;
3027 else {
3028 e[0] = rb_str_plus(a, rb_str_new_cstr(" or "));
3029 rb_str_append(e[0], m);
3030 d = 1;
3031 }
3032 if (!NIL_P(e[0])) {
3033 if (path) {
3034 if (!d) e[0] = rb_str_dup(e[0]);
3035 rb_str_append(rb_str_cat2(e[0], " for "), path);
3036 }
3037 e[1] = INT2FIX(EINVAL);
3039 }
3040 }
3041 rb_syserr_fail_path(e, path);
3042}
3043#endif /* UTIME_EINVAL */
3044
3045#if defined(HAVE_UTIMES)
3046
3047# if !defined(HAVE_UTIMENSAT)
3048/* utimensat() is not found, runtime check is not needed */
3049# elif defined(__APPLE__) && \
3050 (!defined(MAC_OS_X_VERSION_13_0) || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_13_0))
3051
3052# if __has_attribute(availability) && __has_warning("-Wunguarded-availability-new")
3053typedef int utimensat_func(int, const char *, const struct timespec [2], int);
3054
3056RBIMPL_WARNING_IGNORED(-Wunguarded-availability-new)
3057static inline utimensat_func *
3058rb_utimensat(void)
3059{
3060 return &utimensat;
3061}
3063
3064# define utimensat rb_utimensat()
3065# else /* __API_AVAILABLE macro does nothing on gcc */
3066__attribute__((weak)) int utimensat(int, const char *, const struct timespec [2], int);
3067# endif /* utimesat availability */
3068# endif /* __APPLE__ && < MAC_OS_X_VERSION_13_0 */
3069
3070static int
3071utime_internal(const char *path, void *arg)
3072{
3073 struct utime_args *v = arg;
3074 const struct timespec *tsp = v->tsp;
3075 struct timeval tvbuf[2], *tvp = NULL;
3076
3077#if defined(HAVE_UTIMENSAT)
3078# if defined(__APPLE__)
3079 const int try_utimensat = utimensat != NULL;
3080 const int try_utimensat_follow = utimensat != NULL;
3081# else /* !__APPLE__ */
3082# define TRY_UTIMENSAT 1
3083 static int try_utimensat = 1;
3084# ifdef AT_SYMLINK_NOFOLLOW
3085 static int try_utimensat_follow = 1;
3086# else
3087 const int try_utimensat_follow = 0;
3088# endif
3089# endif /* __APPLE__ */
3090 int flags = 0;
3091
3092 if (v->follow ? try_utimensat_follow : try_utimensat) {
3093# ifdef AT_SYMLINK_NOFOLLOW
3094 if (v->follow) {
3095 flags = AT_SYMLINK_NOFOLLOW;
3096 }
3097# endif
3098
3099 int result = utimensat(AT_FDCWD, path, tsp, flags);
3100# ifdef TRY_UTIMENSAT
3101 if (result < 0 && errno == ENOSYS) {
3102# ifdef AT_SYMLINK_NOFOLLOW
3103 try_utimensat_follow = 0;
3104# endif /* AT_SYMLINK_NOFOLLOW */
3105 if (!v->follow)
3106 try_utimensat = 0;
3107 }
3108 else
3109# endif /* TRY_UTIMESAT */
3110 return result;
3111 }
3112#endif /* defined(HAVE_UTIMENSAT) */
3113
3114 if (tsp) {
3115 tvbuf[0].tv_sec = tsp[0].tv_sec;
3116 tvbuf[0].tv_usec = (int)(tsp[0].tv_nsec / 1000);
3117 tvbuf[1].tv_sec = tsp[1].tv_sec;
3118 tvbuf[1].tv_usec = (int)(tsp[1].tv_nsec / 1000);
3119 tvp = tvbuf;
3120 }
3121#ifdef HAVE_LUTIMES
3122 if (v->follow) return lutimes(path, tvp);
3123#endif
3124 return utimes(path, tvp);
3125}
3126
3127#else /* !defined(HAVE_UTIMES) */
3128
3129#if !defined HAVE_UTIME_H && !defined HAVE_SYS_UTIME_H
3130struct utimbuf {
3131 long actime;
3132 long modtime;
3133};
3134#endif
3135
3136static int
3137utime_internal(const char *path, void *arg)
3138{
3139 struct utime_args *v = arg;
3140 const stat_timestamp *tsp = v->tsp;
3141 struct utimbuf utbuf, *utp = NULL;
3142 if (tsp) {
3143 utbuf.actime = tsp[0].tv_sec;
3144 utbuf.modtime = tsp[1].tv_sec;
3145 utp = &utbuf;
3146 }
3147 return utime(path, utp);
3148}
3149#endif /* !defined(HAVE_UTIMES) */
3150
3151static VALUE
3152utime_internal_i(int argc, VALUE *argv, int follow)
3153{
3154 struct utime_args args;
3155 struct timespec tss[2], *tsp = NULL;
3156
3157 apply2args(2);
3158 args.atime = *argv++;
3159 args.mtime = *argv++;
3160
3161 args.follow = follow;
3162
3163 if (!NIL_P(args.atime) || !NIL_P(args.mtime)) {
3164 tsp = tss;
3165 tsp[0] = rb_time_timespec(args.atime);
3166 if (args.atime == args.mtime)
3167 tsp[1] = tsp[0];
3168 else
3169 tsp[1] = rb_time_timespec(args.mtime);
3170 }
3171 args.tsp = tsp;
3172
3173 return apply2files(utime_internal, argc, argv, &args);
3174}
3175
3176/*
3177 * call-seq:
3178 * File.utime(atime, mtime, file_name, ...) -> integer
3179 *
3180 * Sets the access and modification times of each named file to the
3181 * first two arguments. If a file is a symlink, this method acts upon
3182 * its referent rather than the link itself; for the inverse
3183 * behavior see File.lutime. Returns the number of file
3184 * names in the argument list.
3185 */
3186
3187static VALUE
3188rb_file_s_utime(int argc, VALUE *argv, VALUE _)
3189{
3190 return utime_internal_i(argc, argv, FALSE);
3191}
3192
3193#if defined(HAVE_UTIMES) && (defined(HAVE_LUTIMES) || (defined(HAVE_UTIMENSAT) && defined(AT_SYMLINK_NOFOLLOW)))
3194
3195/*
3196 * call-seq:
3197 * File.lutime(atime, mtime, file_name, ...) -> integer
3198 *
3199 * Sets the access and modification times of each named file to the
3200 * first two arguments. If a file is a symlink, this method acts upon
3201 * the link itself as opposed to its referent; for the inverse
3202 * behavior, see File.utime. Returns the number of file
3203 * names in the argument list.
3204 */
3205
3206static VALUE
3207rb_file_s_lutime(int argc, VALUE *argv, VALUE _)
3208{
3209 return utime_internal_i(argc, argv, TRUE);
3210}
3211#else
3212#define rb_file_s_lutime rb_f_notimplement
3213#endif
3214
3215#ifdef RUBY_FUNCTION_NAME_STRING
3216# define syserr_fail2(e, s1, s2) syserr_fail2_in(RUBY_FUNCTION_NAME_STRING, e, s1, s2)
3217#else
3218# define syserr_fail2_in(func, e, s1, s2) syserr_fail2(e, s1, s2)
3219#endif
3220#define sys_fail2(s1, s2) syserr_fail2(errno, s1, s2)
3221NORETURN(static void syserr_fail2_in(const char *,int,VALUE,VALUE));
3222static void
3223syserr_fail2_in(const char *func, int e, VALUE s1, VALUE s2)
3224{
3225 VALUE str;
3226#ifdef MAX_PATH
3227 const int max_pathlen = MAX_PATH;
3228#else
3229 const int max_pathlen = MAXPATHLEN;
3230#endif
3231
3232 if (e == EEXIST) {
3233 rb_syserr_fail_path(e, rb_str_ellipsize(s2, max_pathlen));
3234 }
3235 str = rb_str_new_cstr("(");
3236 rb_str_append(str, rb_str_ellipsize(s1, max_pathlen));
3237 rb_str_cat2(str, ", ");
3238 rb_str_append(str, rb_str_ellipsize(s2, max_pathlen));
3239 rb_str_cat2(str, ")");
3240#ifdef RUBY_FUNCTION_NAME_STRING
3241 rb_syserr_fail_path_in(func, e, str);
3242#else
3243 rb_syserr_fail_path(e, str);
3244#endif
3245}
3246
3247#ifdef HAVE_LINK
3248/*
3249 * call-seq:
3250 * File.link(old_name, new_name) -> 0
3251 *
3252 * Creates a new name for an existing file using a hard link. Will not
3253 * overwrite <i>new_name</i> if it already exists (raising a subclass
3254 * of SystemCallError). Not available on all platforms.
3255 *
3256 * File.link("testfile", ".testfile") #=> 0
3257 * IO.readlines(".testfile")[0] #=> "This is line one\n"
3258 */
3259
3260static VALUE
3261rb_file_s_link(VALUE klass, VALUE from, VALUE to)
3262{
3263 FilePathValue(from);
3264 FilePathValue(to);
3265 from = rb_str_encode_ospath(from);
3266 to = rb_str_encode_ospath(to);
3267
3268 if (link(StringValueCStr(from), StringValueCStr(to)) < 0) {
3269 sys_fail2(from, to);
3270 }
3271 return INT2FIX(0);
3272}
3273#else
3274#define rb_file_s_link rb_f_notimplement
3275#endif
3276
3277#ifdef HAVE_SYMLINK
3278/*
3279 * call-seq:
3280 * File.symlink(old_name, new_name) -> 0
3281 *
3282 * Creates a symbolic link called <i>new_name</i> for the existing file
3283 * <i>old_name</i>. Raises a NotImplemented exception on
3284 * platforms that do not support symbolic links.
3285 *
3286 * File.symlink("testfile", "link2test") #=> 0
3287 *
3288 */
3289
3290static VALUE
3291rb_file_s_symlink(VALUE klass, VALUE from, VALUE to)
3292{
3293 FilePathValue(from);
3294 FilePathValue(to);
3295 from = rb_str_encode_ospath(from);
3296 to = rb_str_encode_ospath(to);
3297
3298 if (symlink(StringValueCStr(from), StringValueCStr(to)) < 0) {
3299 sys_fail2(from, to);
3300 }
3301 return INT2FIX(0);
3302}
3303#else
3304#define rb_file_s_symlink rb_f_notimplement
3305#endif
3306
3307#ifdef HAVE_READLINK
3308/*
3309 * call-seq:
3310 * File.readlink(link_name) -> file_name
3311 *
3312 * Returns the name of the file referenced by the given link.
3313 * Not available on all platforms.
3314 *
3315 * File.symlink("testfile", "link2test") #=> 0
3316 * File.readlink("link2test") #=> "testfile"
3317 */
3318
3319static VALUE
3320rb_file_s_readlink(VALUE klass, VALUE path)
3321{
3322 return rb_readlink(path, rb_filesystem_encoding());
3323}
3324
3325struct readlink_arg {
3326 const char *path;
3327 char *buf;
3328 size_t size;
3329};
3330
3331static void *
3332nogvl_readlink(void *ptr)
3333{
3334 struct readlink_arg *ra = ptr;
3335
3336 return (void *)(VALUE)readlink(ra->path, ra->buf, ra->size);
3337}
3338
3339static ssize_t
3340readlink_without_gvl(VALUE path, VALUE buf, size_t size)
3341{
3342 struct readlink_arg ra;
3343
3344 ra.path = RSTRING_PTR(path);
3345 ra.buf = RSTRING_PTR(buf);
3346 ra.size = size;
3347
3348 return (ssize_t)IO_WITHOUT_GVL(nogvl_readlink, &ra);
3349}
3350
3351VALUE
3352rb_readlink(VALUE path, rb_encoding *enc)
3353{
3354 int size = 100;
3355 ssize_t rv;
3356 VALUE v;
3357
3358 FilePathValue(path);
3359 path = rb_str_encode_ospath(path);
3360 v = rb_enc_str_new(0, size, enc);
3361 while ((rv = readlink_without_gvl(path, v, size)) == size
3362#ifdef _AIX
3363 || (rv < 0 && errno == ERANGE) /* quirky behavior of GPFS */
3364#endif
3365 ) {
3366 rb_str_modify_expand(v, size);
3367 size *= 2;
3368 rb_str_set_len(v, size);
3369 }
3370 if (rv < 0) {
3371 int e = errno;
3372 rb_str_resize(v, 0);
3373 rb_syserr_fail_path(e, path);
3374 }
3375 rb_str_resize(v, rv);
3376
3377 return v;
3378}
3379#else
3380#define rb_file_s_readlink rb_f_notimplement
3381#endif
3382
3383static int
3384unlink_internal(const char *path, void *arg)
3385{
3386 return unlink(path);
3387}
3388
3389/*
3390 * call-seq:
3391 * File.delete(file_name, ...) -> integer
3392 * File.unlink(file_name, ...) -> integer
3393 *
3394 * Deletes the named files, returning the number of names
3395 * passed as arguments. Raises an exception on any error.
3396 * Since the underlying implementation relies on the
3397 * <code>unlink(2)</code> system call, the type of
3398 * exception raised depends on its error type (see
3399 * https://linux.die.net/man/2/unlink) and has the form of
3400 * e.g. Errno::ENOENT.
3401 *
3402 * See also Dir::rmdir.
3403 */
3404
3405static VALUE
3406rb_file_s_unlink(int argc, VALUE *argv, VALUE klass)
3407{
3408 return apply2files(unlink_internal, argc, argv, 0);
3409}
3410
3412 const char *src;
3413 const char *dst;
3414};
3415
3416static void *
3417no_gvl_rename(void *ptr)
3418{
3419 struct rename_args *ra = ptr;
3420
3421 return (void *)(VALUE)rename(ra->src, ra->dst);
3422}
3423
3424/*
3425 * call-seq:
3426 * File.rename(old_name, new_name) -> 0
3427 *
3428 * Renames the given file to the new name. Raises a SystemCallError
3429 * if the file cannot be renamed.
3430 *
3431 * File.rename("afile", "afile.bak") #=> 0
3432 */
3433
3434static VALUE
3435rb_file_s_rename(VALUE klass, VALUE from, VALUE to)
3436{
3437 struct rename_args ra;
3438 VALUE f, t;
3439
3440 FilePathValue(from);
3441 FilePathValue(to);
3442 f = rb_str_encode_ospath(from);
3443 t = rb_str_encode_ospath(to);
3444 ra.src = StringValueCStr(f);
3445 ra.dst = StringValueCStr(t);
3446#if defined __CYGWIN__
3447 errno = 0;
3448#endif
3449 if (IO_WITHOUT_GVL_INT(no_gvl_rename, &ra) < 0) {
3450 int e = errno;
3451#if defined DOSISH
3452 switch (e) {
3453 case EEXIST:
3454 if (chmod(ra.dst, 0666) == 0 &&
3455 unlink(ra.dst) == 0 &&
3456 rename(ra.src, ra.dst) == 0)
3457 return INT2FIX(0);
3458 }
3459#endif
3460 syserr_fail2(e, from, to);
3461 }
3462
3463 return INT2FIX(0);
3464}
3465
3466/*
3467 * call-seq:
3468 * File.umask() -> integer
3469 * File.umask(integer) -> integer
3470 *
3471 * Returns the current umask value for this process. If the optional
3472 * argument is given, set the umask to that value and return the
3473 * previous value. Umask values are <em>subtracted</em> from the
3474 * default permissions, so a umask of <code>0222</code> would make a
3475 * file read-only for everyone.
3476 *
3477 * File.umask(0006) #=> 18
3478 * File.umask #=> 6
3479 */
3480
3481static VALUE
3482rb_file_s_umask(int argc, VALUE *argv, VALUE _)
3483{
3484 mode_t omask = 0;
3485
3486 switch (argc) {
3487 case 0:
3488 omask = umask(0);
3489 umask(omask);
3490 break;
3491 case 1:
3492 omask = umask(NUM2MODET(argv[0]));
3493 break;
3494 default:
3495 rb_error_arity(argc, 0, 1);
3496 }
3497 return MODET2NUM(omask);
3498}
3499
3500#ifdef __CYGWIN__
3501#undef DOSISH
3502#endif
3503#if defined __CYGWIN__ || defined DOSISH
3504#define DOSISH_UNC
3505#define DOSISH_DRIVE_LETTER
3506#define FILE_ALT_SEPARATOR '\\'
3507#endif
3508#ifdef FILE_ALT_SEPARATOR
3509#define isdirsep(x) ((x) == '/' || (x) == FILE_ALT_SEPARATOR)
3510# ifdef DOSISH
3511static const char file_alt_separator[] = {FILE_ALT_SEPARATOR, '\0'};
3512# endif
3513#else
3514#define isdirsep(x) ((x) == '/')
3515#endif
3516
3517#ifndef USE_NTFS
3518# if defined _WIN32
3519# define USE_NTFS 1
3520# else
3521# define USE_NTFS 0
3522# endif
3523#endif
3524
3525#ifndef USE_NTFS_ADS
3526# if USE_NTFS
3527# define USE_NTFS_ADS 1
3528# else
3529# define USE_NTFS_ADS 0
3530# endif
3531#endif
3532
3533#if USE_NTFS
3534#define istrailinggarbage(x) ((x) == '.' || (x) == ' ')
3535#else
3536#define istrailinggarbage(x) 0
3537#endif
3538
3539#if USE_NTFS_ADS
3540# define isADS(x) ((x) == ':')
3541#else
3542# define isADS(x) 0
3543#endif
3544
3545#define Next(p, e, enc) ((p) + rb_enc_mbclen((p), (e), (enc)))
3546#define Inc(p, e, enc) ((p) = Next((p), (e), (enc)))
3547
3548#if defined(DOSISH_UNC)
3549#define has_unc(buf) (isdirsep((buf)[0]) && isdirsep((buf)[1]))
3550#else
3551#define has_unc(buf) 0
3552#endif
3553
3554#ifdef DOSISH_DRIVE_LETTER
3555static inline int
3556has_drive_letter(const char *buf)
3557{
3558 if (ISALPHA(buf[0]) && buf[1] == ':') {
3559 return 1;
3560 }
3561 else {
3562 return 0;
3563 }
3564}
3565
3566#ifndef _WIN32
3567static char*
3568getcwdofdrv(int drv)
3569{
3570 char drive[4];
3571 char *drvcwd, *oldcwd;
3572
3573 drive[0] = drv;
3574 drive[1] = ':';
3575 drive[2] = '\0';
3576
3577 /* the only way that I know to get the current directory
3578 of a particular drive is to change chdir() to that drive,
3579 so save the old cwd before chdir()
3580 */
3581 oldcwd = ruby_getcwd();
3582 if (chdir(drive) == 0) {
3583 drvcwd = ruby_getcwd();
3584 chdir(oldcwd);
3585 xfree(oldcwd);
3586 }
3587 else {
3588 /* perhaps the drive is not exist. we return only drive letter */
3589 drvcwd = strdup(drive);
3590 }
3591 return drvcwd;
3592}
3593
3594static inline int
3595not_same_drive(VALUE path, int drive)
3596{
3597 const char *p = RSTRING_PTR(path);
3598 if (RSTRING_LEN(path) < 2) return 0;
3599 if (has_drive_letter(p)) {
3600 return TOLOWER(p[0]) != TOLOWER(drive);
3601 }
3602 else {
3603 return has_unc(p);
3604 }
3605}
3606#endif /* _WIN32 */
3607#endif /* DOSISH_DRIVE_LETTER */
3608
3609static inline char *
3610skiproot(const char *path, const char *end, rb_encoding *enc)
3611{
3612#ifdef DOSISH_DRIVE_LETTER
3613 if (path + 2 <= end && has_drive_letter(path)) path += 2;
3614#endif
3615 while (path < end && isdirsep(*path)) path++;
3616 return (char *)path;
3617}
3618
3619#define nextdirsep rb_enc_path_next
3620char *
3621rb_enc_path_next(const char *s, const char *e, rb_encoding *enc)
3622{
3623 while (s < e && !isdirsep(*s)) {
3624 Inc(s, e, enc);
3625 }
3626 return (char *)s;
3627}
3628
3629#if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
3630#define skipprefix rb_enc_path_skip_prefix
3631#else
3632#define skipprefix(path, end, enc) (path)
3633#endif
3634char *
3635rb_enc_path_skip_prefix(const char *path, const char *end, rb_encoding *enc)
3636{
3637#if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
3638#ifdef DOSISH_UNC
3639 if (path + 2 <= end && isdirsep(path[0]) && isdirsep(path[1])) {
3640 path += 2;
3641 while (path < end && isdirsep(*path)) path++;
3642 if ((path = rb_enc_path_next(path, end, enc)) < end && path[0] && path[1] && !isdirsep(path[1]))
3643 path = rb_enc_path_next(path + 1, end, enc);
3644 return (char *)path;
3645 }
3646#endif
3647#ifdef DOSISH_DRIVE_LETTER
3648 if (has_drive_letter(path))
3649 return (char *)(path + 2);
3650#endif
3651#endif /* defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER) */
3652 return (char *)path;
3653}
3654
3655static inline char *
3656skipprefixroot(const char *path, const char *end, rb_encoding *enc)
3657{
3658#if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
3659 char *p = skipprefix(path, end, enc);
3660 while (isdirsep(*p)) p++;
3661 return p;
3662#else
3663 return skiproot(path, end, enc);
3664#endif
3665}
3666
3667#define strrdirsep rb_enc_path_last_separator
3668char *
3669rb_enc_path_last_separator(const char *path, const char *end, rb_encoding *enc)
3670{
3671 char *last = NULL;
3672 while (path < end) {
3673 if (isdirsep(*path)) {
3674 const char *tmp = path++;
3675 while (path < end && isdirsep(*path)) path++;
3676 if (path >= end) break;
3677 last = (char *)tmp;
3678 }
3679 else {
3680 Inc(path, end, enc);
3681 }
3682 }
3683 return last;
3684}
3685
3686static char *
3687chompdirsep(const char *path, const char *end, rb_encoding *enc)
3688{
3689 while (path < end) {
3690 if (isdirsep(*path)) {
3691 const char *last = path++;
3692 while (path < end && isdirsep(*path)) path++;
3693 if (path >= end) return (char *)last;
3694 }
3695 else {
3696 Inc(path, end, enc);
3697 }
3698 }
3699 return (char *)path;
3700}
3701
3702char *
3703rb_enc_path_end(const char *path, const char *end, rb_encoding *enc)
3704{
3705 if (path < end && isdirsep(*path)) path++;
3706 return chompdirsep(path, end, enc);
3707}
3708
3709static rb_encoding *
3710fs_enc_check(VALUE path1, VALUE path2)
3711{
3712 rb_encoding *enc = rb_enc_check(path1, path2);
3713 int encidx = rb_enc_to_index(enc);
3714 if (encidx == ENCINDEX_US_ASCII) {
3715 encidx = rb_enc_get_index(path1);
3716 if (encidx == ENCINDEX_US_ASCII)
3717 encidx = rb_enc_get_index(path2);
3718 enc = rb_enc_from_index(encidx);
3719 }
3720 return enc;
3721}
3722
3723#if USE_NTFS
3724static char *
3725ntfs_tail(const char *path, const char *end, rb_encoding *enc)
3726{
3727 while (path < end && *path == '.') path++;
3728 while (path < end && !isADS(*path)) {
3729 if (istrailinggarbage(*path)) {
3730 const char *last = path++;
3731 while (path < end && istrailinggarbage(*path)) path++;
3732 if (path >= end || isADS(*path)) return (char *)last;
3733 }
3734 else if (isdirsep(*path)) {
3735 const char *last = path++;
3736 while (path < end && isdirsep(*path)) path++;
3737 if (path >= end) return (char *)last;
3738 if (isADS(*path)) path++;
3739 }
3740 else {
3741 Inc(path, end, enc);
3742 }
3743 }
3744 return (char *)path;
3745}
3746#endif /* USE_NTFS */
3747
3748#define BUFCHECK(cond) do {\
3749 bdiff = p - buf;\
3750 if (cond) {\
3751 do {buflen *= 2;} while (cond);\
3752 rb_str_resize(result, buflen);\
3753 buf = RSTRING_PTR(result);\
3754 p = buf + bdiff;\
3755 pend = buf + buflen;\
3756 }\
3757} while (0)
3758
3759#define BUFINIT() (\
3760 p = buf = RSTRING_PTR(result),\
3761 buflen = RSTRING_LEN(result),\
3762 pend = p + buflen)
3763
3764#ifdef __APPLE__
3765# define SKIPPATHSEP(p) ((*(p)) ? 1 : 0)
3766#else
3767# define SKIPPATHSEP(p) 1
3768#endif
3769
3770#define BUFCOPY(srcptr, srclen) do { \
3771 const int skip = SKIPPATHSEP(p); \
3772 rb_str_set_len(result, p-buf+skip); \
3773 BUFCHECK(bdiff + ((srclen)+skip) >= buflen); \
3774 p += skip; \
3775 memcpy(p, (srcptr), (srclen)); \
3776 p += (srclen); \
3777} while (0)
3778
3779#define WITH_ROOTDIFF(stmt) do { \
3780 long rootdiff = root - buf; \
3781 stmt; \
3782 root = buf + rootdiff; \
3783} while (0)
3784
3785static VALUE
3786copy_home_path(VALUE result, const char *dir)
3787{
3788 char *buf;
3789#if defined DOSISH || defined __CYGWIN__
3790 char *p, *bend;
3791 rb_encoding *enc;
3792#endif
3793 long dirlen;
3794 int encidx;
3795
3796 dirlen = strlen(dir);
3797 rb_str_resize(result, dirlen);
3798 memcpy(buf = RSTRING_PTR(result), dir, dirlen);
3799 encidx = rb_filesystem_encindex();
3800 rb_enc_associate_index(result, encidx);
3801#if defined DOSISH || defined __CYGWIN__
3802 enc = rb_enc_from_index(encidx);
3803 for (bend = (p = buf) + dirlen; p < bend; Inc(p, bend, enc)) {
3804 if (*p == '\\') {
3805 *p = '/';
3806 }
3807 }
3808#endif
3809 return result;
3810}
3811
3812VALUE
3813rb_home_dir_of(VALUE user, VALUE result)
3814{
3815#ifdef HAVE_PWD_H
3816 VALUE dirname = rb_getpwdirnam_for_login(user);
3817 if (dirname == Qnil) {
3818 rb_raise(rb_eArgError, "user %"PRIsVALUE" doesn't exist", user);
3819 }
3820 const char *dir = RSTRING_PTR(dirname);
3821#else
3822 extern char *getlogin(void);
3823 const char *pwPtr = 0;
3824 const char *login;
3825 # define endpwent() ((void)0)
3826 const char *dir, *username = RSTRING_PTR(user);
3827 rb_encoding *enc = rb_enc_get(user);
3828#if defined _WIN32
3829 rb_encoding *fsenc = rb_utf8_encoding();
3830#else
3831 rb_encoding *fsenc = rb_filesystem_encoding();
3832#endif
3833 if (enc != fsenc) {
3834 dir = username = RSTRING_PTR(rb_str_conv_enc(user, enc, fsenc));
3835 }
3836
3837 if ((login = getlogin()) && strcasecmp(username, login) == 0)
3838 dir = pwPtr = getenv("HOME");
3839 if (!pwPtr) {
3840 rb_raise(rb_eArgError, "user %"PRIsVALUE" doesn't exist", user);
3841 }
3842#endif
3843 copy_home_path(result, dir);
3844 return result;
3845}
3846
3847#ifndef _WIN32 /* this encompasses rb_file_expand_path_internal */
3848VALUE
3849rb_default_home_dir(VALUE result)
3850{
3851 const char *dir = getenv("HOME");
3852
3853#if defined HAVE_PWD_H
3854 if (!dir) {
3855 /* We'll look up the user's default home dir in the password db by
3856 * login name, if possible, and failing that will fall back to looking
3857 * the information up by uid (as would be needed for processes that
3858 * are not a descendant of login(1) or a work-alike).
3859 *
3860 * While the lookup by uid is more likely to succeed (since we always
3861 * have a uid, but may or may not have a login name), we prefer first
3862 * looking up by name to accommodate the possibility of multiple login
3863 * names (each with its own record in the password database, so each
3864 * with a potentially different home directory) being mapped to the
3865 * same uid (as explicitly allowed for by POSIX; see getlogin(3posix)).
3866 */
3867 VALUE login_name = rb_getlogin();
3868
3869# if !defined(HAVE_GETPWUID_R) && !defined(HAVE_GETPWUID)
3870 /* This is a corner case, but for backward compatibility reasons we
3871 * want to emit this error if neither the lookup by login name nor
3872 * lookup by getuid() has a chance of succeeding.
3873 */
3874 if (NIL_P(login_name)) {
3875 rb_raise(rb_eArgError, "couldn't find login name -- expanding '~'");
3876 }
3877# endif /* !defined(HAVE_GETPWUID_R) && !defined(HAVE_GETPWUID) */
3878
3879 VALUE pw_dir = rb_getpwdirnam_for_login(login_name);
3880 if (NIL_P(pw_dir)) {
3881 pw_dir = rb_getpwdiruid();
3882 if (NIL_P(pw_dir)) {
3883 rb_raise(rb_eArgError, "couldn't find home for uid '%ld'", (long)getuid());
3884 }
3885 }
3886
3887 /* found it */
3888 copy_home_path(result, RSTRING_PTR(pw_dir));
3889 rb_str_resize(pw_dir, 0);
3890 return result;
3891 }
3892#endif /* defined HAVE_PWD_H */
3893 if (!dir) {
3894 rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding '~'");
3895 }
3896 return copy_home_path(result, dir);
3897}
3898
3899static VALUE
3900ospath_new(const char *ptr, long len, rb_encoding *fsenc)
3901{
3902#if NORMALIZE_UTF8PATH
3903 VALUE path = rb_str_normalize_ospath(ptr, len);
3904 rb_enc_associate(path, fsenc);
3905 return path;
3906#else
3907 return rb_enc_str_new(ptr, len, fsenc);
3908#endif
3909}
3910
3911static char *
3912append_fspath(VALUE result, VALUE fname, char *dir, rb_encoding **enc, rb_encoding *fsenc)
3913{
3914 char *buf, *cwdp = dir;
3915 VALUE dirname = Qnil;
3916 size_t dirlen = strlen(dir), buflen = rb_str_capacity(result);
3917
3918 if (NORMALIZE_UTF8PATH || *enc != fsenc) {
3919 dirname = ospath_new(dir, dirlen, fsenc);
3920 if (!rb_enc_compatible(fname, dirname)) {
3921 xfree(dir);
3922 /* rb_enc_check must raise because the two encodings are not
3923 * compatible. */
3924 rb_enc_check(fname, dirname);
3925 rb_bug("unreachable");
3926 }
3927 rb_encoding *direnc = fs_enc_check(fname, dirname);
3928 if (direnc != fsenc) {
3929 dirname = rb_str_conv_enc(dirname, fsenc, direnc);
3930 RSTRING_GETMEM(dirname, cwdp, dirlen);
3931 }
3932 else if (NORMALIZE_UTF8PATH) {
3933 RSTRING_GETMEM(dirname, cwdp, dirlen);
3934 }
3935 *enc = direnc;
3936 }
3937 do {buflen *= 2;} while (dirlen > buflen);
3938 rb_str_resize(result, buflen);
3939 buf = RSTRING_PTR(result);
3940 memcpy(buf, cwdp, dirlen);
3941 xfree(dir);
3942 if (!NIL_P(dirname)) rb_str_resize(dirname, 0);
3943 rb_enc_associate(result, *enc);
3944 return buf + dirlen;
3945}
3946
3947VALUE
3948rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_name, VALUE result)
3949{
3950 const char *s, *b, *fend;
3951 char *buf, *p, *pend, *root;
3952 size_t buflen, bdiff;
3953 rb_encoding *enc, *fsenc = rb_filesystem_encoding();
3954
3955 s = StringValuePtr(fname);
3956 fend = s + RSTRING_LEN(fname);
3957 enc = rb_enc_get(fname);
3958 BUFINIT();
3959
3960 if (s[0] == '~' && abs_mode == 0) { /* execute only if NOT absolute_path() */
3961 long userlen = 0;
3962 if (isdirsep(s[1]) || s[1] == '\0') {
3963 buf = 0;
3964 b = 0;
3965 rb_str_set_len(result, 0);
3966 if (*++s) ++s;
3967 rb_default_home_dir(result);
3968 }
3969 else {
3970 s = nextdirsep(b = s, fend, enc);
3971 b++; /* b[0] is '~' */
3972 userlen = s - b;
3973 BUFCHECK(bdiff + userlen >= buflen);
3974 memcpy(p, b, userlen);
3975 ENC_CODERANGE_CLEAR(result);
3976 rb_str_set_len(result, userlen);
3977 rb_enc_associate(result, enc);
3978 rb_home_dir_of(result, result);
3979 buf = p + 1;
3980 p += userlen;
3981 }
3982 if (!rb_is_absolute_path(RSTRING_PTR(result))) {
3983 if (userlen) {
3984 rb_enc_raise(enc, rb_eArgError, "non-absolute home of %.*s%.0"PRIsVALUE,
3985 (int)userlen, b, fname);
3986 }
3987 else {
3988 rb_raise(rb_eArgError, "non-absolute home");
3989 }
3990 }
3991 BUFINIT();
3992 p = pend;
3993 }
3994#ifdef DOSISH_DRIVE_LETTER
3995 /* skip drive letter */
3996 else if (has_drive_letter(s)) {
3997 if (isdirsep(s[2])) {
3998 /* specified drive letter, and full path */
3999 /* skip drive letter */
4000 BUFCHECK(bdiff + 2 >= buflen);
4001 memcpy(p, s, 2);
4002 p += 2;
4003 s += 2;
4004 rb_enc_copy(result, fname);
4005 }
4006 else {
4007 /* specified drive, but not full path */
4008 int same = 0;
4009 if (!NIL_P(dname) && !not_same_drive(dname, s[0])) {
4010 rb_file_expand_path_internal(dname, Qnil, abs_mode, long_name, result);
4011 BUFINIT();
4012 if (has_drive_letter(p) && TOLOWER(p[0]) == TOLOWER(s[0])) {
4013 /* ok, same drive */
4014 same = 1;
4015 }
4016 }
4017 if (!same) {
4018 char *e = append_fspath(result, fname, getcwdofdrv(*s), &enc, fsenc);
4019 BUFINIT();
4020 p = e;
4021 }
4022 else {
4023 rb_enc_associate(result, enc = fs_enc_check(result, fname));
4024 p = pend;
4025 }
4026 p = chompdirsep(skiproot(buf, p, enc), p, enc);
4027 s += 2;
4028 }
4029 }
4030#endif /* DOSISH_DRIVE_LETTER */
4031 else if (!rb_is_absolute_path(s)) {
4032 if (!NIL_P(dname)) {
4033 rb_file_expand_path_internal(dname, Qnil, abs_mode, long_name, result);
4034 rb_enc_associate(result, fs_enc_check(result, fname));
4035 BUFINIT();
4036 p = pend;
4037 }
4038 else {
4039 char *e = append_fspath(result, fname, ruby_getcwd(), &enc, fsenc);
4040 BUFINIT();
4041 p = e;
4042 }
4043#if defined DOSISH || defined __CYGWIN__
4044 if (isdirsep(*s)) {
4045 /* specified full path, but not drive letter nor UNC */
4046 /* we need to get the drive letter or UNC share name */
4047 p = skipprefix(buf, p, enc);
4048 }
4049 else
4050#endif /* defined DOSISH || defined __CYGWIN__ */
4051 p = chompdirsep(skiproot(buf, p, enc), p, enc);
4052 }
4053 else {
4054 size_t len;
4055 b = s;
4056 do s++; while (isdirsep(*s));
4057 len = s - b;
4058 p = buf + len;
4059 BUFCHECK(bdiff >= buflen);
4060 memset(buf, '/', len);
4061 rb_str_set_len(result, len);
4062 rb_enc_associate(result, fs_enc_check(result, fname));
4063 }
4064 if (p > buf && p[-1] == '/')
4065 --p;
4066 else {
4067 rb_str_set_len(result, p-buf);
4068 BUFCHECK(bdiff + 1 >= buflen);
4069 *p = '/';
4070 }
4071
4072 rb_str_set_len(result, p-buf+1);
4073 BUFCHECK(bdiff + 1 >= buflen);
4074 p[1] = 0;
4075 root = skipprefix(buf, p+1, enc);
4076
4077 b = s;
4078 while (*s) {
4079 switch (*s) {
4080 case '.':
4081 if (b == s++) { /* beginning of path element */
4082 switch (*s) {
4083 case '\0':
4084 b = s;
4085 break;
4086 case '.':
4087 if (*(s+1) == '\0' || isdirsep(*(s+1))) {
4088 /* We must go back to the parent */
4089 char *n;
4090 *p = '\0';
4091 if (!(n = strrdirsep(root, p, enc))) {
4092 *p = '/';
4093 }
4094 else {
4095 p = n;
4096 }
4097 b = ++s;
4098 }
4099#if USE_NTFS
4100 else {
4101 do ++s; while (istrailinggarbage(*s));
4102 }
4103#endif /* USE_NTFS */
4104 break;
4105 case '/':
4106#if defined DOSISH || defined __CYGWIN__
4107 case '\\':
4108#endif
4109 b = ++s;
4110 break;
4111 default:
4112 /* ordinary path element, beginning don't move */
4113 break;
4114 }
4115 }
4116#if USE_NTFS
4117 else {
4118 --s;
4119 case ' ': {
4120 const char *e = s;
4121 while (s < fend && istrailinggarbage(*s)) s++;
4122 if (s >= fend) {
4123 s = e;
4124 goto endpath;
4125 }
4126 }
4127 }
4128#endif /* USE_NTFS */
4129 break;
4130 case '/':
4131#if defined DOSISH || defined __CYGWIN__
4132 case '\\':
4133#endif
4134 if (s > b) {
4135 WITH_ROOTDIFF(BUFCOPY(b, s-b));
4136 *p = '/';
4137 }
4138 b = ++s;
4139 break;
4140 default:
4141#ifdef __APPLE__
4142 {
4143 int n = ignored_char_p(s, fend, enc);
4144 if (n) {
4145 if (s > b) {
4146 WITH_ROOTDIFF(BUFCOPY(b, s-b));
4147 *p = '\0';
4148 }
4149 b = s += n;
4150 break;
4151 }
4152 }
4153#endif /* __APPLE__ */
4154 Inc(s, fend, enc);
4155 break;
4156 }
4157 }
4158
4159 if (s > b) {
4160#if USE_NTFS
4161# if USE_NTFS_ADS
4162 static const char prime[] = ":$DATA";
4163 enum {prime_len = sizeof(prime) -1};
4164# endif
4165 endpath:
4166# if USE_NTFS_ADS
4167 if (s > b + prime_len && strncasecmp(s - prime_len, prime, prime_len) == 0) {
4168 /* alias of stream */
4169 /* get rid of a bug of x64 VC++ */
4170 if (isADS(*(s - (prime_len+1)))) {
4171 s -= prime_len + 1; /* prime */
4172 }
4173 else if (memchr(b, ':', s - prime_len - b)) {
4174 s -= prime_len; /* alternative */
4175 }
4176 }
4177# endif /* USE_NTFS_ADS */
4178#endif /* USE_NTFS */
4179 BUFCOPY(b, s-b);
4180 rb_str_set_len(result, p-buf);
4181 }
4182 if (p == skiproot(buf, p + !!*p, enc) - 1) p++;
4183
4184#if USE_NTFS
4185 *p = '\0';
4186 if ((s = strrdirsep(b = buf, p, enc)) != 0 && !strpbrk(s, "*?")) {
4187 VALUE tmp, v;
4188 size_t len;
4189 int encidx;
4190 WCHAR *wstr;
4191 WIN32_FIND_DATAW wfd;
4192 HANDLE h;
4193#ifdef __CYGWIN__
4194#ifdef HAVE_CYGWIN_CONV_PATH
4195 char *w32buf = NULL;
4196 const int flags = CCP_POSIX_TO_WIN_A | CCP_RELATIVE;
4197#else
4198 char w32buf[MAXPATHLEN];
4199#endif /* HAVE_CYGWIN_CONV_PATH */
4200 const char *path;
4201 ssize_t bufsize;
4202 int lnk_added = 0, is_symlink = 0;
4203 struct stat st;
4204 p = (char *)s;
4205 len = strlen(p);
4206 if (lstat_without_gvl(buf, &st) == 0 && S_ISLNK(st.st_mode)) {
4207 is_symlink = 1;
4208 if (len > 4 && STRCASECMP(p + len - 4, ".lnk") != 0) {
4209 lnk_added = 1;
4210 }
4211 }
4212 path = *buf ? buf : "/";
4213#ifdef HAVE_CYGWIN_CONV_PATH
4214 bufsize = cygwin_conv_path(flags, path, NULL, 0);
4215 if (bufsize > 0) {
4216 bufsize += len;
4217 if (lnk_added) bufsize += 4;
4218 w32buf = ALLOCA_N(char, bufsize);
4219 if (cygwin_conv_path(flags, path, w32buf, bufsize) == 0) {
4220 b = w32buf;
4221 }
4222 }
4223#else /* !HAVE_CYGWIN_CONV_PATH */
4224 bufsize = MAXPATHLEN;
4225 if (cygwin_conv_to_win32_path(path, w32buf) == 0) {
4226 b = w32buf;
4227 }
4228#endif /* !HAVE_CYGWIN_CONV_PATH */
4229 if (is_symlink && b == w32buf) {
4230 *p = '\\';
4231 strlcat(w32buf, p, bufsize);
4232 if (lnk_added) {
4233 strlcat(w32buf, ".lnk", bufsize);
4234 }
4235 }
4236 else {
4237 lnk_added = 0;
4238 }
4239 *p = '/';
4240#endif /* __CYGWIN__ */
4241 rb_str_set_len(result, p - buf + strlen(p));
4242 encidx = ENCODING_GET(result);
4243 tmp = result;
4244 if (encidx != ENCINDEX_UTF_8 && !is_ascii_string(result)) {
4245 tmp = rb_str_encode_ospath(result);
4246 }
4247 len = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, NULL, 0);
4248 wstr = ALLOCV_N(WCHAR, v, len);
4249 MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, wstr, len);
4250 if (tmp != result) rb_str_set_len(tmp, 0);
4251 h = FindFirstFileW(wstr, &wfd);
4252 ALLOCV_END(v);
4253 if (h != INVALID_HANDLE_VALUE) {
4254 size_t wlen;
4255 FindClose(h);
4256 len = lstrlenW(wfd.cFileName);
4257#ifdef __CYGWIN__
4258 if (lnk_added && len > 4 &&
4259 wcscasecmp(wfd.cFileName + len - 4, L".lnk") == 0) {
4260 wfd.cFileName[len -= 4] = L'\0';
4261 }
4262#else
4263 p = (char *)s;
4264#endif
4265 ++p;
4266 wlen = (int)len;
4267 len = WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, NULL, 0, NULL, NULL);
4268 if (tmp == result) {
4269 BUFCHECK(bdiff + len >= buflen);
4270 WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, p, len + 1, NULL, NULL);
4271 }
4272 else {
4274 WideCharToMultiByte(CP_UTF8, 0, wfd.cFileName, wlen, RSTRING_PTR(tmp), len + 1, NULL, NULL);
4275 rb_str_cat_conv_enc_opts(result, bdiff, RSTRING_PTR(tmp), len,
4276 rb_utf8_encoding(), 0, Qnil);
4277 BUFINIT();
4278 rb_str_resize(tmp, 0);
4279 }
4280 p += len;
4281 }
4282#ifdef __CYGWIN__
4283 else {
4284 p += strlen(p);
4285 }
4286#endif
4287 }
4288#endif /* USE_NTFS */
4289
4290 rb_str_set_len(result, p - buf);
4291 rb_enc_check(fname, result);
4292 ENC_CODERANGE_CLEAR(result);
4293 return result;
4294}
4295#endif /* !_WIN32 (this ifdef started above rb_default_home_dir) */
4296
4297#define EXPAND_PATH_BUFFER() rb_usascii_str_new(0, 1)
4298
4299static VALUE
4300str_shrink(VALUE str)
4301{
4302 rb_str_resize(str, RSTRING_LEN(str));
4303 return str;
4304}
4305
4306#define expand_path(fname, dname, abs_mode, long_name, result) \
4307 str_shrink(rb_file_expand_path_internal(fname, dname, abs_mode, long_name, result))
4308
4309#define check_expand_path_args(fname, dname) \
4310 (((fname) = rb_get_path(fname)), \
4311 (void)(NIL_P(dname) ? (dname) : ((dname) = rb_get_path(dname))))
4312
4313static VALUE
4314file_expand_path_1(VALUE fname)
4315{
4316 return rb_file_expand_path_internal(fname, Qnil, 0, 0, EXPAND_PATH_BUFFER());
4317}
4318
4319VALUE
4320rb_file_expand_path(VALUE fname, VALUE dname)
4321{
4322 check_expand_path_args(fname, dname);
4323 return expand_path(fname, dname, 0, 1, EXPAND_PATH_BUFFER());
4324}
4325
4326VALUE
4327rb_file_expand_path_fast(VALUE fname, VALUE dname)
4328{
4329 return expand_path(fname, dname, 0, 0, EXPAND_PATH_BUFFER());
4330}
4331
4332VALUE
4333rb_file_s_expand_path(int argc, const VALUE *argv)
4334{
4335 rb_check_arity(argc, 1, 2);
4336 return rb_file_expand_path(argv[0], argc > 1 ? argv[1] : Qnil);
4337}
4338
4339/*
4340 * call-seq:
4341 * File.expand_path(file_name [, dir_string] ) -> abs_file_name
4342 *
4343 * Converts a pathname to an absolute pathname. Relative paths are
4344 * referenced from the current working directory of the process unless
4345 * +dir_string+ is given, in which case it will be used as the
4346 * starting point. The given pathname may start with a
4347 * ``<code>~</code>'', which expands to the process owner's home
4348 * directory (the environment variable +HOME+ must be set
4349 * correctly). ``<code>~</code><i>user</i>'' expands to the named
4350 * user's home directory.
4351 *
4352 * File.expand_path("~oracle/bin") #=> "/home/oracle/bin"
4353 *
4354 * A simple example of using +dir_string+ is as follows.
4355 * File.expand_path("ruby", "/usr/bin") #=> "/usr/bin/ruby"
4356 *
4357 * A more complex example which also resolves parent directory is as follows.
4358 * Suppose we are in bin/mygem and want the absolute path of lib/mygem.rb.
4359 *
4360 * File.expand_path("../../lib/mygem.rb", __FILE__)
4361 * #=> ".../path/to/project/lib/mygem.rb"
4362 *
4363 * So first it resolves the parent of __FILE__, that is bin/, then go to the
4364 * parent, the root of the project and appends +lib/mygem.rb+.
4365 */
4366
4367static VALUE
4368s_expand_path(int c, const VALUE * v, VALUE _)
4369{
4370 return rb_file_s_expand_path(c, v);
4371}
4372
4373VALUE
4374rb_file_absolute_path(VALUE fname, VALUE dname)
4375{
4376 check_expand_path_args(fname, dname);
4377 return expand_path(fname, dname, 1, 1, EXPAND_PATH_BUFFER());
4378}
4379
4380VALUE
4381rb_file_s_absolute_path(int argc, const VALUE *argv)
4382{
4383 rb_check_arity(argc, 1, 2);
4384 return rb_file_absolute_path(argv[0], argc > 1 ? argv[1] : Qnil);
4385}
4386
4387/*
4388 * call-seq:
4389 * File.absolute_path(file_name [, dir_string] ) -> abs_file_name
4390 *
4391 * Converts a pathname to an absolute pathname. Relative paths are
4392 * referenced from the current working directory of the process unless
4393 * <i>dir_string</i> is given, in which case it will be used as the
4394 * starting point. If the given pathname starts with a ``<code>~</code>''
4395 * it is NOT expanded, it is treated as a normal directory name.
4396 *
4397 * File.absolute_path("~oracle/bin") #=> "<relative_path>/~oracle/bin"
4398 */
4399
4400static VALUE
4401s_absolute_path(int c, const VALUE * v, VALUE _)
4402{
4403 return rb_file_s_absolute_path(c, v);
4404}
4405
4406/*
4407 * call-seq:
4408 * File.absolute_path?(file_name) -> true or false
4409 *
4410 * Returns <code>true</code> if +file_name+ is an absolute path, and
4411 * <code>false</code> otherwise.
4412 *
4413 * File.absolute_path?("c:/foo") #=> false (on Linux), true (on Windows)
4414 */
4415
4416static VALUE
4417s_absolute_path_p(VALUE klass, VALUE fname)
4418{
4419 VALUE path = rb_get_path(fname);
4420
4421 if (!rb_is_absolute_path(RSTRING_PTR(path))) return Qfalse;
4422 return Qtrue;
4423}
4424
4425enum rb_realpath_mode {
4426 RB_REALPATH_CHECK,
4427 RB_REALPATH_DIR,
4428 RB_REALPATH_STRICT,
4429 RB_REALPATH_MODE_MAX
4430};
4431
4432static int
4433realpath_rec(long *prefixlenp, VALUE *resolvedp, const char *unresolved, VALUE fallback,
4434 VALUE loopcheck, enum rb_realpath_mode mode, int last)
4435{
4436 const char *pend = unresolved + strlen(unresolved);
4437 rb_encoding *enc = rb_enc_get(*resolvedp);
4438 ID resolving;
4439 CONST_ID(resolving, "resolving");
4440 while (unresolved < pend) {
4441 const char *testname = unresolved;
4442 const char *unresolved_firstsep = rb_enc_path_next(unresolved, pend, enc);
4443 long testnamelen = unresolved_firstsep - unresolved;
4444 const char *unresolved_nextname = unresolved_firstsep;
4445 while (unresolved_nextname < pend && isdirsep(*unresolved_nextname))
4446 unresolved_nextname++;
4447 unresolved = unresolved_nextname;
4448 if (testnamelen == 1 && testname[0] == '.') {
4449 }
4450 else if (testnamelen == 2 && testname[0] == '.' && testname[1] == '.') {
4451 if (*prefixlenp < RSTRING_LEN(*resolvedp)) {
4452 const char *resolved_str = RSTRING_PTR(*resolvedp);
4453 const char *resolved_names = resolved_str + *prefixlenp;
4454 const char *lastsep = strrdirsep(resolved_names, resolved_str + RSTRING_LEN(*resolvedp), enc);
4455 long len = lastsep ? lastsep - resolved_names : 0;
4456 rb_str_resize(*resolvedp, *prefixlenp + len);
4457 }
4458 }
4459 else {
4460 VALUE checkval;
4461 VALUE testpath = rb_str_dup(*resolvedp);
4462 if (*prefixlenp < RSTRING_LEN(testpath))
4463 rb_str_cat2(testpath, "/");
4464#if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
4465 if (*prefixlenp > 1 && *prefixlenp == RSTRING_LEN(testpath)) {
4466 const char *prefix = RSTRING_PTR(testpath);
4467 const char *last = rb_enc_left_char_head(prefix, prefix + *prefixlenp - 1, prefix + *prefixlenp, enc);
4468 if (!isdirsep(*last)) rb_str_cat2(testpath, "/");
4469 }
4470#endif
4471 rb_str_cat(testpath, testname, testnamelen);
4472 checkval = rb_hash_aref(loopcheck, testpath);
4473 if (!NIL_P(checkval)) {
4474 if (checkval == ID2SYM(resolving)) {
4475 if (mode == RB_REALPATH_CHECK) {
4476 errno = ELOOP;
4477 return -1;
4478 }
4479 rb_syserr_fail_path(ELOOP, testpath);
4480 }
4481 else {
4482 *resolvedp = rb_str_dup(checkval);
4483 }
4484 }
4485 else {
4486 struct stat sbuf;
4487 int ret;
4488 ret = lstat_without_gvl(RSTRING_PTR(testpath), &sbuf);
4489 if (ret == -1) {
4490 int e = errno;
4491 if (e == ENOENT && !NIL_P(fallback)) {
4492 if (stat_without_gvl(RSTRING_PTR(fallback), &sbuf) == 0) {
4493 rb_str_replace(*resolvedp, fallback);
4494 return 0;
4495 }
4496 }
4497 if (mode == RB_REALPATH_CHECK) return -1;
4498 if (e == ENOENT) {
4499 if (mode == RB_REALPATH_STRICT || !last || *unresolved_firstsep)
4500 rb_syserr_fail_path(e, testpath);
4501 *resolvedp = testpath;
4502 break;
4503 }
4504 else {
4505 rb_syserr_fail_path(e, testpath);
4506 }
4507 }
4508#ifdef HAVE_READLINK
4509 if (S_ISLNK(sbuf.st_mode)) {
4510 VALUE link;
4511 VALUE link_orig = Qnil;
4512 const char *link_prefix, *link_names;
4513 long link_prefixlen;
4514 rb_hash_aset(loopcheck, testpath, ID2SYM(resolving));
4515 link = rb_readlink(testpath, enc);
4516 link_prefix = RSTRING_PTR(link);
4517 link_names = skipprefixroot(link_prefix, link_prefix + RSTRING_LEN(link), rb_enc_get(link));
4518 link_prefixlen = link_names - link_prefix;
4519 if (link_prefixlen > 0) {
4520 rb_encoding *tmpenc, *linkenc = rb_enc_get(link);
4521 link_orig = link;
4522 link = rb_str_subseq(link, 0, link_prefixlen);
4523 tmpenc = fs_enc_check(*resolvedp, link);
4524 if (tmpenc != linkenc) link = rb_str_conv_enc(link, linkenc, tmpenc);
4525 *resolvedp = link;
4526 *prefixlenp = link_prefixlen;
4527 }
4528 if (realpath_rec(prefixlenp, resolvedp, link_names, testpath,
4529 loopcheck, mode, !*unresolved_firstsep))
4530 return -1;
4531 RB_GC_GUARD(link_orig);
4532 rb_hash_aset(loopcheck, testpath, rb_str_dup_frozen(*resolvedp));
4533 }
4534 else
4535#endif /* HAVE_READLINK */
4536 {
4537 VALUE s = rb_str_dup_frozen(testpath);
4538 rb_hash_aset(loopcheck, s, s);
4539 *resolvedp = testpath;
4540 }
4541 }
4542 }
4543 }
4544 return 0;
4545}
4546
4547static VALUE
4548rb_check_realpath_emulate(VALUE basedir, VALUE path, rb_encoding *origenc, enum rb_realpath_mode mode)
4549{
4550 long prefixlen;
4551 VALUE resolved;
4552 VALUE unresolved_path;
4553 VALUE loopcheck;
4554 VALUE curdir = Qnil;
4555
4556 rb_encoding *enc;
4557 char *path_names = NULL, *basedir_names = NULL, *curdir_names = NULL;
4558 char *ptr, *prefixptr = NULL, *pend;
4559 long len;
4560
4561 unresolved_path = rb_str_dup_frozen(path);
4562
4563 if (!NIL_P(basedir)) {
4564 FilePathValue(basedir);
4565 basedir = TO_OSPATH(rb_str_dup_frozen(basedir));
4566 }
4567
4568 enc = rb_enc_get(unresolved_path);
4569 unresolved_path = TO_OSPATH(unresolved_path);
4570 RSTRING_GETMEM(unresolved_path, ptr, len);
4571 path_names = skipprefixroot(ptr, ptr + len, rb_enc_get(unresolved_path));
4572 if (ptr != path_names) {
4573 resolved = rb_str_subseq(unresolved_path, 0, path_names - ptr);
4574 goto root_found;
4575 }
4576
4577 if (!NIL_P(basedir)) {
4578 RSTRING_GETMEM(basedir, ptr, len);
4579 basedir_names = skipprefixroot(ptr, ptr + len, rb_enc_get(basedir));
4580 if (ptr != basedir_names) {
4581 resolved = rb_str_subseq(basedir, 0, basedir_names - ptr);
4582 goto root_found;
4583 }
4584 }
4585
4586 curdir = rb_dir_getwd_ospath();
4587 RSTRING_GETMEM(curdir, ptr, len);
4588 curdir_names = skipprefixroot(ptr, ptr + len, rb_enc_get(curdir));
4589 resolved = rb_str_subseq(curdir, 0, curdir_names - ptr);
4590
4591 root_found:
4592 RSTRING_GETMEM(resolved, prefixptr, prefixlen);
4593 pend = prefixptr + prefixlen;
4594 ptr = chompdirsep(prefixptr, pend, enc);
4595 if (ptr < pend) {
4596 prefixlen = ++ptr - prefixptr;
4597 rb_str_set_len(resolved, prefixlen);
4598 }
4599#ifdef FILE_ALT_SEPARATOR
4600 while (prefixptr < ptr) {
4601 if (*prefixptr == FILE_ALT_SEPARATOR) {
4602 *prefixptr = '/';
4603 }
4604 Inc(prefixptr, pend, enc);
4605 }
4606#endif
4607
4608 switch (rb_enc_to_index(enc)) {
4609 case ENCINDEX_ASCII_8BIT:
4610 case ENCINDEX_US_ASCII:
4611 rb_enc_associate_index(resolved, rb_filesystem_encindex());
4612 }
4613
4614 loopcheck = rb_hash_new();
4615 if (curdir_names) {
4616 if (realpath_rec(&prefixlen, &resolved, curdir_names, Qnil, loopcheck, mode, 0))
4617 return Qnil;
4618 }
4619 if (basedir_names) {
4620 if (realpath_rec(&prefixlen, &resolved, basedir_names, Qnil, loopcheck, mode, 0))
4621 return Qnil;
4622 }
4623 if (realpath_rec(&prefixlen, &resolved, path_names, Qnil, loopcheck, mode, 1))
4624 return Qnil;
4625
4626 if (origenc && origenc != rb_enc_get(resolved)) {
4627 if (rb_enc_str_asciionly_p(resolved)) {
4628 rb_enc_associate(resolved, origenc);
4629 }
4630 else {
4631 resolved = rb_str_conv_enc(resolved, NULL, origenc);
4632 }
4633 }
4634
4635 RB_GC_GUARD(unresolved_path);
4636 RB_GC_GUARD(curdir);
4637 return resolved;
4638}
4639
4640static VALUE rb_file_join(VALUE ary);
4641
4642#ifndef HAVE_REALPATH
4643static VALUE
4644rb_check_realpath_emulate_try(VALUE arg)
4645{
4646 VALUE *args = (VALUE *)arg;
4647 return rb_check_realpath_emulate(args[0], args[1], (rb_encoding *)args[2], RB_REALPATH_CHECK);
4648}
4649
4650static VALUE
4651rb_check_realpath_emulate_rescue(VALUE arg, VALUE exc)
4652{
4653 return Qnil;
4654}
4655#elif !defined(NEEDS_REALPATH_BUFFER) && defined(__APPLE__) && \
4656 (!defined(MAC_OS_X_VERSION_10_6) || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6))
4657/* realpath() on OSX < 10.6 doesn't implement automatic allocation */
4658# include <sys/syslimits.h>
4659# define NEEDS_REALPATH_BUFFER 1
4660#endif /* HAVE_REALPATH */
4661
4662static VALUE
4663rb_check_realpath_internal(VALUE basedir, VALUE path, rb_encoding *origenc, enum rb_realpath_mode mode)
4664{
4665#ifdef HAVE_REALPATH
4666 VALUE unresolved_path;
4667 char *resolved_ptr = NULL;
4668 VALUE resolved;
4669# if defined(NEEDS_REALPATH_BUFFER) && NEEDS_REALPATH_BUFFER
4670 char resolved_buffer[PATH_MAX];
4671# else
4672 char *const resolved_buffer = NULL;
4673# endif
4674
4675 if (mode == RB_REALPATH_DIR) {
4676 return rb_check_realpath_emulate(basedir, path, origenc, mode);
4677 }
4678
4679 unresolved_path = rb_str_dup_frozen(path);
4680 if (*RSTRING_PTR(unresolved_path) != '/' && !NIL_P(basedir)) {
4681 unresolved_path = rb_file_join(rb_assoc_new(basedir, unresolved_path));
4682 }
4683 if (origenc) unresolved_path = TO_OSPATH(unresolved_path);
4684
4685 if ((resolved_ptr = realpath(RSTRING_PTR(unresolved_path), resolved_buffer)) == NULL) {
4686 /*
4687 wasi-libc 22 and later support realpath(3) but return ENOTSUP
4688 when the underlying host syscall returns it.
4689 glibc realpath(3) does not allow /path/to/file.rb/../other_file.rb,
4690 returning ENOTDIR in that case.
4691 glibc realpath(3) can also return ENOENT for paths that exist,
4692 such as /dev/fd/5.
4693 Fallback to the emulated approach in either of those cases. */
4694 if (errno == ENOTSUP ||
4695 errno == ENOTDIR ||
4696 (errno == ENOENT && rb_file_exist_p(0, unresolved_path))) {
4697 return rb_check_realpath_emulate(basedir, path, origenc, mode);
4698
4699 }
4700 if (mode == RB_REALPATH_CHECK) {
4701 return Qnil;
4702 }
4703 rb_sys_fail_path(unresolved_path);
4704 }
4705 resolved = ospath_new(resolved_ptr, strlen(resolved_ptr), rb_filesystem_encoding());
4706# if !(defined(NEEDS_REALPATH_BUFFER) && NEEDS_REALPATH_BUFFER)
4707 free(resolved_ptr);
4708# endif
4709
4710# if !defined(__linux__) && !defined(__APPLE__)
4711 /* As `resolved` is a String in the filesystem encoding, no
4712 * conversion is needed */
4713 struct stat st;
4714 if (stat_without_gvl(RSTRING_PTR(resolved), &st) < 0) {
4715 if (mode == RB_REALPATH_CHECK) {
4716 return Qnil;
4717 }
4718 rb_sys_fail_path(unresolved_path);
4719 }
4720# endif /* !defined(__linux__) && !defined(__APPLE__) */
4721
4722 if (origenc && origenc != rb_enc_get(resolved)) {
4723 if (!rb_enc_str_asciionly_p(resolved)) {
4724 resolved = rb_str_conv_enc(resolved, NULL, origenc);
4725 }
4726 rb_enc_associate(resolved, origenc);
4727 }
4728
4729 if (is_broken_string(resolved)) {
4730 rb_enc_associate(resolved, rb_filesystem_encoding());
4731 if (is_broken_string(resolved)) {
4732 rb_enc_associate(resolved, rb_ascii8bit_encoding());
4733 }
4734 }
4735
4736 RB_GC_GUARD(unresolved_path);
4737 return resolved;
4738#else /* !HAVE_REALPATH */
4739 if (mode == RB_REALPATH_CHECK) {
4740 VALUE arg[3];
4741 arg[0] = basedir;
4742 arg[1] = path;
4743 arg[2] = (VALUE)origenc;
4744
4745 return rb_rescue(rb_check_realpath_emulate_try, (VALUE)arg,
4746 rb_check_realpath_emulate_rescue, Qnil);
4747 }
4748 else {
4749 return rb_check_realpath_emulate(basedir, path, origenc, mode);
4750 }
4751#endif /* HAVE_REALPATH */
4752}
4753
4754VALUE
4755rb_realpath_internal(VALUE basedir, VALUE path, int strict)
4756{
4757 const enum rb_realpath_mode mode =
4758 strict ? RB_REALPATH_STRICT : RB_REALPATH_DIR;
4759 return rb_check_realpath_internal(basedir, path, rb_enc_get(path), mode);
4760}
4761
4762VALUE
4763rb_check_realpath(VALUE basedir, VALUE path, rb_encoding *enc)
4764{
4765 return rb_check_realpath_internal(basedir, path, enc, RB_REALPATH_CHECK);
4766}
4767
4768/*
4769 * call-seq:
4770 * File.realpath(pathname [, dir_string]) -> real_pathname
4771 *
4772 * Returns the real (absolute) pathname of _pathname_ in the actual
4773 * filesystem not containing symlinks or useless dots.
4774 *
4775 * If _dir_string_ is given, it is used as a base directory
4776 * for interpreting relative pathname instead of the current directory.
4777 *
4778 * All components of the pathname must exist when this method is
4779 * called.
4780 */
4781static VALUE
4782rb_file_s_realpath(int argc, VALUE *argv, VALUE klass)
4783{
4784 VALUE basedir = (rb_check_arity(argc, 1, 2) > 1) ? argv[1] : Qnil;
4785 VALUE path = argv[0];
4786 FilePathValue(path);
4787 return rb_realpath_internal(basedir, path, 1);
4788}
4789
4790/*
4791 * call-seq:
4792 * File.realdirpath(pathname [, dir_string]) -> real_pathname
4793 *
4794 * Returns the real (absolute) pathname of _pathname_ in the actual filesystem.
4795 * The real pathname doesn't contain symlinks or useless dots.
4796 *
4797 * If _dir_string_ is given, it is used as a base directory
4798 * for interpreting relative pathname instead of the current directory.
4799 *
4800 * The last component of the real pathname can be nonexistent.
4801 */
4802static VALUE
4803rb_file_s_realdirpath(int argc, VALUE *argv, VALUE klass)
4804{
4805 VALUE basedir = (rb_check_arity(argc, 1, 2) > 1) ? argv[1] : Qnil;
4806 VALUE path = argv[0];
4807 FilePathValue(path);
4808 return rb_realpath_internal(basedir, path, 0);
4809}
4810
4811static size_t
4812rmext(const char *p, long l0, long l1, const char *e, long l2, rb_encoding *enc)
4813{
4814 int len1, len2;
4815 unsigned int c;
4816 const char *s, *last;
4817
4818 if (!e || !l2) return 0;
4819
4820 c = rb_enc_codepoint_len(e, e + l2, &len1, enc);
4821 if (rb_enc_ascget(e + len1, e + l2, &len2, enc) == '*' && len1 + len2 == l2) {
4822 if (c == '.') return l0;
4823 s = p;
4824 e = p + l1;
4825 last = e;
4826 while (s < e) {
4827 if (rb_enc_codepoint_len(s, e, &len1, enc) == c) last = s;
4828 s += len1;
4829 }
4830 return last - p;
4831 }
4832 if (l1 < l2) return l1;
4833
4834 s = p+l1-l2;
4835 if (!at_char_boundary(p, s, p+l1, enc)) return 0;
4836#if CASEFOLD_FILESYSTEM
4837#define fncomp strncasecmp
4838#else
4839#define fncomp strncmp
4840#endif
4841 if (fncomp(s, e, l2) == 0) {
4842 return l1-l2;
4843 }
4844 return 0;
4845}
4846
4847const char *
4848ruby_enc_find_basename(const char *name, long *baselen, long *alllen, rb_encoding *enc)
4849{
4850 const char *p, *q, *e, *end;
4851#if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
4852 const char *root;
4853#endif
4854 long f = 0, n = -1;
4855
4856 end = name + (alllen ? (size_t)*alllen : strlen(name));
4857 name = skipprefix(name, end, enc);
4858#if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
4859 root = name;
4860#endif
4861 while (isdirsep(*name))
4862 name++;
4863 if (!*name) {
4864 p = name - 1;
4865 f = 1;
4866#if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
4867 if (name != root) {
4868 /* has slashes */
4869 }
4870#ifdef DOSISH_DRIVE_LETTER
4871 else if (*p == ':') {
4872 p++;
4873 f = 0;
4874 }
4875#endif /* DOSISH_DRIVE_LETTER */
4876#ifdef DOSISH_UNC
4877 else {
4878 p = "/";
4879 }
4880#endif /* DOSISH_UNC */
4881#endif /* defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC */
4882 }
4883 else {
4884 if (!(p = strrdirsep(name, end, enc))) {
4885 p = name;
4886 }
4887 else {
4888 while (isdirsep(*p)) p++; /* skip last / */
4889 }
4890#if USE_NTFS
4891 n = ntfs_tail(p, end, enc) - p;
4892#else
4893 n = chompdirsep(p, end, enc) - p;
4894#endif
4895 for (q = p; q - p < n && *q == '.'; q++);
4896 for (e = 0; q - p < n; Inc(q, end, enc)) {
4897 if (*q == '.') e = q;
4898 }
4899 if (e) f = e - p;
4900 else f = n;
4901 }
4902
4903 if (baselen)
4904 *baselen = f;
4905 if (alllen)
4906 *alllen = n;
4907 return p;
4908}
4909
4910/*
4911 * call-seq:
4912 * File.basename(file_name [, suffix] ) -> base_name
4913 *
4914 * Returns the last component of the filename given in
4915 * <i>file_name</i> (after first stripping trailing separators),
4916 * which can be formed using both File::SEPARATOR and
4917 * File::ALT_SEPARATOR as the separator when File::ALT_SEPARATOR is
4918 * not <code>nil</code>. If <i>suffix</i> is given and present at the
4919 * end of <i>file_name</i>, it is removed. If <i>suffix</i> is ".*",
4920 * any extension will be removed.
4921 *
4922 * File.basename("/home/gumby/work/ruby.rb") #=> "ruby.rb"
4923 * File.basename("/home/gumby/work/ruby.rb", ".rb") #=> "ruby"
4924 * File.basename("/home/gumby/work/ruby.rb", ".*") #=> "ruby"
4925 */
4926
4927static VALUE
4928rb_file_s_basename(int argc, VALUE *argv, VALUE _)
4929{
4930 VALUE fname, fext, basename;
4931 const char *name, *p;
4932 long f, n;
4933 rb_encoding *enc;
4934
4935 fext = Qnil;
4936 if (rb_check_arity(argc, 1, 2) == 2) {
4937 fext = argv[1];
4938 StringValue(fext);
4939 enc = check_path_encoding(fext);
4940 }
4941 fname = argv[0];
4942 FilePathStringValue(fname);
4943 if (NIL_P(fext) || !(enc = rb_enc_compatible(fname, fext))) {
4944 enc = rb_enc_get(fname);
4945 fext = Qnil;
4946 }
4947 if ((n = RSTRING_LEN(fname)) == 0 || !*(name = RSTRING_PTR(fname)))
4948 return rb_str_new_shared(fname);
4949
4950 p = ruby_enc_find_basename(name, &f, &n, enc);
4951 if (n >= 0) {
4952 if (NIL_P(fext)) {
4953 f = n;
4954 }
4955 else {
4956 const char *fp;
4957 fp = StringValueCStr(fext);
4958 if (!(f = rmext(p, f, n, fp, RSTRING_LEN(fext), enc))) {
4959 f = n;
4960 }
4961 RB_GC_GUARD(fext);
4962 }
4963 if (f == RSTRING_LEN(fname)) return rb_str_new_shared(fname);
4964 }
4965
4966 basename = rb_str_new(p, f);
4967 rb_enc_copy(basename, fname);
4968 return basename;
4969}
4970
4971static VALUE rb_file_dirname_n(VALUE fname, int n);
4972
4973/*
4974 * call-seq:
4975 * File.dirname(file_name, level = 1) -> dir_name
4976 *
4977 * Returns all components of the filename given in <i>file_name</i>
4978 * except the last one (after first stripping trailing separators).
4979 * The filename can be formed using both File::SEPARATOR and
4980 * File::ALT_SEPARATOR as the separator when File::ALT_SEPARATOR is
4981 * not <code>nil</code>.
4982 *
4983 * File.dirname("/home/gumby/work/ruby.rb") #=> "/home/gumby/work"
4984 *
4985 * If +level+ is given, removes the last +level+ components, not only
4986 * one.
4987 *
4988 * File.dirname("/home/gumby/work/ruby.rb", 2) #=> "/home/gumby"
4989 * File.dirname("/home/gumby/work/ruby.rb", 4) #=> "/"
4990 */
4991
4992static VALUE
4993rb_file_s_dirname(int argc, VALUE *argv, VALUE klass)
4994{
4995 int n = 1;
4996 if ((argc = rb_check_arity(argc, 1, 2)) > 1) {
4997 n = NUM2INT(argv[1]);
4998 }
4999 return rb_file_dirname_n(argv[0], n);
5000}
5001
5002VALUE
5003rb_file_dirname(VALUE fname)
5004{
5005 return rb_file_dirname_n(fname, 1);
5006}
5007
5008static VALUE
5009rb_file_dirname_n(VALUE fname, int n)
5010{
5011 const char *name, *root, *p, *end;
5012 VALUE dirname;
5013 rb_encoding *enc;
5014 VALUE sepsv = 0;
5015 const char **seps;
5016
5017 if (n < 0) rb_raise(rb_eArgError, "negative level: %d", n);
5018 FilePathStringValue(fname);
5019 name = StringValueCStr(fname);
5020 end = name + RSTRING_LEN(fname);
5021 enc = rb_enc_get(fname);
5022 root = skiproot(name, end, enc);
5023#ifdef DOSISH_UNC
5024 if (root > name + 1 && isdirsep(*name))
5025 root = skipprefix(name = root - 2, end, enc);
5026#else
5027 if (root > name + 1)
5028 name = root - 1;
5029#endif
5030 if (n > (end - root + 1) / 2) {
5031 p = root;
5032 }
5033 else {
5034 int i;
5035 switch (n) {
5036 case 0:
5037 p = end;
5038 break;
5039 case 1:
5040 if (!(p = strrdirsep(root, end, enc))) p = root;
5041 break;
5042 default:
5043 seps = ALLOCV_N(const char *, sepsv, n);
5044 for (i = 0; i < n; ++i) seps[i] = root;
5045 i = 0;
5046 for (p = root; p < end; ) {
5047 if (isdirsep(*p)) {
5048 const char *tmp = p++;
5049 while (p < end && isdirsep(*p)) p++;
5050 if (p >= end) break;
5051 seps[i++] = tmp;
5052 if (i == n) i = 0;
5053 }
5054 else {
5055 Inc(p, end, enc);
5056 }
5057 }
5058 p = seps[i];
5059 ALLOCV_END(sepsv);
5060 break;
5061 }
5062 }
5063 if (p == name) {
5064 dirname = rb_str_new(".", 1);
5065 rb_enc_copy(dirname, fname);
5066 return dirname;
5067 }
5068#ifdef DOSISH_DRIVE_LETTER
5069 if (has_drive_letter(name) && isdirsep(*(name + 2))) {
5070 const char *top = skiproot(name + 2, end, enc);
5071 dirname = rb_str_new(name, 3);
5072 rb_str_cat(dirname, top, p - top);
5073 }
5074 else
5075#endif
5076 dirname = rb_str_new(name, p - name);
5077#ifdef DOSISH_DRIVE_LETTER
5078 if (has_drive_letter(name) && root == name + 2 && p - name == 2)
5079 rb_str_cat(dirname, ".", 1);
5080#endif
5081 rb_enc_copy(dirname, fname);
5082 return dirname;
5083}
5084
5085/*
5086 * accept a String, and return the pointer of the extension.
5087 * if len is passed, set the length of extension to it.
5088 * returned pointer is in ``name'' or NULL.
5089 * returns *len
5090 * no dot NULL 0
5091 * dotfile top 0
5092 * end with dot dot 1
5093 * .ext dot len of .ext
5094 * .ext:stream dot len of .ext without :stream (NTFS only)
5095 *
5096 */
5097const char *
5098ruby_enc_find_extname(const char *name, long *len, rb_encoding *enc)
5099{
5100 const char *p, *e, *end = name + (len ? *len : (long)strlen(name));
5101
5102 p = strrdirsep(name, end, enc); /* get the last path component */
5103 if (!p)
5104 p = name;
5105 else
5106 do name = ++p; while (isdirsep(*p));
5107
5108 e = 0;
5109 while (*p && *p == '.') p++;
5110 while (*p) {
5111 if (*p == '.' || istrailinggarbage(*p)) {
5112#if USE_NTFS
5113 const char *last = p++, *dot = last;
5114 while (istrailinggarbage(*p)) {
5115 if (*p == '.') dot = p;
5116 p++;
5117 }
5118 if (!*p || isADS(*p)) {
5119 p = last;
5120 break;
5121 }
5122 if (*last == '.' || dot > last) e = dot;
5123 continue;
5124#else
5125 e = p; /* get the last dot of the last component */
5126#endif /* USE_NTFS */
5127 }
5128#if USE_NTFS
5129 else if (isADS(*p)) {
5130 break;
5131 }
5132#endif
5133 else if (isdirsep(*p))
5134 break;
5135 Inc(p, end, enc);
5136 }
5137
5138 if (len) {
5139 /* no dot, or the only dot is first or end? */
5140 if (!e || e == name)
5141 *len = 0;
5142 else if (e+1 == p)
5143 *len = 1;
5144 else
5145 *len = p - e;
5146 }
5147 return e;
5148}
5149
5150/*
5151 * call-seq:
5152 * File.extname(path) -> string
5153 *
5154 * Returns the extension (the portion of file name in +path+
5155 * starting from the last period).
5156 *
5157 * If +path+ is a dotfile, or starts with a period, then the starting
5158 * dot is not dealt with the start of the extension.
5159 *
5160 * An empty string will also be returned when the period is the last character
5161 * in +path+.
5162 *
5163 * On Windows, trailing dots are truncated.
5164 *
5165 * File.extname("test.rb") #=> ".rb"
5166 * File.extname("a/b/d/test.rb") #=> ".rb"
5167 * File.extname(".a/b/d/test.rb") #=> ".rb"
5168 * File.extname("foo.") #=> "" on Windows
5169 * File.extname("foo.") #=> "." on non-Windows
5170 * File.extname("test") #=> ""
5171 * File.extname(".profile") #=> ""
5172 * File.extname(".profile.sh") #=> ".sh"
5173 *
5174 */
5175
5176static VALUE
5177rb_file_s_extname(VALUE klass, VALUE fname)
5178{
5179 const char *name, *e;
5180 long len;
5181 VALUE extname;
5182
5183 FilePathStringValue(fname);
5184 name = StringValueCStr(fname);
5185 len = RSTRING_LEN(fname);
5186 e = ruby_enc_find_extname(name, &len, rb_enc_get(fname));
5187 if (len < 1)
5188 return rb_str_new(0, 0);
5189 extname = rb_str_subseq(fname, e - name, len); /* keep the dot, too! */
5190 return extname;
5191}
5192
5193/*
5194 * call-seq:
5195 * File.path(path) -> string
5196 *
5197 * Returns the string representation of the path
5198 *
5199 * File.path(File::NULL) #=> "/dev/null"
5200 * File.path(Pathname.new("/tmp")) #=> "/tmp"
5201 *
5202 * If +path+ is not a String:
5203 *
5204 * 1. If it has the +to_path+ method, that method will be called to
5205 * coerce to a String.
5206 *
5207 * 2. Otherwise, or if the coerced result is not a String too, the
5208 * standard coersion using +to_str+ method will take place on that
5209 * object. (See also String.try_convert)
5210 *
5211 * The coerced string must satisfy the following conditions:
5212 *
5213 * 1. It must be in an ASCII-compatible encoding; otherwise, an
5214 * Encoding::CompatibilityError is raised.
5215 *
5216 * 2. It must not contain the NUL character (<tt>\0</tt>); otherwise,
5217 * an ArgumentError is raised.
5218 */
5219
5220static VALUE
5221rb_file_s_path(VALUE klass, VALUE fname)
5222{
5223 return rb_get_path(fname);
5224}
5225
5226/*
5227 * call-seq:
5228 * File.split(file_name) -> array
5229 *
5230 * Splits the given string into a directory and a file component and
5231 * returns them in a two-element array. See also File::dirname and
5232 * File::basename.
5233 *
5234 * File.split("/home/gumby/.profile") #=> ["/home/gumby", ".profile"]
5235 */
5236
5237static VALUE
5238rb_file_s_split(VALUE klass, VALUE path)
5239{
5240 FilePathStringValue(path); /* get rid of converting twice */
5241 return rb_assoc_new(rb_file_dirname(path), rb_file_s_basename(1,&path,Qundef));
5242}
5243
5244static VALUE
5245file_inspect_join(VALUE ary, VALUE arg, int recur)
5246{
5247 if (recur || ary == arg) rb_raise(rb_eArgError, "recursive array");
5248 return rb_file_join(arg);
5249}
5250
5251static VALUE
5252rb_file_join(VALUE ary)
5253{
5254 long len, i;
5255 VALUE result, tmp;
5256 const char *name, *tail;
5257 int checked = TRUE;
5258 rb_encoding *enc;
5259
5260 if (RARRAY_LEN(ary) == 0) return rb_str_new(0, 0);
5261
5262 len = 1;
5263 for (i=0; i<RARRAY_LEN(ary); i++) {
5264 tmp = RARRAY_AREF(ary, i);
5265 if (RB_TYPE_P(tmp, T_STRING)) {
5266 check_path_encoding(tmp);
5267 len += RSTRING_LEN(tmp);
5268 }
5269 else {
5270 len += 10;
5271 }
5272 }
5273 len += RARRAY_LEN(ary) - 1;
5274 result = rb_str_buf_new(len);
5275 RBASIC_CLEAR_CLASS(result);
5276 for (i=0; i<RARRAY_LEN(ary); i++) {
5277 tmp = RARRAY_AREF(ary, i);
5278 switch (OBJ_BUILTIN_TYPE(tmp)) {
5279 case T_STRING:
5280 if (!checked) check_path_encoding(tmp);
5281 StringValueCStr(tmp);
5282 break;
5283 case T_ARRAY:
5284 if (ary == tmp) {
5285 rb_raise(rb_eArgError, "recursive array");
5286 }
5287 else {
5288 tmp = rb_exec_recursive(file_inspect_join, ary, tmp);
5289 }
5290 break;
5291 default:
5293 checked = FALSE;
5294 }
5295 RSTRING_GETMEM(result, name, len);
5296 if (i == 0) {
5297 rb_enc_copy(result, tmp);
5298 }
5299 else {
5300 tail = chompdirsep(name, name + len, rb_enc_get(result));
5301 if (RSTRING_PTR(tmp) && isdirsep(RSTRING_PTR(tmp)[0])) {
5302 rb_str_set_len(result, tail - name);
5303 }
5304 else if (!*tail) {
5305 rb_str_cat(result, "/", 1);
5306 }
5307 }
5308 enc = fs_enc_check(result, tmp);
5309 rb_str_buf_append(result, tmp);
5310 rb_enc_associate(result, enc);
5311 }
5312 RBASIC_SET_CLASS_RAW(result, rb_cString);
5313
5314 return result;
5315}
5316
5317/*
5318 * call-seq:
5319 * File.join(string, ...) -> string
5320 *
5321 * Returns a new string formed by joining the strings using
5322 * <code>"/"</code>.
5323 *
5324 * File.join("usr", "mail", "gumby") #=> "usr/mail/gumby"
5325 *
5326 */
5327
5328static VALUE
5329rb_file_s_join(VALUE klass, VALUE args)
5330{
5331 return rb_file_join(args);
5332}
5333
5334#if defined(HAVE_TRUNCATE)
5335struct truncate_arg {
5336 const char *path;
5337 rb_off_t pos;
5338};
5339
5340static void *
5341nogvl_truncate(void *ptr)
5342{
5343 struct truncate_arg *ta = ptr;
5344 return (void *)(VALUE)truncate(ta->path, ta->pos);
5345}
5346
5347/*
5348 * call-seq:
5349 * File.truncate(file_name, integer) -> 0
5350 *
5351 * Truncates the file <i>file_name</i> to be at most <i>integer</i>
5352 * bytes long. Not available on all platforms.
5353 *
5354 * f = File.new("out", "w")
5355 * f.write("1234567890") #=> 10
5356 * f.close #=> nil
5357 * File.truncate("out", 5) #=> 0
5358 * File.size("out") #=> 5
5359 *
5360 */
5361
5362static VALUE
5363rb_file_s_truncate(VALUE klass, VALUE path, VALUE len)
5364{
5365 struct truncate_arg ta;
5366 int r;
5367
5368 ta.pos = NUM2OFFT(len);
5369 FilePathValue(path);
5370 path = rb_str_encode_ospath(path);
5371 ta.path = StringValueCStr(path);
5372
5373 r = IO_WITHOUT_GVL_INT(nogvl_truncate, &ta);
5374 if (r < 0)
5375 rb_sys_fail_path(path);
5376 return INT2FIX(0);
5377}
5378#else
5379#define rb_file_s_truncate rb_f_notimplement
5380#endif
5381
5382#if defined(HAVE_FTRUNCATE)
5383struct ftruncate_arg {
5384 int fd;
5385 rb_off_t pos;
5386};
5387
5388static VALUE
5389nogvl_ftruncate(void *ptr)
5390{
5391 struct ftruncate_arg *fa = ptr;
5392
5393 return (VALUE)ftruncate(fa->fd, fa->pos);
5394}
5395
5396/*
5397 * call-seq:
5398 * file.truncate(integer) -> 0
5399 *
5400 * Truncates <i>file</i> to at most <i>integer</i> bytes. The file
5401 * must be opened for writing. Not available on all platforms.
5402 *
5403 * f = File.new("out", "w")
5404 * f.syswrite("1234567890") #=> 10
5405 * f.truncate(5) #=> 0
5406 * f.close() #=> nil
5407 * File.size("out") #=> 5
5408 */
5409
5410static VALUE
5411rb_file_truncate(VALUE obj, VALUE len)
5412{
5413 rb_io_t *fptr;
5414 struct ftruncate_arg fa;
5415
5416 fa.pos = NUM2OFFT(len);
5417 GetOpenFile(obj, fptr);
5418 if (!(fptr->mode & FMODE_WRITABLE)) {
5419 rb_raise(rb_eIOError, "not opened for writing");
5420 }
5421 rb_io_flush_raw(obj, 0);
5422 fa.fd = fptr->fd;
5423 if ((int)rb_io_blocking_region(fptr, nogvl_ftruncate, &fa) < 0) {
5424 rb_sys_fail_path(fptr->pathv);
5425 }
5426 return INT2FIX(0);
5427}
5428#else
5429#define rb_file_truncate rb_f_notimplement
5430#endif
5431
5432# ifndef LOCK_SH
5433# define LOCK_SH 1
5434# endif
5435# ifndef LOCK_EX
5436# define LOCK_EX 2
5437# endif
5438# ifndef LOCK_NB
5439# define LOCK_NB 4
5440# endif
5441# ifndef LOCK_UN
5442# define LOCK_UN 8
5443# endif
5444
5445#ifdef __CYGWIN__
5446#include <winerror.h>
5447#endif
5448
5449static VALUE
5450rb_thread_flock(void *data)
5451{
5452#ifdef __CYGWIN__
5453 int old_errno = errno;
5454#endif
5455 int *op = data, ret = flock(op[0], op[1]);
5456
5457#ifdef __CYGWIN__
5458 if (GetLastError() == ERROR_NOT_LOCKED) {
5459 ret = 0;
5460 errno = old_errno;
5461 }
5462#endif
5463 return (VALUE)ret;
5464}
5465
5466/* :markup: markdown
5467 *
5468 * call-seq:
5469 * flock(locking_constant) -> 0 or false
5470 *
5471 * Locks or unlocks file +self+ according to the given `locking_constant`,
5472 * a bitwise OR of the values in the table below.
5473 *
5474 * Not available on all platforms.
5475 *
5476 * Returns `false` if `File::LOCK_NB` is specified and the operation would have blocked;
5477 * otherwise returns `0`.
5478 *
5479 * | Constant | Lock | Effect
5480 * |-----------------|--------------|-----------------------------------------------------------------------------------------------------------------|
5481 * | +File::LOCK_EX+ | Exclusive | Only one process may hold an exclusive lock for +self+ at a time. |
5482 * | +File::LOCK_NB+ | Non-blocking | No blocking; may be combined with +File::LOCK_SH+ or +File::LOCK_EX+ using the bitwise OR operator <tt>\|</tt>. |
5483 * | +File::LOCK_SH+ | Shared | Multiple processes may each hold a shared lock for +self+ at the same time. |
5484 * | +File::LOCK_UN+ | Unlock | Remove an existing lock held by this process. |
5485 *
5486 * Example:
5487 *
5488 * ```ruby
5489 * # Update a counter using an exclusive lock.
5490 * # Don't use File::WRONLY because it truncates the file.
5491 * File.open('counter', File::RDWR | File::CREAT, 0644) do |f|
5492 * f.flock(File::LOCK_EX)
5493 * value = f.read.to_i + 1
5494 * f.rewind
5495 * f.write("#{value}\n")
5496 * f.flush
5497 * f.truncate(f.pos)
5498 * end
5499 *
5500 * # Read the counter using a shared lock.
5501 * File.open('counter', 'r') do |f|
5502 * f.flock(File::LOCK_SH)
5503 * f.read
5504 * end
5505 * ```
5506 *
5507 */
5508
5509static VALUE
5510rb_file_flock(VALUE obj, VALUE operation)
5511{
5512 rb_io_t *fptr;
5513 int op[2], op1;
5514 struct timeval time;
5515
5516 op[1] = op1 = NUM2INT(operation);
5517 GetOpenFile(obj, fptr);
5518 op[0] = fptr->fd;
5519
5520 if (fptr->mode & FMODE_WRITABLE) {
5521 rb_io_flush_raw(obj, 0);
5522 }
5523 while ((int)rb_io_blocking_region(fptr, rb_thread_flock, op) < 0) {
5524 int e = errno;
5525 switch (e) {
5526 case EAGAIN:
5527 case EACCES:
5528#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
5529 case EWOULDBLOCK:
5530#endif
5531 if (op1 & LOCK_NB) return Qfalse;
5532
5533 time.tv_sec = 0;
5534 time.tv_usec = 100 * 1000; /* 0.1 sec */
5535 rb_thread_wait_for(time);
5536 rb_io_check_closed(fptr);
5537 continue;
5538
5539 case EINTR:
5540#if defined(ERESTART)
5541 case ERESTART:
5542#endif
5543 break;
5544
5545 default:
5546 rb_syserr_fail_path(e, fptr->pathv);
5547 }
5548 }
5549 return INT2FIX(0);
5550}
5551
5552static void
5553test_check(int n, int argc, VALUE *argv)
5554{
5555 int i;
5556
5557 n+=1;
5558 rb_check_arity(argc, n, n);
5559 for (i=1; i<n; i++) {
5560 if (!RB_TYPE_P(argv[i], T_FILE)) {
5561 FilePathValue(argv[i]);
5562 }
5563 }
5564}
5565
5566#define CHECK(n) test_check((n), argc, argv)
5567
5568/*
5569 * :markup: markdown
5570 *
5571 * call-seq:
5572 * test(char, path0, path1 = nil) -> object
5573 *
5574 * Performs a test on one or both of the <i>filesystem entities</i> at the given paths
5575 * `path0` and `path1`:
5576 *
5577 * - Each path `path0` or `path1` points to a file, directory, device, pipe, etc.
5578 * - Character `char` selects a specific test.
5579 *
5580 * The tests:
5581 *
5582 * - Each of these tests operates only on the entity at `path0`,
5583 * and returns `true` or `false`;
5584 * for a non-existent entity, returns `false` (does not raise exception):
5585 *
5586 * | Character | Test |
5587 * |:------------:|:--------------------------------------------------------------------------|
5588 * | <tt>'b'</tt> | Whether the entity is a block device. |
5589 * | <tt>'c'</tt> | Whether the entity is a character device. |
5590 * | <tt>'d'</tt> | Whether the entity is a directory. |
5591 * | <tt>'e'</tt> | Whether the entity is an existing entity. |
5592 * | <tt>'f'</tt> | Whether the entity is an existing regular file. |
5593 * | <tt>'g'</tt> | Whether the entity's setgid bit is set. |
5594 * | <tt>'G'</tt> | Whether the entity's group ownership is equal to the caller's. |
5595 * | <tt>'k'</tt> | Whether the entity's sticky bit is set. |
5596 * | <tt>'l'</tt> | Whether the entity is a symbolic link. |
5597 * | <tt>'o'</tt> | Whether the entity is owned by the caller's effective uid. |
5598 * | <tt>'O'</tt> | Like <tt>'o'</tt>, but uses the real uid (not the effective uid). |
5599 * | <tt>'p'</tt> | Whether the entity is a FIFO device (named pipe). |
5600 * | <tt>'r'</tt> | Whether the entity is readable by the caller's effective uid/gid. |
5601 * | <tt>'R'</tt> | Like <tt>'r'</tt>, but uses the real uid/gid (not the effective uid/gid). |
5602 * | <tt>'S'</tt> | Whether the entity is a socket. |
5603 * | <tt>'u'</tt> | Whether the entity's setuid bit is set. |
5604 * | <tt>'w'</tt> | Whether the entity is writable by the caller's effective uid/gid. |
5605 * | <tt>'W'</tt> | Like <tt>'w'</tt>, but uses the real uid/gid (not the effective uid/gid). |
5606 * | <tt>'x'</tt> | Whether the entity is executable by the caller's effective uid/gid. |
5607 * | <tt>'X'</tt> | Like <tt>'x'</tt>, but uses the real uid/gid (not the effective uid/git). |
5608 * | <tt>'z'</tt> | Whether the entity exists and is of length zero. |
5609 *
5610 * - This test operates only on the entity at `path0`,
5611 * and returns an integer size or +nil+:
5612 *
5613 * | Character | Test |
5614 * |:------------:|:---------------------------------------------------------------------------------------------|
5615 * | <tt>'s'</tt> | Returns positive integer size if the entity exists and has non-zero length, +nil+ otherwise. |
5616 *
5617 * - Each of these tests operates only on the entity at `path0`,
5618 * and returns a Time object;
5619 * raises an exception if the entity does not exist:
5620 *
5621 * | Character | Test |
5622 * |:------------:|:---------------------------------------|
5623 * | <tt>'A'</tt> | Last access time for the entity. |
5624 * | <tt>'C'</tt> | Last change time for the entity. |
5625 * | <tt>'M'</tt> | Last modification time for the entity. |
5626 *
5627 * - Each of these tests operates on the modification time (`mtime`)
5628 * of each of the entities at `path0` and `path1`,
5629 * and returns a `true` or `false`;
5630 * returns `false` if either entity does not exist:
5631 *
5632 * | Character | Test |
5633 * |:------------:|:----------------------------------------------------------------|
5634 * | <tt>'<'</tt> | Whether the `mtime` at `path0` is less than that at `path1`. |
5635 * | <tt>'='</tt> | Whether the `mtime` at `path0` is equal to that at `path1`. |
5636 * | <tt>'>'</tt> | Whether the `mtime` at `path0` is greater than that at `path1`. |
5637 *
5638 * - This test operates on the content of each of the entities at `path0` and `path1`,
5639 * and returns a `true` or `false`;
5640 * returns `false` if either entity does not exist:
5641 *
5642 * | Character | Test |
5643 * |:------------:|:----------------------------------------------|
5644 * | <tt>'-'</tt> | Whether the entities exist and are identical. |
5645 *
5646 */
5647
5648static VALUE
5649rb_f_test(int argc, VALUE *argv, VALUE _)
5650{
5651 int cmd;
5652
5653 if (argc == 0) rb_check_arity(argc, 2, 3);
5654 cmd = NUM2CHR(argv[0]);
5655 if (cmd == 0) {
5656 goto unknown;
5657 }
5658 if (strchr("bcdefgGkloOprRsSuwWxXz", cmd)) {
5659 CHECK(1);
5660 switch (cmd) {
5661 case 'b':
5662 return rb_file_blockdev_p(0, argv[1]);
5663
5664 case 'c':
5665 return rb_file_chardev_p(0, argv[1]);
5666
5667 case 'd':
5668 return rb_file_directory_p(0, argv[1]);
5669
5670 case 'e':
5671 return rb_file_exist_p(0, argv[1]);
5672
5673 case 'f':
5674 return rb_file_file_p(0, argv[1]);
5675
5676 case 'g':
5677 return rb_file_sgid_p(0, argv[1]);
5678
5679 case 'G':
5680 return rb_file_grpowned_p(0, argv[1]);
5681
5682 case 'k':
5683 return rb_file_sticky_p(0, argv[1]);
5684
5685 case 'l':
5686 return rb_file_symlink_p(0, argv[1]);
5687
5688 case 'o':
5689 return rb_file_owned_p(0, argv[1]);
5690
5691 case 'O':
5692 return rb_file_rowned_p(0, argv[1]);
5693
5694 case 'p':
5695 return rb_file_pipe_p(0, argv[1]);
5696
5697 case 'r':
5698 return rb_file_readable_p(0, argv[1]);
5699
5700 case 'R':
5701 return rb_file_readable_real_p(0, argv[1]);
5702
5703 case 's':
5704 return rb_file_size_p(0, argv[1]);
5705
5706 case 'S':
5707 return rb_file_socket_p(0, argv[1]);
5708
5709 case 'u':
5710 return rb_file_suid_p(0, argv[1]);
5711
5712 case 'w':
5713 return rb_file_writable_p(0, argv[1]);
5714
5715 case 'W':
5716 return rb_file_writable_real_p(0, argv[1]);
5717
5718 case 'x':
5719 return rb_file_executable_p(0, argv[1]);
5720
5721 case 'X':
5722 return rb_file_executable_real_p(0, argv[1]);
5723
5724 case 'z':
5725 return rb_file_zero_p(0, argv[1]);
5726 }
5727 }
5728
5729 if (strchr("MAC", cmd)) {
5730 struct stat st;
5731 VALUE fname = argv[1];
5732
5733 CHECK(1);
5734 if (rb_stat(fname, &st) == -1) {
5735 int e = errno;
5736 FilePathValue(fname);
5737 rb_syserr_fail_path(e, fname);
5738 }
5739
5740 switch (cmd) {
5741 case 'A':
5742 return stat_atime(&st);
5743 case 'M':
5744 return stat_mtime(&st);
5745 case 'C':
5746 return stat_ctime(&st);
5747 }
5748 }
5749
5750 if (cmd == '-') {
5751 CHECK(2);
5752 return rb_file_identical_p(0, argv[1], argv[2]);
5753 }
5754
5755 if (strchr("=<>", cmd)) {
5756 struct stat st1, st2;
5757 stat_timestamp t1, t2;
5758
5759 CHECK(2);
5760 if (rb_stat(argv[1], &st1) < 0) return Qfalse;
5761 if (rb_stat(argv[2], &st2) < 0) return Qfalse;
5762
5763 t1 = stat_mtimespec(&st1);
5764 t2 = stat_mtimespec(&st2);
5765
5766 switch (cmd) {
5767 case '=':
5768 if (t1.tv_sec == t2.tv_sec && t1.tv_nsec == t2.tv_nsec) return Qtrue;
5769 return Qfalse;
5770
5771 case '>':
5772 if (t1.tv_sec > t2.tv_sec) return Qtrue;
5773 if (t1.tv_sec == t2.tv_sec && t1.tv_nsec > t2.tv_nsec) return Qtrue;
5774 return Qfalse;
5775
5776 case '<':
5777 if (t1.tv_sec < t2.tv_sec) return Qtrue;
5778 if (t1.tv_sec == t2.tv_sec && t1.tv_nsec < t2.tv_nsec) return Qtrue;
5779 return Qfalse;
5780 }
5781 }
5782 unknown:
5783 /* unknown command */
5784 if (ISPRINT(cmd)) {
5785 rb_raise(rb_eArgError, "unknown command '%s%c'", cmd == '\'' || cmd == '\\' ? "\\" : "", cmd);
5786 }
5787 else {
5788 rb_raise(rb_eArgError, "unknown command \"\\x%02X\"", cmd);
5789 }
5791}
5792
5793
5794/*
5795 * Document-class: File::Stat
5796 *
5797 * Objects of class File::Stat encapsulate common status information
5798 * for File objects. The information is recorded at the moment the
5799 * File::Stat object is created; changes made to the file after that
5800 * point will not be reflected. File::Stat objects are returned by
5801 * IO#stat, File::stat, File#lstat, and File::lstat. Many of these
5802 * methods return platform-specific values, and not all values are
5803 * meaningful on all systems. See also Kernel#test.
5804 */
5805
5806static VALUE
5807rb_stat_s_alloc(VALUE klass)
5808{
5809 VALUE obj;
5810 stat_alloc(rb_cStat, &obj);
5811 return obj;
5812}
5813
5814/*
5815 * call-seq:
5816 * File::Stat.new(file_name) -> stat
5817 *
5818 * Create a File::Stat object for the given file name (raising an
5819 * exception if the file doesn't exist).
5820 */
5821
5822static VALUE
5823rb_stat_init(VALUE obj, VALUE fname)
5824{
5825 rb_io_stat_data st;
5826
5827 FilePathValue(fname);
5828 fname = rb_str_encode_ospath(fname);
5829 if (STATX(StringValueCStr(fname), &st, STATX_ALL) == -1) {
5830 rb_sys_fail_path(fname);
5831 }
5832
5833 struct rb_stat *rb_st;
5834 TypedData_Get_Struct(obj, struct rb_stat, &stat_data_type, rb_st);
5835
5836 rb_st->stat = st;
5837 rb_st->initialized = true;
5838
5839 return Qnil;
5840}
5841
5842/* :nodoc: */
5843static VALUE
5844rb_stat_init_copy(VALUE copy, VALUE orig)
5845{
5846 if (!OBJ_INIT_COPY(copy, orig)) return copy;
5847
5848 struct rb_stat *orig_rb_st;
5849 TypedData_Get_Struct(orig, struct rb_stat, &stat_data_type, orig_rb_st);
5850
5851 struct rb_stat *copy_rb_st;
5852 TypedData_Get_Struct(copy, struct rb_stat, &stat_data_type, copy_rb_st);
5853
5854 *copy_rb_st = *orig_rb_st;
5855 return copy;
5856}
5857
5858/*
5859 * call-seq:
5860 * stat.ftype -> string
5861 *
5862 * Identifies the type of <i>stat</i>. The return string is one of:
5863 * ``<code>file</code>'', ``<code>directory</code>'',
5864 * ``<code>characterSpecial</code>'', ``<code>blockSpecial</code>'',
5865 * ``<code>fifo</code>'', ``<code>link</code>'',
5866 * ``<code>socket</code>'', or ``<code>unknown</code>''.
5867 *
5868 * File.stat("/dev/tty").ftype #=> "characterSpecial"
5869 *
5870 */
5871
5872static VALUE
5873rb_stat_ftype(VALUE obj)
5874{
5875 return rb_file_ftype(get_stat(obj)->ST_(mode));
5876}
5877
5878/*
5879 * call-seq:
5880 * stat.directory? -> true or false
5881 *
5882 * Returns <code>true</code> if <i>stat</i> is a directory,
5883 * <code>false</code> otherwise.
5884 *
5885 * File.stat("testfile").directory? #=> false
5886 * File.stat(".").directory? #=> true
5887 */
5888
5889static VALUE
5890rb_stat_d(VALUE obj)
5891{
5892 if (S_ISDIR(get_stat(obj)->ST_(mode))) return Qtrue;
5893 return Qfalse;
5894}
5895
5896/*
5897 * call-seq:
5898 * stat.pipe? -> true or false
5899 *
5900 * Returns <code>true</code> if the operating system supports pipes and
5901 * <i>stat</i> is a pipe; <code>false</code> otherwise.
5902 */
5903
5904static VALUE
5905rb_stat_p(VALUE obj)
5906{
5907#ifdef S_IFIFO
5908 if (S_ISFIFO(get_stat(obj)->ST_(mode))) return Qtrue;
5909
5910#endif
5911 return Qfalse;
5912}
5913
5914/*
5915 * call-seq:
5916 * stat.symlink? -> true or false
5917 *
5918 * Returns <code>true</code> if <i>stat</i> is a symbolic link,
5919 * <code>false</code> if it isn't or if the operating system doesn't
5920 * support this feature. As File::stat automatically follows symbolic
5921 * links, #symlink? will always be <code>false</code> for an object
5922 * returned by File::stat.
5923 *
5924 * File.symlink("testfile", "alink") #=> 0
5925 * File.stat("alink").symlink? #=> false
5926 * File.lstat("alink").symlink? #=> true
5927 *
5928 */
5929
5930static VALUE
5931rb_stat_l(VALUE obj)
5932{
5933#ifdef S_ISLNK
5934 if (S_ISLNK(get_stat(obj)->ST_(mode))) return Qtrue;
5935#endif
5936 return Qfalse;
5937}
5938
5939/*
5940 * call-seq:
5941 * stat.socket? -> true or false
5942 *
5943 * Returns <code>true</code> if <i>stat</i> is a socket,
5944 * <code>false</code> if it isn't or if the operating system doesn't
5945 * support this feature.
5946 *
5947 * File.stat("testfile").socket? #=> false
5948 *
5949 */
5950
5951static VALUE
5952rb_stat_S(VALUE obj)
5953{
5954#ifdef S_ISSOCK
5955 if (S_ISSOCK(get_stat(obj)->ST_(mode))) return Qtrue;
5956
5957#endif
5958 return Qfalse;
5959}
5960
5961/*
5962 * call-seq:
5963 * stat.blockdev? -> true or false
5964 *
5965 * Returns <code>true</code> if the file is a block device,
5966 * <code>false</code> if it isn't or if the operating system doesn't
5967 * support this feature.
5968 *
5969 * File.stat("testfile").blockdev? #=> false
5970 * File.stat("/dev/hda1").blockdev? #=> true
5971 *
5972 */
5973
5974static VALUE
5975rb_stat_b(VALUE obj)
5976{
5977#ifdef S_ISBLK
5978 if (S_ISBLK(get_stat(obj)->ST_(mode))) return Qtrue;
5979
5980#endif
5981 return Qfalse;
5982}
5983
5984/*
5985 * call-seq:
5986 * stat.chardev? -> true or false
5987 *
5988 * Returns <code>true</code> if the file is a character device,
5989 * <code>false</code> if it isn't or if the operating system doesn't
5990 * support this feature.
5991 *
5992 * File.stat("/dev/tty").chardev? #=> true
5993 *
5994 */
5995
5996static VALUE
5997rb_stat_c(VALUE obj)
5998{
5999 if (S_ISCHR(get_stat(obj)->ST_(mode))) return Qtrue;
6000
6001 return Qfalse;
6002}
6003
6004/*
6005 * call-seq:
6006 * stat.owned? -> true or false
6007 *
6008 * Returns <code>true</code> if the effective user id of the process is
6009 * the same as the owner of <i>stat</i>.
6010 *
6011 * File.stat("testfile").owned? #=> true
6012 * File.stat("/etc/passwd").owned? #=> false
6013 *
6014 */
6015
6016static VALUE
6017rb_stat_owned(VALUE obj)
6018{
6019 if (get_stat(obj)->ST_(uid) == geteuid()) return Qtrue;
6020 return Qfalse;
6021}
6022
6023static VALUE
6024rb_stat_rowned(VALUE obj)
6025{
6026 if (get_stat(obj)->ST_(uid) == getuid()) return Qtrue;
6027 return Qfalse;
6028}
6029
6030/*
6031 * call-seq:
6032 * stat.grpowned? -> true or false
6033 *
6034 * Returns true if the effective group id of the process is the same as
6035 * the group id of <i>stat</i>. On Windows, returns <code>false</code>.
6036 *
6037 * File.stat("testfile").grpowned? #=> true
6038 * File.stat("/etc/passwd").grpowned? #=> false
6039 *
6040 */
6041
6042static VALUE
6043rb_stat_grpowned(VALUE obj)
6044{
6045#ifndef _WIN32
6046 if (rb_group_member(get_stat(obj)->ST_(gid))) return Qtrue;
6047#endif
6048 return Qfalse;
6049}
6050
6051/*
6052 * call-seq:
6053 * stat.readable? -> true or false
6054 *
6055 * Returns <code>true</code> if <i>stat</i> is readable by the
6056 * effective user id of this process.
6057 *
6058 * File.stat("testfile").readable? #=> true
6059 *
6060 */
6061
6062static VALUE
6063rb_stat_r(VALUE obj)
6064{
6065 rb_io_stat_data *st = get_stat(obj);
6066
6067#ifdef USE_GETEUID
6068 if (geteuid() == 0) return Qtrue;
6069#endif
6070#ifdef S_IRUSR
6071 if (rb_stat_owned(obj))
6072 return RBOOL(st->ST_(mode) & S_IRUSR);
6073#endif
6074#ifdef S_IRGRP
6075 if (rb_stat_grpowned(obj))
6076 return RBOOL(st->ST_(mode) & S_IRGRP);
6077#endif
6078#ifdef S_IROTH
6079 if (!(st->ST_(mode) & S_IROTH)) return Qfalse;
6080#endif
6081 return Qtrue;
6082}
6083
6084/*
6085 * call-seq:
6086 * stat.readable_real? -> true or false
6087 *
6088 * Returns <code>true</code> if <i>stat</i> is readable by the real
6089 * user id of this process.
6090 *
6091 * File.stat("testfile").readable_real? #=> true
6092 *
6093 */
6094
6095static VALUE
6096rb_stat_R(VALUE obj)
6097{
6098 rb_io_stat_data *st = get_stat(obj);
6099
6100#ifdef USE_GETEUID
6101 if (getuid() == 0) return Qtrue;
6102#endif
6103#ifdef S_IRUSR
6104 if (rb_stat_rowned(obj))
6105 return RBOOL(st->ST_(mode) & S_IRUSR);
6106#endif
6107#ifdef S_IRGRP
6108 if (rb_group_member(get_stat(obj)->ST_(gid)))
6109 return RBOOL(st->ST_(mode) & S_IRGRP);
6110#endif
6111#ifdef S_IROTH
6112 if (!(st->ST_(mode) & S_IROTH)) return Qfalse;
6113#endif
6114 return Qtrue;
6115}
6116
6117/*
6118 * call-seq:
6119 * stat.world_readable? -> integer or nil
6120 *
6121 * If <i>stat</i> is readable by others, returns an integer
6122 * representing the file permission bits of <i>stat</i>. Returns
6123 * <code>nil</code> otherwise. The meaning of the bits is platform
6124 * dependent; on Unix systems, see <code>stat(2)</code>.
6125 *
6126 * m = File.stat("/etc/passwd").world_readable? #=> 420
6127 * sprintf("%o", m) #=> "644"
6128 */
6129
6130static VALUE
6131rb_stat_wr(VALUE obj)
6132{
6133#ifdef S_IROTH
6134 rb_io_stat_data *st = get_stat(obj);
6135 if ((st->ST_(mode) & (S_IROTH)) == S_IROTH) {
6136 return UINT2NUM(st->ST_(mode) & (S_IRUGO|S_IWUGO|S_IXUGO));
6137 }
6138#endif
6139 return Qnil;
6140}
6141
6142/*
6143 * call-seq:
6144 * stat.writable? -> true or false
6145 *
6146 * Returns <code>true</code> if <i>stat</i> is writable by the
6147 * effective user id of this process.
6148 *
6149 * File.stat("testfile").writable? #=> true
6150 *
6151 */
6152
6153static VALUE
6154rb_stat_w(VALUE obj)
6155{
6156 rb_io_stat_data *st = get_stat(obj);
6157
6158#ifdef USE_GETEUID
6159 if (geteuid() == 0) return Qtrue;
6160#endif
6161#ifdef S_IWUSR
6162 if (rb_stat_owned(obj))
6163 return RBOOL(st->ST_(mode) & S_IWUSR);
6164#endif
6165#ifdef S_IWGRP
6166 if (rb_stat_grpowned(obj))
6167 return RBOOL(st->ST_(mode) & S_IWGRP);
6168#endif
6169#ifdef S_IWOTH
6170 if (!(st->ST_(mode) & S_IWOTH)) return Qfalse;
6171#endif
6172 return Qtrue;
6173}
6174
6175/*
6176 * call-seq:
6177 * stat.writable_real? -> true or false
6178 *
6179 * Returns <code>true</code> if <i>stat</i> is writable by the real
6180 * user id of this process.
6181 *
6182 * File.stat("testfile").writable_real? #=> true
6183 *
6184 */
6185
6186static VALUE
6187rb_stat_W(VALUE obj)
6188{
6189 rb_io_stat_data *st = get_stat(obj);
6190
6191#ifdef USE_GETEUID
6192 if (getuid() == 0) return Qtrue;
6193#endif
6194#ifdef S_IWUSR
6195 if (rb_stat_rowned(obj))
6196 return RBOOL(st->ST_(mode) & S_IWUSR);
6197#endif
6198#ifdef S_IWGRP
6199 if (rb_group_member(get_stat(obj)->ST_(gid)))
6200 return RBOOL(st->ST_(mode) & S_IWGRP);
6201#endif
6202#ifdef S_IWOTH
6203 if (!(st->ST_(mode) & S_IWOTH)) return Qfalse;
6204#endif
6205 return Qtrue;
6206}
6207
6208/*
6209 * call-seq:
6210 * stat.world_writable? -> integer or nil
6211 *
6212 * If <i>stat</i> is writable by others, returns an integer
6213 * representing the file permission bits of <i>stat</i>. Returns
6214 * <code>nil</code> otherwise. The meaning of the bits is platform
6215 * dependent; on Unix systems, see <code>stat(2)</code>.
6216 *
6217 * m = File.stat("/tmp").world_writable? #=> 511
6218 * sprintf("%o", m) #=> "777"
6219 */
6220
6221static VALUE
6222rb_stat_ww(VALUE obj)
6223{
6224#ifdef S_IWOTH
6225 rb_io_stat_data *st = get_stat(obj);
6226 if ((st->ST_(mode) & (S_IWOTH)) == S_IWOTH) {
6227 return UINT2NUM(st->ST_(mode) & (S_IRUGO|S_IWUGO|S_IXUGO));
6228 }
6229#endif
6230 return Qnil;
6231}
6232
6233/*
6234 * call-seq:
6235 * stat.executable? -> true or false
6236 *
6237 * Returns <code>true</code> if <i>stat</i> is executable or if the
6238 * operating system doesn't distinguish executable files from
6239 * nonexecutable files. The tests are made using the effective owner of
6240 * the process.
6241 *
6242 * File.stat("testfile").executable? #=> false
6243 *
6244 */
6245
6246static VALUE
6247rb_stat_x(VALUE obj)
6248{
6249 rb_io_stat_data *st = get_stat(obj);
6250
6251#ifdef USE_GETEUID
6252 if (geteuid() == 0) {
6253 return RBOOL(st->ST_(mode) & S_IXUGO);
6254 }
6255#endif
6256#ifdef S_IXUSR
6257 if (rb_stat_owned(obj))
6258 return RBOOL(st->ST_(mode) & S_IXUSR);
6259#endif
6260#ifdef S_IXGRP
6261 if (rb_stat_grpowned(obj))
6262 return RBOOL(st->ST_(mode) & S_IXGRP);
6263#endif
6264#ifdef S_IXOTH
6265 if (!(st->ST_(mode) & S_IXOTH)) return Qfalse;
6266#endif
6267 return Qtrue;
6268}
6269
6270/*
6271 * call-seq:
6272 * stat.executable_real? -> true or false
6273 *
6274 * Same as <code>executable?</code>, but tests using the real owner of
6275 * the process.
6276 */
6277
6278static VALUE
6279rb_stat_X(VALUE obj)
6280{
6281 rb_io_stat_data *st = get_stat(obj);
6282
6283#ifdef USE_GETEUID
6284 if (getuid() == 0) {
6285 return RBOOL(st->ST_(mode) & S_IXUGO);
6286 }
6287#endif
6288#ifdef S_IXUSR
6289 if (rb_stat_rowned(obj))
6290 return RBOOL(st->ST_(mode) & S_IXUSR);
6291#endif
6292#ifdef S_IXGRP
6293 if (rb_group_member(get_stat(obj)->ST_(gid)))
6294 return RBOOL(st->ST_(mode) & S_IXGRP);
6295#endif
6296#ifdef S_IXOTH
6297 if (!(st->ST_(mode) & S_IXOTH)) return Qfalse;
6298#endif
6299 return Qtrue;
6300}
6301
6302/*
6303 * call-seq:
6304 * stat.file? -> true or false
6305 *
6306 * Returns <code>true</code> if <i>stat</i> is a regular file (not
6307 * a device file, pipe, socket, etc.).
6308 *
6309 * File.stat("testfile").file? #=> true
6310 *
6311 */
6312
6313static VALUE
6314rb_stat_f(VALUE obj)
6315{
6316 if (S_ISREG(get_stat(obj)->ST_(mode))) return Qtrue;
6317 return Qfalse;
6318}
6319
6320/*
6321 * call-seq:
6322 * stat.zero? -> true or false
6323 *
6324 * Returns <code>true</code> if <i>stat</i> is a zero-length file;
6325 * <code>false</code> otherwise.
6326 *
6327 * File.stat("testfile").zero? #=> false
6328 *
6329 */
6330
6331static VALUE
6332rb_stat_z(VALUE obj)
6333{
6334 if (get_stat(obj)->ST_(size) == 0) return Qtrue;
6335 return Qfalse;
6336}
6337
6338/*
6339 * call-seq:
6340 * stat.size? -> Integer or nil
6341 *
6342 * Returns +nil+ if <i>stat</i> is a zero-length file, the size of
6343 * the file otherwise.
6344 *
6345 * File.stat("testfile").size? #=> 66
6346 * File.stat(File::NULL).size? #=> nil
6347 *
6348 */
6349
6350static VALUE
6351rb_stat_s(VALUE obj)
6352{
6353 rb_off_t size = get_stat(obj)->ST_(size);
6354
6355 if (size == 0) return Qnil;
6356 return OFFT2NUM(size);
6357}
6358
6359/*
6360 * call-seq:
6361 * stat.setuid? -> true or false
6362 *
6363 * Returns <code>true</code> if <i>stat</i> has the set-user-id
6364 * permission bit set, <code>false</code> if it doesn't or if the
6365 * operating system doesn't support this feature.
6366 *
6367 * File.stat("/bin/su").setuid? #=> true
6368 */
6369
6370static VALUE
6371rb_stat_suid(VALUE obj)
6372{
6373#ifdef S_ISUID
6374 if (get_stat(obj)->ST_(mode) & S_ISUID) return Qtrue;
6375#endif
6376 return Qfalse;
6377}
6378
6379/*
6380 * call-seq:
6381 * stat.setgid? -> true or false
6382 *
6383 * Returns <code>true</code> if <i>stat</i> has the set-group-id
6384 * permission bit set, <code>false</code> if it doesn't or if the
6385 * operating system doesn't support this feature.
6386 *
6387 * File.stat("/usr/sbin/lpc").setgid? #=> true
6388 *
6389 */
6390
6391static VALUE
6392rb_stat_sgid(VALUE obj)
6393{
6394#ifdef S_ISGID
6395 if (get_stat(obj)->ST_(mode) & S_ISGID) return Qtrue;
6396#endif
6397 return Qfalse;
6398}
6399
6400/*
6401 * call-seq:
6402 * stat.sticky? -> true or false
6403 *
6404 * Returns <code>true</code> if <i>stat</i> has its sticky bit set,
6405 * <code>false</code> if it doesn't or if the operating system doesn't
6406 * support this feature.
6407 *
6408 * File.stat("testfile").sticky? #=> false
6409 *
6410 */
6411
6412static VALUE
6413rb_stat_sticky(VALUE obj)
6414{
6415#ifdef S_ISVTX
6416 if (get_stat(obj)->ST_(mode) & S_ISVTX) return Qtrue;
6417#endif
6418 return Qfalse;
6419}
6420
6421#if !defined HAVE_MKFIFO && defined HAVE_MKNOD && defined S_IFIFO
6422#define mkfifo(path, mode) mknod(path, (mode)&~S_IFMT|S_IFIFO, 0)
6423#define HAVE_MKFIFO
6424#endif
6425
6426#ifdef HAVE_MKFIFO
6427struct mkfifo_arg {
6428 const char *path;
6429 mode_t mode;
6430};
6431
6432static void *
6433nogvl_mkfifo(void *ptr)
6434{
6435 struct mkfifo_arg *ma = ptr;
6436
6437 return (void *)(VALUE)mkfifo(ma->path, ma->mode);
6438}
6439
6440/*
6441 * call-seq:
6442 * File.mkfifo(file_name, mode=0666) => 0
6443 *
6444 * Creates a FIFO special file with name _file_name_. _mode_
6445 * specifies the FIFO's permissions. It is modified by the process's
6446 * umask in the usual way: the permissions of the created file are
6447 * (mode & ~umask).
6448 */
6449
6450static VALUE
6451rb_file_s_mkfifo(int argc, VALUE *argv, VALUE _)
6452{
6453 VALUE path;
6454 struct mkfifo_arg ma;
6455
6456 ma.mode = 0666;
6457 rb_check_arity(argc, 1, 2);
6458 if (argc > 1) {
6459 ma.mode = NUM2MODET(argv[1]);
6460 }
6461 path = argv[0];
6462 FilePathValue(path);
6463 path = rb_str_encode_ospath(path);
6464 ma.path = RSTRING_PTR(path);
6465 if (IO_WITHOUT_GVL(nogvl_mkfifo, &ma)) {
6466 rb_sys_fail_path(path);
6467 }
6468 return INT2FIX(0);
6469}
6470#else
6471#define rb_file_s_mkfifo rb_f_notimplement
6472#endif
6473
6474static VALUE rb_mFConst;
6475
6476void
6477rb_file_const(const char *name, VALUE value)
6478{
6479 rb_define_const(rb_mFConst, name, value);
6480}
6481
6482int
6483rb_is_absolute_path(const char *path)
6484{
6485#ifdef DOSISH_DRIVE_LETTER
6486 if (has_drive_letter(path) && isdirsep(path[2])) return 1;
6487#endif
6488#ifdef DOSISH_UNC
6489 if (isdirsep(path[0]) && isdirsep(path[1])) return 1;
6490#endif
6491#ifndef DOSISH
6492 if (path[0] == '/') return 1;
6493#endif
6494 return 0;
6495}
6496
6497#ifndef ENABLE_PATH_CHECK
6498# if defined DOSISH || defined __CYGWIN__
6499# define ENABLE_PATH_CHECK 0
6500# else
6501# define ENABLE_PATH_CHECK 1
6502# endif
6503#endif
6504
6505#if ENABLE_PATH_CHECK
6506static int
6507path_check_0(VALUE path)
6508{
6509 struct stat st;
6510 const char *p0 = StringValueCStr(path);
6511 const char *e0;
6512 rb_encoding *enc;
6513 char *p = 0, *s;
6514
6515 if (!rb_is_absolute_path(p0)) {
6516 char *buf = ruby_getcwd();
6517 VALUE newpath;
6518
6519 newpath = rb_str_new2(buf);
6520 xfree(buf);
6521
6522 rb_str_cat2(newpath, "/");
6523 rb_str_cat2(newpath, p0);
6524 path = newpath;
6525 p0 = RSTRING_PTR(path);
6526 }
6527 e0 = p0 + RSTRING_LEN(path);
6528 enc = rb_enc_get(path);
6529 for (;;) {
6530#ifndef S_IWOTH
6531# define S_IWOTH 002
6532#endif
6533 if (STAT(p0, &st) == 0 && S_ISDIR(st.st_mode) && (st.st_mode & S_IWOTH)
6534#ifdef S_ISVTX
6535 && !(p && (st.st_mode & S_ISVTX))
6536#endif
6537 && !access(p0, W_OK)) {
6538 rb_enc_warn(enc, "Insecure world writable dir %s in PATH, mode 0%"
6539#if SIZEOF_DEV_T > SIZEOF_INT
6541#else
6542 "o",
6543#endif
6544 p0, st.st_mode);
6545 if (p) *p = '/';
6546 RB_GC_GUARD(path);
6547 return 0;
6548 }
6549 s = strrdirsep(p0, e0, enc);
6550 if (p) *p = '/';
6551 if (!s || s == p0) return 1;
6552 p = s;
6553 e0 = p;
6554 *p = '\0';
6555 }
6556}
6557#endif
6558
6559int
6560rb_path_check(const char *path)
6561{
6562 rb_warn_deprecated_to_remove_at(3.6, "rb_path_check", NULL);
6563#if ENABLE_PATH_CHECK
6564 const char *p0, *p, *pend;
6565 const char sep = PATH_SEP_CHAR;
6566
6567 if (!path) return 1;
6568
6569 pend = path + strlen(path);
6570 p0 = path;
6571 p = strchr(path, sep);
6572 if (!p) p = pend;
6573
6574 for (;;) {
6575 if (!path_check_0(rb_str_new(p0, p - p0))) {
6576 return 0; /* not safe */
6577 }
6578 p0 = p + 1;
6579 if (p0 > pend) break;
6580 p = strchr(p0, sep);
6581 if (!p) p = pend;
6582 }
6583#endif
6584 return 1;
6585}
6586
6587int
6588ruby_is_fd_loadable(int fd)
6589{
6590#ifdef _WIN32
6591 return 1;
6592#else
6593 struct stat st;
6594
6595 if (fstat(fd, &st) < 0)
6596 return 0;
6597
6598 if (S_ISREG(st.st_mode))
6599 return 1;
6600
6601 if (S_ISFIFO(st.st_mode) || S_ISCHR(st.st_mode))
6602 return -1;
6603
6604 if (S_ISDIR(st.st_mode))
6605 errno = EISDIR;
6606 else
6607 errno = ENXIO;
6608
6609 return 0;
6610#endif
6611}
6612
6613#ifndef _WIN32
6614int
6615rb_file_load_ok(const char *path)
6616{
6617 int ret = 1;
6618 /*
6619 open(2) may block if path is FIFO and it's empty. Let's use O_NONBLOCK.
6620 FIXME: Why O_NDELAY is checked?
6621 */
6622 int mode = (O_RDONLY |
6623#if defined O_NONBLOCK
6624 O_NONBLOCK |
6625#elif defined O_NDELAY
6626 O_NDELAY |
6627#endif
6628 0);
6629 int fd = rb_cloexec_open(path, mode, 0);
6630 if (fd < 0) {
6631 if (!rb_gc_for_fd(errno)) return 0;
6632 fd = rb_cloexec_open(path, mode, 0);
6633 if (fd < 0) return 0;
6634 }
6635 rb_update_max_fd(fd);
6636 ret = ruby_is_fd_loadable(fd);
6637 (void)close(fd);
6638 return ret;
6639}
6640#endif
6641
6642static int
6643is_explicit_relative(const char *path)
6644{
6645 if (*path++ != '.') return 0;
6646 if (*path == '.') path++;
6647 return isdirsep(*path);
6648}
6649
6650static VALUE
6651copy_path_class(VALUE path, VALUE orig)
6652{
6653 int encidx = rb_enc_get_index(orig);
6654 if (encidx == ENCINDEX_ASCII_8BIT || encidx == ENCINDEX_US_ASCII)
6655 encidx = rb_filesystem_encindex();
6656 rb_enc_associate_index(path, encidx);
6657 str_shrink(path);
6658 RBASIC_SET_CLASS(path, rb_obj_class(orig));
6659 OBJ_FREEZE(path);
6660 return path;
6661}
6662
6663int
6664rb_find_file_ext(VALUE *filep, const char *const *ext)
6665{
6666 const char *f = StringValueCStr(*filep);
6667 VALUE fname = *filep, load_path, tmp;
6668 long i, j, fnlen;
6669 int expanded = 0;
6670
6671 if (!ext[0]) return 0;
6672
6673 if (f[0] == '~') {
6674 fname = file_expand_path_1(fname);
6675 f = RSTRING_PTR(fname);
6676 *filep = fname;
6677 expanded = 1;
6678 }
6679
6680 if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) {
6681 if (!expanded) fname = file_expand_path_1(fname);
6682 fnlen = RSTRING_LEN(fname);
6683 for (i=0; ext[i]; i++) {
6684 rb_str_cat2(fname, ext[i]);
6685 if (rb_file_load_ok(RSTRING_PTR(fname))) {
6686 *filep = copy_path_class(fname, *filep);
6687 return (int)(i+1);
6688 }
6689 rb_str_set_len(fname, fnlen);
6690 }
6691 return 0;
6692 }
6693
6694 RB_GC_GUARD(load_path) = rb_get_expanded_load_path();
6695 if (!load_path) return 0;
6696
6697 fname = rb_str_dup(*filep);
6698 RBASIC_CLEAR_CLASS(fname);
6699 fnlen = RSTRING_LEN(fname);
6700 tmp = rb_str_tmp_new(MAXPATHLEN + 2);
6701 rb_enc_associate_index(tmp, rb_usascii_encindex());
6702 for (j=0; ext[j]; j++) {
6703 rb_str_cat2(fname, ext[j]);
6704 for (i = 0; i < RARRAY_LEN(load_path); i++) {
6705 VALUE str = RARRAY_AREF(load_path, i);
6706
6707 RB_GC_GUARD(str) = rb_get_path(str);
6708 if (RSTRING_LEN(str) == 0) continue;
6709 rb_file_expand_path_internal(fname, str, 0, 0, tmp);
6710 if (rb_file_load_ok(RSTRING_PTR(tmp))) {
6711 *filep = copy_path_class(tmp, *filep);
6712 return (int)(j+1);
6713 }
6714 }
6715 rb_str_set_len(fname, fnlen);
6716 }
6717 rb_str_resize(tmp, 0);
6718 RB_GC_GUARD(load_path);
6719 return 0;
6720}
6721
6722VALUE
6723rb_find_file(VALUE path)
6724{
6725 VALUE tmp, load_path;
6726 const char *f = StringValueCStr(path);
6727 int expanded = 0;
6728
6729 if (f[0] == '~') {
6730 tmp = file_expand_path_1(path);
6731 path = copy_path_class(tmp, path);
6732 f = RSTRING_PTR(path);
6733 expanded = 1;
6734 }
6735
6736 if (expanded || rb_is_absolute_path(f) || is_explicit_relative(f)) {
6737 if (!rb_file_load_ok(f)) return 0;
6738 if (!expanded)
6739 path = copy_path_class(file_expand_path_1(path), path);
6740 return path;
6741 }
6742
6743 RB_GC_GUARD(load_path) = rb_get_expanded_load_path();
6744 if (load_path) {
6745 long i;
6746
6747 tmp = rb_str_tmp_new(MAXPATHLEN + 2);
6748 rb_enc_associate_index(tmp, rb_usascii_encindex());
6749 for (i = 0; i < RARRAY_LEN(load_path); i++) {
6750 VALUE str = RARRAY_AREF(load_path, i);
6751 RB_GC_GUARD(str) = rb_get_path(str);
6752 if (RSTRING_LEN(str) > 0) {
6753 rb_file_expand_path_internal(path, str, 0, 0, tmp);
6754 f = RSTRING_PTR(tmp);
6755 if (rb_file_load_ok(f)) goto found;
6756 }
6757 }
6758 rb_str_resize(tmp, 0);
6759 return 0;
6760 }
6761 else {
6762 return 0; /* no path, no load */
6763 }
6764
6765 found:
6766 return copy_path_class(tmp, path);
6767}
6768
6769#define define_filetest_function(name, func, argc) do { \
6770 rb_define_module_function(rb_mFileTest, name, func, argc); \
6771 rb_define_singleton_method(rb_cFile, name, func, argc); \
6772} while(false)
6773
6774const char ruby_null_device[] =
6775#if defined DOSISH
6776 "NUL"
6777#elif defined AMIGA || defined __amigaos__
6778 "NIL"
6779#elif defined __VMS
6780 "NL:"
6781#else
6782 "/dev/null"
6783#endif
6784 ;
6785
6786/*
6787 * A \File object is a representation of a file in the underlying platform.
6788 *
6789 * Class \File extends module FileTest, supporting such singleton methods
6790 * as <tt>File.exist?</tt>.
6791 *
6792 * == About the Examples
6793 *
6794 * Many examples here use these variables:
6795 *
6796 * :include: doc/examples/files.rdoc
6797 *
6798 * == Access Modes
6799 *
6800 * Methods File.new and File.open each create a \File object for a given file path.
6801 *
6802 * === \String Access Modes
6803 *
6804 * Methods File.new and File.open each may take string argument +mode+, which:
6805 *
6806 * - Begins with a 1- or 2-character
6807 * {read/write mode}[rdoc-ref:File@Read-2FWrite+Mode].
6808 * - May also contain a 1-character {data mode}[rdoc-ref:File@Data+Mode].
6809 * - May also contain a 1-character
6810 * {file-create mode}[rdoc-ref:File@File-Create+Mode].
6811 *
6812 * ==== Read/Write Mode
6813 *
6814 * The read/write +mode+ determines:
6815 *
6816 * - Whether the file is to be initially truncated.
6817 *
6818 * - Whether reading is allowed, and if so:
6819 *
6820 * - The initial read position in the file.
6821 * - Where in the file reading can occur.
6822 *
6823 * - Whether writing is allowed, and if so:
6824 *
6825 * - The initial write position in the file.
6826 * - Where in the file writing can occur.
6827 *
6828 * These tables summarize:
6829 *
6830 * Read/Write Modes for Existing File
6831 *
6832 * |------|-----------|----------|----------|----------|-----------|
6833 * | R/W | Initial | | Initial | | Initial |
6834 * | Mode | Truncate? | Read | Read Pos | Write | Write Pos |
6835 * |------|-----------|----------|----------|----------|-----------|
6836 * | 'r' | No | Anywhere | 0 | Error | - |
6837 * | 'w' | Yes | Error | - | Anywhere | 0 |
6838 * | 'a' | No | Error | - | End only | End |
6839 * | 'r+' | No | Anywhere | 0 | Anywhere | 0 |
6840 * | 'w+' | Yes | Anywhere | 0 | Anywhere | 0 |
6841 * | 'a+' | No | Anywhere | End | End only | End |
6842 * |------|-----------|----------|----------|----------|-----------|
6843 *
6844 * Read/Write Modes for \File To Be Created
6845 *
6846 * |------|----------|----------|----------|-----------|
6847 * | R/W | | Initial | | Initial |
6848 * | Mode | Read | Read Pos | Write | Write Pos |
6849 * |------|----------|----------|----------|-----------|
6850 * | 'w' | Error | - | Anywhere | 0 |
6851 * | 'a' | Error | - | End only | 0 |
6852 * | 'w+' | Anywhere | 0 | Anywhere | 0 |
6853 * | 'a+' | Anywhere | 0 | End only | End |
6854 * |------|----------|----------|----------|-----------|
6855 *
6856 * Note that modes <tt>'r'</tt> and <tt>'r+'</tt> are not allowed
6857 * for a non-existent file (exception raised).
6858 *
6859 * In the tables:
6860 *
6861 * - +Anywhere+ means that methods IO#rewind, IO#pos=, and IO#seek
6862 * may be used to change the file's position,
6863 * so that allowed reading or writing may occur anywhere in the file.
6864 * - <tt>End only</tt> means that writing can occur only at end-of-file,
6865 * and that methods IO#rewind, IO#pos=, and IO#seek do not affect writing.
6866 * - +Error+ means that an exception is raised if disallowed reading or writing
6867 * is attempted.
6868 *
6869 * ===== Read/Write Modes for Existing \File
6870 *
6871 * - <tt>'r'</tt>:
6872 *
6873 * - \File is not initially truncated:
6874 *
6875 * f = File.new('t.txt') # => #<File:t.txt>
6876 * f.size == 0 # => false
6877 *
6878 * - File's initial read position is 0:
6879 *
6880 * f.pos # => 0
6881 *
6882 * - \File may be read anywhere; see IO#rewind, IO#pos=, IO#seek:
6883 *
6884 * f.readline # => "First line\n"
6885 * f.readline # => "Second line\n"
6886 *
6887 * f.rewind
6888 * f.readline # => "First line\n"
6889 *
6890 * f.pos = 1
6891 * f.readline # => "irst line\n"
6892 *
6893 * f.seek(1, :CUR)
6894 * f.readline # => "econd line\n"
6895 *
6896 * - Writing is not allowed:
6897 *
6898 * f.write('foo') # Raises IOError.
6899 *
6900 * - <tt>'w'</tt>:
6901 *
6902 * - \File is initially truncated:
6903 *
6904 * path = 't.tmp'
6905 * File.write(path, text)
6906 * f = File.new(path, 'w')
6907 * f.size == 0 # => true
6908 *
6909 * - File's initial write position is 0:
6910 *
6911 * f.pos # => 0
6912 *
6913 * - \File may be written anywhere (even past end-of-file);
6914 * see IO#rewind, IO#pos=, IO#seek:
6915 *
6916 * f.write('foo')
6917 * f.flush
6918 * File.read(path) # => "foo"
6919 * f.pos # => 3
6920 *
6921 * f.write('bar')
6922 * f.flush
6923 * File.read(path) # => "foobar"
6924 * f.pos # => 6
6925 *
6926 * f.rewind
6927 * f.write('baz')
6928 * f.flush
6929 * File.read(path) # => "bazbar"
6930 * f.pos # => 3
6931 *
6932 * f.pos = 3
6933 * f.write('foo')
6934 * f.flush
6935 * File.read(path) # => "bazfoo"
6936 * f.pos # => 6
6937 *
6938 * f.seek(-3, :END)
6939 * f.write('bam')
6940 * f.flush
6941 * File.read(path) # => "bazbam"
6942 * f.pos # => 6
6943 *
6944 * f.pos = 8
6945 * f.write('bah') # Zero padding as needed.
6946 * f.flush
6947 * File.read(path) # => "bazbam\u0000\u0000bah"
6948 * f.pos # => 11
6949 *
6950 * - Reading is not allowed:
6951 *
6952 * f.read # Raises IOError.
6953 *
6954 * - <tt>'a'</tt>:
6955 *
6956 * - \File is not initially truncated:
6957 *
6958 * path = 't.tmp'
6959 * File.write(path, 'foo')
6960 * f = File.new(path, 'a')
6961 * f.size == 0 # => false
6962 *
6963 * - File's initial position is 0 (but is ignored):
6964 *
6965 * f.pos # => 0
6966 *
6967 * - \File may be written only at end-of-file;
6968 * IO#rewind, IO#pos=, IO#seek do not affect writing:
6969 *
6970 * f.write('bar')
6971 * f.flush
6972 * File.read(path) # => "foobar"
6973 * f.write('baz')
6974 * f.flush
6975 * File.read(path) # => "foobarbaz"
6976 *
6977 * f.rewind
6978 * f.write('bat')
6979 * f.flush
6980 * File.read(path) # => "foobarbazbat"
6981 *
6982 * - Reading is not allowed:
6983 *
6984 * f.read # Raises IOError.
6985 *
6986 * - <tt>'r+'</tt>:
6987 *
6988 * - \File is not initially truncated:
6989 *
6990 * path = 't.tmp'
6991 * File.write(path, text)
6992 * f = File.new(path, 'r+')
6993 * f.size == 0 # => false
6994 *
6995 * - File's initial read position is 0:
6996 *
6997 * f.pos # => 0
6998 *
6999 * - \File may be read or written anywhere (even past end-of-file);
7000 * see IO#rewind, IO#pos=, IO#seek:
7001 *
7002 * f.readline # => "First line\n"
7003 * f.readline # => "Second line\n"
7004 *
7005 * f.rewind
7006 * f.readline # => "First line\n"
7007 *
7008 * f.pos = 1
7009 * f.readline # => "irst line\n"
7010 *
7011 * f.seek(1, :CUR)
7012 * f.readline # => "econd line\n"
7013 *
7014 * f.rewind
7015 * f.write('WWW')
7016 * f.flush
7017 * File.read(path)
7018 * # => "WWWst line\nSecond line\nFourth line\nFifth line\n"
7019 *
7020 * f.pos = 10
7021 * f.write('XXX')
7022 * f.flush
7023 * File.read(path)
7024 * # => "WWWst lineXXXecond line\nFourth line\nFifth line\n"
7025 *
7026 * f.seek(-6, :END)
7027 * # => 0
7028 * f.write('YYY')
7029 * # => 3
7030 * f.flush
7031 * # => #<File:t.tmp>
7032 * File.read(path)
7033 * # => "WWWst lineXXXecond line\nFourth line\nFifth YYYe\n"
7034 *
7035 * f.seek(2, :END)
7036 * f.write('ZZZ') # Zero padding as needed.
7037 * f.flush
7038 * File.read(path)
7039 * # => "WWWst lineXXXecond line\nFourth line\nFifth YYYe\n\u0000\u0000ZZZ"
7040 *
7041 *
7042 * - <tt>'a+'</tt>:
7043 *
7044 * - \File is not initially truncated:
7045 *
7046 * path = 't.tmp'
7047 * File.write(path, 'foo')
7048 * f = File.new(path, 'a+')
7049 * f.size == 0 # => false
7050 *
7051 * - File's initial read position is 0:
7052 *
7053 * f.pos # => 0
7054 *
7055 * - \File may be written only at end-of-file;
7056 * IO#rewind, IO#pos=, IO#seek do not affect writing:
7057 *
7058 * f.write('bar')
7059 * f.flush
7060 * File.read(path) # => "foobar"
7061 * f.write('baz')
7062 * f.flush
7063 * File.read(path) # => "foobarbaz"
7064 *
7065 * f.rewind
7066 * f.write('bat')
7067 * f.flush
7068 * File.read(path) # => "foobarbazbat"
7069 *
7070 * - \File may be read anywhere; see IO#rewind, IO#pos=, IO#seek:
7071 *
7072 * f.rewind
7073 * f.read # => "foobarbazbat"
7074 *
7075 * f.pos = 3
7076 * f.read # => "barbazbat"
7077 *
7078 * f.seek(-3, :END)
7079 * f.read # => "bat"
7080 *
7081 * ===== Read/Write Modes for \File To Be Created
7082 *
7083 * Note that modes <tt>'r'</tt> and <tt>'r+'</tt> are not allowed
7084 * for a non-existent file (exception raised).
7085 *
7086 * - <tt>'w'</tt>:
7087 *
7088 * - File's initial write position is 0:
7089 *
7090 * path = 't.tmp'
7091 * FileUtils.rm_f(path)
7092 * f = File.new(path, 'w')
7093 * f.pos # => 0
7094 *
7095 * - \File may be written anywhere (even past end-of-file);
7096 * see IO#rewind, IO#pos=, IO#seek:
7097 *
7098 * f.write('foo')
7099 * f.flush
7100 * File.read(path) # => "foo"
7101 * f.pos # => 3
7102 *
7103 * f.write('bar')
7104 * f.flush
7105 * File.read(path) # => "foobar"
7106 * f.pos # => 6
7107 *
7108 * f.rewind
7109 * f.write('baz')
7110 * f.flush
7111 * File.read(path) # => "bazbar"
7112 * f.pos # => 3
7113 *
7114 * f.pos = 3
7115 * f.write('foo')
7116 * f.flush
7117 * File.read(path) # => "bazfoo"
7118 * f.pos # => 6
7119 *
7120 * f.seek(-3, :END)
7121 * f.write('bam')
7122 * f.flush
7123 * File.read(path) # => "bazbam"
7124 * f.pos # => 6
7125 *
7126 * f.pos = 8
7127 * f.write('bah') # Zero padding as needed.
7128 * f.flush
7129 * File.read(path) # => "bazbam\u0000\u0000bah"
7130 * f.pos # => 11
7131 *
7132 * - Reading is not allowed:
7133 *
7134 * f.read # Raises IOError.
7135 *
7136 * - <tt>'a'</tt>:
7137 *
7138 * - File's initial write position is 0:
7139 *
7140 * path = 't.tmp'
7141 * FileUtils.rm_f(path)
7142 * f = File.new(path, 'a')
7143 * f.pos # => 0
7144 *
7145 * - Writing occurs only at end-of-file:
7146 *
7147 * f.write('foo')
7148 * f.pos # => 3
7149 * f.write('bar')
7150 * f.pos # => 6
7151 * f.flush
7152 * File.read(path) # => "foobar"
7153 *
7154 * f.rewind
7155 * f.write('baz')
7156 * f.flush
7157 * File.read(path) # => "foobarbaz"
7158 *
7159 * - Reading is not allowed:
7160 *
7161 * f.read # Raises IOError.
7162 *
7163 * - <tt>'w+'</tt>:
7164 *
7165 * - File's initial position is 0:
7166 *
7167 * path = 't.tmp'
7168 * FileUtils.rm_f(path)
7169 * f = File.new(path, 'w+')
7170 * f.pos # => 0
7171 *
7172 * - \File may be written anywhere (even past end-of-file);
7173 * see IO#rewind, IO#pos=, IO#seek:
7174 *
7175 * f.write('foo')
7176 * f.flush
7177 * File.read(path) # => "foo"
7178 * f.pos # => 3
7179 *
7180 * f.write('bar')
7181 * f.flush
7182 * File.read(path) # => "foobar"
7183 * f.pos # => 6
7184 *
7185 * f.rewind
7186 * f.write('baz')
7187 * f.flush
7188 * File.read(path) # => "bazbar"
7189 * f.pos # => 3
7190 *
7191 * f.pos = 3
7192 * f.write('foo')
7193 * f.flush
7194 * File.read(path) # => "bazfoo"
7195 * f.pos # => 6
7196 *
7197 * f.seek(-3, :END)
7198 * f.write('bam')
7199 * f.flush
7200 * File.read(path) # => "bazbam"
7201 * f.pos # => 6
7202 *
7203 * f.pos = 8
7204 * f.write('bah') # Zero padding as needed.
7205 * f.flush
7206 * File.read(path) # => "bazbam\u0000\u0000bah"
7207 * f.pos # => 11
7208 *
7209 * - \File may be read anywhere (even past end-of-file);
7210 * see IO#rewind, IO#pos=, IO#seek:
7211 *
7212 * f.rewind
7213 * # => 0
7214 * f.read
7215 * # => "bazbam\u0000\u0000bah"
7216 *
7217 * f.pos = 3
7218 * # => 3
7219 * f.read
7220 * # => "bam\u0000\u0000bah"
7221 *
7222 * f.seek(-3, :END)
7223 * # => 0
7224 * f.read
7225 * # => "bah"
7226 *
7227 * - <tt>'a+'</tt>:
7228 *
7229 * - File's initial write position is 0:
7230 *
7231 * path = 't.tmp'
7232 * FileUtils.rm_f(path)
7233 * f = File.new(path, 'a+')
7234 * f.pos # => 0
7235 *
7236 * - Writing occurs only at end-of-file:
7237 *
7238 * f.write('foo')
7239 * f.pos # => 3
7240 * f.write('bar')
7241 * f.pos # => 6
7242 * f.flush
7243 * File.read(path) # => "foobar"
7244 *
7245 * f.rewind
7246 * f.write('baz')
7247 * f.flush
7248 * File.read(path) # => "foobarbaz"
7249 *
7250 * - \File may be read anywhere (even past end-of-file);
7251 * see IO#rewind, IO#pos=, IO#seek:
7252 *
7253 * f.rewind
7254 * f.read # => "foobarbaz"
7255 *
7256 * f.pos = 3
7257 * f.read # => "barbaz"
7258 *
7259 * f.seek(-3, :END)
7260 * f.read # => "baz"
7261 *
7262 * f.pos = 800
7263 * f.read # => ""
7264 *
7265 * ==== \Data Mode
7266 *
7267 * To specify whether data is to be treated as text or as binary data,
7268 * either of the following may be suffixed to any of the string read/write modes
7269 * above:
7270 *
7271 * - <tt>'t'</tt>: Text data; sets the default external encoding
7272 * to <tt>Encoding::UTF_8</tt>;
7273 * on Windows, enables conversion between EOL and CRLF
7274 * and enables interpreting <tt>0x1A</tt> as an end-of-file marker.
7275 * - <tt>'b'</tt>: Binary data; sets the default external encoding
7276 * to <tt>Encoding::ASCII_8BIT</tt>;
7277 * on Windows, suppresses conversion between EOL and CRLF
7278 * and disables interpreting <tt>0x1A</tt> as an end-of-file marker.
7279 *
7280 * If neither is given, the stream defaults to text data.
7281 *
7282 * Examples:
7283 *
7284 * File.new('t.txt', 'rt')
7285 * File.new('t.dat', 'rb')
7286 *
7287 * When the data mode is specified, the read/write mode may not be omitted,
7288 * and the data mode must precede the file-create mode, if given:
7289 *
7290 * File.new('t.dat', 'b') # Raises an exception.
7291 * File.new('t.dat', 'rxb') # Raises an exception.
7292 *
7293 * ==== \File-Create Mode
7294 *
7295 * The following may be suffixed to any writable string mode above:
7296 *
7297 * - <tt>'x'</tt>: Creates the file if it does not exist;
7298 * raises an exception if the file exists.
7299 *
7300 * Example:
7301 *
7302 * File.new('t.tmp', 'wx')
7303 *
7304 * When the file-create mode is specified, the read/write mode may not be omitted,
7305 * and the file-create mode must follow the data mode:
7306 *
7307 * File.new('t.dat', 'x') # Raises an exception.
7308 * File.new('t.dat', 'rxb') # Raises an exception.
7309 *
7310 * === \Integer Access Modes
7311 *
7312 * When mode is an integer it must be one or more of the following constants,
7313 * which may be combined by the bitwise OR operator <tt>|</tt>:
7314 *
7315 * - +File::RDONLY+: Open for reading only.
7316 * - +File::WRONLY+: Open for writing only.
7317 * - +File::RDWR+: Open for reading and writing.
7318 * - +File::APPEND+: Open for appending only.
7319 *
7320 * Examples:
7321 *
7322 * File.new('t.txt', File::RDONLY)
7323 * File.new('t.tmp', File::RDWR | File::CREAT | File::EXCL)
7324 *
7325 * Note: Method IO#set_encoding does not allow the mode to be specified as an integer.
7326 *
7327 * === File-Create Mode Specified as an \Integer
7328 *
7329 * These constants may also be ORed into the integer mode:
7330 *
7331 * - +File::CREAT+: Create file if it does not exist.
7332 * - +File::EXCL+: Raise an exception if +File::CREAT+ is given and the file exists.
7333 *
7334 * === \Data Mode Specified as an \Integer
7335 *
7336 * \Data mode cannot be specified as an integer.
7337 * When the stream access mode is given as an integer,
7338 * the data mode is always text, never binary.
7339 *
7340 * Note that although there is a constant +File::BINARY+,
7341 * setting its value in an integer stream mode has no effect;
7342 * this is because, as documented in File::Constants,
7343 * the +File::BINARY+ value disables line code conversion,
7344 * but does not change the external encoding.
7345 *
7346 * === Encodings
7347 *
7348 * Any of the string modes above may specify encodings -
7349 * either external encoding only or both external and internal encodings -
7350 * by appending one or both encoding names, separated by colons:
7351 *
7352 * f = File.new('t.dat', 'rb')
7353 * f.external_encoding # => #<Encoding:ASCII-8BIT>
7354 * f.internal_encoding # => nil
7355 * f = File.new('t.dat', 'rb:UTF-16')
7356 * f.external_encoding # => #<Encoding:UTF-16 (dummy)>
7357 * f.internal_encoding # => nil
7358 * f = File.new('t.dat', 'rb:UTF-16:UTF-16')
7359 * f.external_encoding # => #<Encoding:UTF-16 (dummy)>
7360 * f.internal_encoding # => #<Encoding:UTF-16>
7361 * f.close
7362 *
7363 * The numerous encoding names are available in array Encoding.name_list:
7364 *
7365 * Encoding.name_list.take(3) # => ["ASCII-8BIT", "UTF-8", "US-ASCII"]
7366 *
7367 * When the external encoding is set, strings read are tagged by that encoding
7368 * when reading, and strings written are converted to that encoding when
7369 * writing.
7370 *
7371 * When both external and internal encodings are set,
7372 * strings read are converted from external to internal encoding,
7373 * and strings written are converted from internal to external encoding.
7374 * For further details about transcoding input and output,
7375 * see {Encodings}[rdoc-ref:encodings.rdoc@Encodings].
7376 *
7377 * If the external encoding is <tt>'BOM|UTF-8'</tt>, <tt>'BOM|UTF-16LE'</tt>
7378 * or <tt>'BOM|UTF16-BE'</tt>,
7379 * Ruby checks for a Unicode BOM in the input document
7380 * to help determine the encoding.
7381 * For UTF-16 encodings the file open mode must be binary.
7382 * If the BOM is found,
7383 * it is stripped and the external encoding from the BOM is used.
7384 *
7385 * Note that the BOM-style encoding option is case insensitive,
7386 * so <tt>'bom|utf-8'</tt> is also valid.
7387 *
7388 * == \File Permissions
7389 *
7390 * A \File object has _permissions_, an octal integer representing
7391 * the permissions of an actual file in the underlying platform.
7392 *
7393 * Note that file permissions are quite different from the _mode_
7394 * of a file stream (\File object).
7395 *
7396 * In a \File object, the permissions are available thus,
7397 * where method +mode+, despite its name, returns permissions:
7398 *
7399 * f = File.new('t.txt')
7400 * f.lstat.mode.to_s(8) # => "100644"
7401 *
7402 * On a Unix-based operating system,
7403 * the three low-order octal digits represent the permissions
7404 * for owner (6), group (4), and world (4).
7405 * The triplet of bits in each octal digit represent, respectively,
7406 * read, write, and execute permissions.
7407 *
7408 * Permissions <tt>0644</tt> thus represent read-write access for owner
7409 * and read-only access for group and world.
7410 * See man pages {open(2)}[https://www.unix.com/man-page/bsd/2/open]
7411 * and {chmod(2)}[https://www.unix.com/man-page/bsd/2/chmod].
7412 *
7413 * For a directory, the meaning of the execute bit changes:
7414 * when set, the directory can be searched.
7415 *
7416 * Higher-order bits in permissions may indicate the type of file
7417 * (plain, directory, pipe, socket, etc.) and various other special features.
7418 *
7419 * On non-Posix operating systems, permissions may include only read-only or read-write,
7420 * in which case, the remaining permission will resemble typical values.
7421 * On Windows, for instance, the default permissions are <code>0644</code>;
7422 * The only change that can be made is to make the file
7423 * read-only, which is reported as <code>0444</code>.
7424 *
7425 * For a method that actually creates a file in the underlying platform
7426 * (as opposed to merely creating a \File object),
7427 * permissions may be specified:
7428 *
7429 * File.new('t.tmp', File::CREAT, 0644)
7430 * File.new('t.tmp', File::CREAT, 0444)
7431 *
7432 * Permissions may also be changed:
7433 *
7434 * f = File.new('t.tmp', File::CREAT, 0444)
7435 * f.chmod(0644)
7436 * f.chmod(0444)
7437 *
7438 * == \File \Constants
7439 *
7440 * Various constants for use in \File and IO methods
7441 * may be found in module File::Constants;
7442 * an array of their names is returned by <tt>File::Constants.constants</tt>.
7443 *
7444 * == What's Here
7445 *
7446 * First, what's elsewhere. Class \File:
7447 *
7448 * - Inherits from {class IO}[rdoc-ref:IO@What-27s+Here],
7449 * in particular, methods for creating, reading, and writing files
7450 * - Includes module FileTest,
7451 * which provides dozens of additional methods.
7452 *
7453 * Here, class \File provides methods that are useful for:
7454 *
7455 * - {Creating}[rdoc-ref:File@Creating]
7456 * - {Querying}[rdoc-ref:File@Querying]
7457 * - {Settings}[rdoc-ref:File@Settings]
7458 * - {Other}[rdoc-ref:File@Other]
7459 *
7460 * === Creating
7461 *
7462 * - ::new: Opens the file at the given path; returns the file.
7463 * - ::open: Same as ::new, but when given a block will yield the file to the block,
7464 * and close the file upon exiting the block.
7465 * - ::link: Creates a new name for an existing file using a hard link.
7466 * - ::mkfifo: Returns the FIFO file created at the given path.
7467 * - ::symlink: Creates a symbolic link for the given file path.
7468 *
7469 * === Querying
7470 *
7471 * _Paths_
7472 *
7473 * - ::absolute_path: Returns the absolute file path for the given path.
7474 * - ::absolute_path?: Returns whether the given path is the absolute file path.
7475 * - ::basename: Returns the last component of the given file path.
7476 * - ::dirname: Returns all but the last component of the given file path.
7477 * - ::expand_path: Returns the absolute file path for the given path,
7478 * expanding <tt>~</tt> for a home directory.
7479 * - ::extname: Returns the file extension for the given file path.
7480 * - ::fnmatch? (aliased as ::fnmatch): Returns whether the given file path
7481 * matches the given pattern.
7482 * - ::join: Joins path components into a single path string.
7483 * - ::path: Returns the string representation of the given path.
7484 * - ::readlink: Returns the path to the file at the given symbolic link.
7485 * - ::realdirpath: Returns the real path for the given file path,
7486 * where the last component need not exist.
7487 * - ::realpath: Returns the real path for the given file path,
7488 * where all components must exist.
7489 * - ::split: Returns an array of two strings: the directory name and basename
7490 * of the file at the given path.
7491 * - #path (aliased as #to_path): Returns the string representation of the given path.
7492 *
7493 * _Times_
7494 *
7495 * - ::atime: Returns a Time for the most recent access to the given file.
7496 * - ::birthtime: Returns a Time for the creation of the given file.
7497 * - ::ctime: Returns a Time for the metadata change of the given file.
7498 * - ::mtime: Returns a Time for the most recent data modification to
7499 * the content of the given file.
7500 * - #atime: Returns a Time for the most recent access to +self+.
7501 * - #birthtime: Returns a Time the creation for +self+.
7502 * - #ctime: Returns a Time for the metadata change of +self+.
7503 * - #mtime: Returns a Time for the most recent data modification
7504 * to the content of +self+.
7505 *
7506 * _Types_
7507 *
7508 * - ::blockdev?: Returns whether the file at the given path is a block device.
7509 * - ::chardev?: Returns whether the file at the given path is a character device.
7510 * - ::directory?: Returns whether the file at the given path is a directory.
7511 * - ::executable?: Returns whether the file at the given path is executable
7512 * by the effective user and group of the current process.
7513 * - ::executable_real?: Returns whether the file at the given path is executable
7514 * by the real user and group of the current process.
7515 * - ::exist?: Returns whether the file at the given path exists.
7516 * - ::file?: Returns whether the file at the given path is a regular file.
7517 * - ::ftype: Returns a string giving the type of the file at the given path.
7518 * - ::grpowned?: Returns whether the effective group of the current process
7519 * owns the file at the given path.
7520 * - ::identical?: Returns whether the files at two given paths are identical.
7521 * - ::lstat: Returns the File::Stat object for the last symbolic link
7522 * in the given path.
7523 * - ::owned?: Returns whether the effective user of the current process
7524 * owns the file at the given path.
7525 * - ::pipe?: Returns whether the file at the given path is a pipe.
7526 * - ::readable?: Returns whether the file at the given path is readable
7527 * by the effective user and group of the current process.
7528 * - ::readable_real?: Returns whether the file at the given path is readable
7529 * by the real user and group of the current process.
7530 * - ::setgid?: Returns whether the setgid bit is set for the file at the given path.
7531 * - ::setuid?: Returns whether the setuid bit is set for the file at the given path.
7532 * - ::socket?: Returns whether the file at the given path is a socket.
7533 * - ::stat: Returns the File::Stat object for the file at the given path.
7534 * - ::sticky?: Returns whether the file at the given path has its sticky bit set.
7535 * - ::symlink?: Returns whether the file at the given path is a symbolic link.
7536 * - ::umask: Returns the umask value for the current process.
7537 * - ::world_readable?: Returns whether the file at the given path is readable
7538 * by others.
7539 * - ::world_writable?: Returns whether the file at the given path is writable
7540 * by others.
7541 * - ::writable?: Returns whether the file at the given path is writable
7542 * by the effective user and group of the current process.
7543 * - ::writable_real?: Returns whether the file at the given path is writable
7544 * by the real user and group of the current process.
7545 * - #lstat: Returns the File::Stat object for the last symbolic link
7546 * in the path for +self+.
7547 *
7548 * _Contents_
7549 *
7550 * - ::empty? (aliased as ::zero?): Returns whether the file at the given path
7551 * exists and is empty.
7552 * - ::size: Returns the size (bytes) of the file at the given path.
7553 * - ::size?: Returns +nil+ if there is no file at the given path,
7554 * or if that file is empty; otherwise returns the file size (bytes).
7555 * - #size: Returns the size (bytes) of +self+.
7556 *
7557 * === Settings
7558 *
7559 * - ::chmod: Changes permissions of the file at the given path.
7560 * - ::chown: Change ownership of the file at the given path.
7561 * - ::lchmod: Changes permissions of the last symbolic link in the given path.
7562 * - ::lchown: Change ownership of the last symbolic in the given path.
7563 * - ::lutime: For each given file path, sets the access time and modification time
7564 * of the last symbolic link in the path.
7565 * - ::rename: Moves the file at one given path to another given path.
7566 * - ::utime: Sets the access time and modification time of each file
7567 * at the given paths.
7568 * - #flock: Locks or unlocks +self+.
7569 *
7570 * === Other
7571 *
7572 * - ::truncate: Truncates the file at the given file path to the given size.
7573 * - ::unlink (aliased as ::delete): Deletes the file for each given file path.
7574 * - #truncate: Truncates +self+ to the given size.
7575 *
7576 */
7577
7578void
7579Init_File(void)
7580{
7581#if defined(__APPLE__) && defined(HAVE_WORKING_FORK)
7582 rb_CFString_class_initialize_before_fork();
7583#endif
7584
7585 VALUE separator;
7586
7587 rb_mFileTest = rb_define_module("FileTest");
7588 rb_cFile = rb_define_class("File", rb_cIO);
7589
7590 define_filetest_function("directory?", rb_file_directory_p, 1);
7591 define_filetest_function("exist?", rb_file_exist_p, 1);
7592 define_filetest_function("readable?", rb_file_readable_p, 1);
7593 define_filetest_function("readable_real?", rb_file_readable_real_p, 1);
7594 define_filetest_function("world_readable?", rb_file_world_readable_p, 1);
7595 define_filetest_function("writable?", rb_file_writable_p, 1);
7596 define_filetest_function("writable_real?", rb_file_writable_real_p, 1);
7597 define_filetest_function("world_writable?", rb_file_world_writable_p, 1);
7598 define_filetest_function("executable?", rb_file_executable_p, 1);
7599 define_filetest_function("executable_real?", rb_file_executable_real_p, 1);
7600 define_filetest_function("file?", rb_file_file_p, 1);
7601 define_filetest_function("zero?", rb_file_zero_p, 1);
7602 define_filetest_function("empty?", rb_file_zero_p, 1);
7603 define_filetest_function("size?", rb_file_size_p, 1);
7604 define_filetest_function("size", rb_file_s_size, 1);
7605 define_filetest_function("owned?", rb_file_owned_p, 1);
7606 define_filetest_function("grpowned?", rb_file_grpowned_p, 1);
7607
7608 define_filetest_function("pipe?", rb_file_pipe_p, 1);
7609 define_filetest_function("symlink?", rb_file_symlink_p, 1);
7610 define_filetest_function("socket?", rb_file_socket_p, 1);
7611
7612 define_filetest_function("blockdev?", rb_file_blockdev_p, 1);
7613 define_filetest_function("chardev?", rb_file_chardev_p, 1);
7614
7615 define_filetest_function("setuid?", rb_file_suid_p, 1);
7616 define_filetest_function("setgid?", rb_file_sgid_p, 1);
7617 define_filetest_function("sticky?", rb_file_sticky_p, 1);
7618
7619 define_filetest_function("identical?", rb_file_identical_p, 2);
7620
7621 rb_define_singleton_method(rb_cFile, "stat", rb_file_s_stat, 1);
7622 rb_define_singleton_method(rb_cFile, "lstat", rb_file_s_lstat, 1);
7623 rb_define_singleton_method(rb_cFile, "ftype", rb_file_s_ftype, 1);
7624
7625 rb_define_singleton_method(rb_cFile, "atime", rb_file_s_atime, 1);
7626 rb_define_singleton_method(rb_cFile, "mtime", rb_file_s_mtime, 1);
7627 rb_define_singleton_method(rb_cFile, "ctime", rb_file_s_ctime, 1);
7628 rb_define_singleton_method(rb_cFile, "birthtime", rb_file_s_birthtime, 1);
7629
7630 rb_define_singleton_method(rb_cFile, "utime", rb_file_s_utime, -1);
7631 rb_define_singleton_method(rb_cFile, "chmod", rb_file_s_chmod, -1);
7632 rb_define_singleton_method(rb_cFile, "chown", rb_file_s_chown, -1);
7633 rb_define_singleton_method(rb_cFile, "lchmod", rb_file_s_lchmod, -1);
7634 rb_define_singleton_method(rb_cFile, "lchown", rb_file_s_lchown, -1);
7635 rb_define_singleton_method(rb_cFile, "lutime", rb_file_s_lutime, -1);
7636
7637 rb_define_singleton_method(rb_cFile, "link", rb_file_s_link, 2);
7638 rb_define_singleton_method(rb_cFile, "symlink", rb_file_s_symlink, 2);
7639 rb_define_singleton_method(rb_cFile, "readlink", rb_file_s_readlink, 1);
7640
7641 rb_define_singleton_method(rb_cFile, "unlink", rb_file_s_unlink, -1);
7642 rb_define_singleton_method(rb_cFile, "delete", rb_file_s_unlink, -1);
7643 rb_define_singleton_method(rb_cFile, "rename", rb_file_s_rename, 2);
7644 rb_define_singleton_method(rb_cFile, "umask", rb_file_s_umask, -1);
7645 rb_define_singleton_method(rb_cFile, "truncate", rb_file_s_truncate, 2);
7646 rb_define_singleton_method(rb_cFile, "mkfifo", rb_file_s_mkfifo, -1);
7647 rb_define_singleton_method(rb_cFile, "expand_path", s_expand_path, -1);
7648 rb_define_singleton_method(rb_cFile, "absolute_path", s_absolute_path, -1);
7649 rb_define_singleton_method(rb_cFile, "absolute_path?", s_absolute_path_p, 1);
7650 rb_define_singleton_method(rb_cFile, "realpath", rb_file_s_realpath, -1);
7651 rb_define_singleton_method(rb_cFile, "realdirpath", rb_file_s_realdirpath, -1);
7652 rb_define_singleton_method(rb_cFile, "basename", rb_file_s_basename, -1);
7653 rb_define_singleton_method(rb_cFile, "dirname", rb_file_s_dirname, -1);
7654 rb_define_singleton_method(rb_cFile, "extname", rb_file_s_extname, 1);
7655 rb_define_singleton_method(rb_cFile, "path", rb_file_s_path, 1);
7656
7657 separator = rb_fstring_lit("/");
7658 /* separates directory parts in path */
7659 rb_define_const(rb_cFile, "Separator", separator);
7660 /* separates directory parts in path */
7661 rb_define_const(rb_cFile, "SEPARATOR", separator);
7662 rb_define_singleton_method(rb_cFile, "split", rb_file_s_split, 1);
7663 rb_define_singleton_method(rb_cFile, "join", rb_file_s_join, -2);
7664
7665#ifdef DOSISH
7666 /* platform specific alternative separator */
7667 rb_define_const(rb_cFile, "ALT_SEPARATOR", rb_obj_freeze(rb_usascii_str_new2(file_alt_separator)));
7668#else
7669 rb_define_const(rb_cFile, "ALT_SEPARATOR", Qnil);
7670#endif
7671 /* path list separator */
7672 rb_define_const(rb_cFile, "PATH_SEPARATOR", rb_fstring_cstr(PATH_SEP));
7673
7674 rb_define_method(rb_cIO, "stat", rb_io_stat, 0); /* this is IO's method */
7675 rb_define_method(rb_cFile, "lstat", rb_file_lstat, 0);
7676
7677 rb_define_method(rb_cFile, "atime", rb_file_atime, 0);
7678 rb_define_method(rb_cFile, "mtime", rb_file_mtime, 0);
7679 rb_define_method(rb_cFile, "ctime", rb_file_ctime, 0);
7680 rb_define_method(rb_cFile, "birthtime", rb_file_birthtime, 0);
7681 rb_define_method(rb_cFile, "size", file_size, 0);
7682
7683 rb_define_method(rb_cFile, "chmod", rb_file_chmod, 1);
7684 rb_define_method(rb_cFile, "chown", rb_file_chown, 2);
7685 rb_define_method(rb_cFile, "truncate", rb_file_truncate, 1);
7686
7687 rb_define_method(rb_cFile, "flock", rb_file_flock, 1);
7688
7689 /*
7690 * Document-module: File::Constants
7691 *
7692 * Module +File::Constants+ defines file-related constants.
7693 *
7694 * There are two families of constants here:
7695 *
7696 * - Those having to do with {file access}[rdoc-ref:File::Constants@File+Access].
7697 * - Those having to do with {filename globbing}[rdoc-ref:File::Constants@Filename+Globbing+Constants+-28File-3A-3AFNM_-2A-29].
7698 *
7699 * \File constants defined for the local process may be retrieved
7700 * with method File::Constants.constants:
7701 *
7702 * File::Constants.constants.take(5)
7703 * # => [:RDONLY, :WRONLY, :RDWR, :APPEND, :CREAT]
7704 *
7705 * == \File Access
7706 *
7707 * \File-access constants may be used with optional argument +mode+ in calls
7708 * to the following methods:
7709 *
7710 * - File.new.
7711 * - File.open.
7712 * - IO.for_fd.
7713 * - IO.new.
7714 * - IO.open.
7715 * - IO.popen.
7716 * - IO.reopen.
7717 * - IO.sysopen.
7718 * - StringIO.new.
7719 * - StringIO.open.
7720 * - StringIO#reopen.
7721 *
7722 * === Read/Write Access
7723 *
7724 * Read-write access for a stream
7725 * may be specified by a file-access constant.
7726 *
7727 * The constant may be specified as part of a bitwise OR of other such constants.
7728 *
7729 * Any combination of the constants in this section may be specified.
7730 *
7731 * ==== File::RDONLY
7732 *
7733 * Flag File::RDONLY specifies the stream should be opened for reading only:
7734 *
7735 * filepath = '/tmp/t.tmp'
7736 * f = File.new(filepath, File::RDONLY)
7737 * f.write('Foo') # Raises IOError (not opened for writing).
7738 *
7739 * ==== File::WRONLY
7740 *
7741 * Flag File::WRONLY specifies that the stream should be opened for writing only:
7742 *
7743 * f = File.new(filepath, File::WRONLY)
7744 * f.read # Raises IOError (not opened for reading).
7745 *
7746 * ==== File::RDWR
7747 *
7748 * Flag File::RDWR specifies that the stream should be opened
7749 * for both reading and writing:
7750 *
7751 * f = File.new(filepath, File::RDWR)
7752 * f.write('Foo') # => 3
7753 * f.rewind # => 0
7754 * f.read # => "Foo"
7755 *
7756 * === \File Positioning
7757 *
7758 * ==== File::APPEND
7759 *
7760 * Flag File::APPEND specifies that the stream should be opened
7761 * in append mode.
7762 *
7763 * Before each write operation, the position is set to end-of-stream.
7764 * The modification of the position and the following write operation
7765 * are performed as a single atomic step.
7766 *
7767 * ==== File::TRUNC
7768 *
7769 * Flag File::TRUNC specifies that the stream should be truncated
7770 * at its beginning.
7771 * If the file exists and is successfully opened for writing,
7772 * it is to be truncated to position zero;
7773 * its ctime and mtime are updated.
7774 *
7775 * There is no effect on a FIFO special file or a terminal device.
7776 * The effect on other file types is implementation-defined.
7777 * The result of using File::TRUNC with File::RDONLY is undefined.
7778 *
7779 * === Creating and Preserving
7780 *
7781 * ==== File::CREAT
7782 *
7783 * Flag File::CREAT specifies that the stream should be created
7784 * if it does not already exist.
7785 *
7786 * If the file exists:
7787 *
7788 * - Raise an exception if File::EXCL is also specified.
7789 * - Otherwise, do nothing.
7790 *
7791 * If the file does not exist, then it is created.
7792 * Upon successful completion, the atime, ctime, and mtime of the file are updated,
7793 * and the ctime and mtime of the parent directory are updated.
7794 *
7795 * ==== File::EXCL
7796 *
7797 * Flag File::EXCL specifies that the stream should not already exist;
7798 * If flags File::CREAT and File::EXCL are both specified
7799 * and the stream already exists, an exception is raised.
7800 *
7801 * The check for the existence and creation of the file is performed as an
7802 * atomic operation.
7803 *
7804 * If both File::EXCL and File::CREAT are specified and the path names a symbolic link,
7805 * an exception is raised regardless of the contents of the symbolic link.
7806 *
7807 * If File::EXCL is specified and File::CREAT is not specified,
7808 * the result is undefined.
7809 *
7810 * === POSIX \File \Constants
7811 *
7812 * Some file-access constants are defined only on POSIX-compliant systems;
7813 * those are:
7814 *
7815 * - File::SYNC.
7816 * - File::DSYNC.
7817 * - File::RSYNC.
7818 * - File::DIRECT.
7819 * - File::NOATIME.
7820 * - File::NOCTTY.
7821 * - File::NOFOLLOW.
7822 * - File::TMPFILE.
7823 *
7824 * ==== File::SYNC, File::RSYNC, and File::DSYNC
7825 *
7826 * Flag File::SYNC, File::RSYNC, or File::DSYNC
7827 * specifies synchronization of I/O operations with the underlying file system.
7828 *
7829 * These flags are valid only for POSIX-compliant systems.
7830 *
7831 * - File::SYNC specifies that all write operations (both data and metadata)
7832 * are immediately to be flushed to the underlying storage device.
7833 * This means that the data is written to the storage device,
7834 * and the file's metadata (e.g., file size, timestamps, permissions)
7835 * are also synchronized.
7836 * This guarantees that data is safely stored on the storage medium
7837 * before returning control to the calling program.
7838 * This flag can have a significant impact on performance
7839 * since it requires synchronous writes, which can be slower
7840 * compared to asynchronous writes.
7841 *
7842 * - File::RSYNC specifies that any read operations on the file will not return
7843 * until all outstanding write operations
7844 * (those that have been issued but not completed) are also synchronized.
7845 * This is useful when you want to read the most up-to-date data,
7846 * which may still be in the process of being written.
7847 *
7848 * - File::DSYNC specifies that all _data_ write operations
7849 * are immediately to be flushed to the underlying storage device;
7850 * this differs from File::SYNC, which requires that _metadata_
7851 * also be synchronized.
7852 *
7853 * Note that the behavior of these flags may vary slightly
7854 * depending on the operating system and filesystem being used.
7855 * Additionally, using these flags can have an impact on performance
7856 * due to the synchronous nature of the I/O operations,
7857 * so they should be used judiciously,
7858 * especially in performance-critical applications.
7859 *
7860 * ==== File::NOCTTY
7861 *
7862 * Flag File::NOCTTY specifies that if the stream is a terminal device,
7863 * that device does not become the controlling terminal for the process.
7864 *
7865 * Defined only for POSIX-compliant systems.
7866 *
7867 * ==== File::DIRECT
7868 *
7869 * Flag File::DIRECT requests that cache effects of the I/O to and from the stream
7870 * be minimized.
7871 *
7872 * Defined only for POSIX-compliant systems.
7873 *
7874 * ==== File::NOATIME
7875 *
7876 * Flag File::NOATIME specifies that act of opening the stream
7877 * should not modify its access time (atime).
7878 *
7879 * Defined only for POSIX-compliant systems.
7880 *
7881 * ==== File::NOFOLLOW
7882 *
7883 * Flag File::NOFOLLOW specifies that if path is a symbolic link,
7884 * it should not be followed.
7885 *
7886 * Defined only for POSIX-compliant systems.
7887 *
7888 * ==== File::TMPFILE
7889 *
7890 * Flag File::TMPFILE specifies that the opened stream
7891 * should be a new temporary file.
7892 *
7893 * Defined only for POSIX-compliant systems.
7894 *
7895 * === Other File-Access \Constants
7896 *
7897 * ==== File::NONBLOCK
7898 *
7899 * When possible, the file is opened in nonblocking mode.
7900 * Neither the open operation nor any subsequent I/O operations on
7901 * the file will cause the calling process to wait.
7902 *
7903 * ==== File::BINARY
7904 *
7905 * Flag File::BINARY specifies that the stream is to be accessed in binary mode.
7906 *
7907 * ==== File::SHARE_DELETE
7908 *
7909 * Flag File::SHARE_DELETE enables other processes to open the stream
7910 * with delete access.
7911 *
7912 * Windows only.
7913 *
7914 * If the stream is opened for (local) delete access without File::SHARE_DELETE,
7915 * and another process attempts to open it with delete access,
7916 * the attempt fails and the stream is not opened for that process.
7917 *
7918 * == Locking
7919 *
7920 * Four file constants relate to stream locking;
7921 * see File#flock:
7922 *
7923 * ==== File::LOCK_EX
7924 *
7925 * Flag File::LOCK_EX specifies an exclusive lock;
7926 * only one process a a time may lock the stream.
7927 *
7928 * ==== File::LOCK_NB
7929 *
7930 * Flag File::LOCK_NB specifies non-blocking locking for the stream;
7931 * may be combined with File::LOCK_EX or File::LOCK_SH.
7932 *
7933 * ==== File::LOCK_SH
7934 *
7935 * Flag File::LOCK_SH specifies that multiple processes may lock
7936 * the stream at the same time.
7937 *
7938 * ==== File::LOCK_UN
7939 *
7940 * Flag File::LOCK_UN specifies that the stream is not to be locked.
7941 *
7942 * == Filename Globbing \Constants (File::FNM_*)
7943 *
7944 * Filename-globbing constants may be used with optional argument +flags+
7945 * in calls to the following methods:
7946 *
7947 * - Dir.glob.
7948 * - File.fnmatch.
7949 * - Pathname#fnmatch.
7950 * - Pathname.glob.
7951 * - Pathname#glob.
7952 *
7953 * The constants are:
7954 *
7955 * ==== File::FNM_CASEFOLD
7956 *
7957 * Flag File::FNM_CASEFOLD makes patterns case insensitive
7958 * for File.fnmatch (but not Dir.glob).
7959 *
7960 * ==== File::FNM_DOTMATCH
7961 *
7962 * Flag File::FNM_DOTMATCH makes the <tt>'*'</tt> pattern
7963 * match a filename starting with <tt>'.'</tt>.
7964 *
7965 * ==== File::FNM_EXTGLOB
7966 *
7967 * Flag File::FNM_EXTGLOB enables pattern <tt>'{_a_,_b_}'</tt>,
7968 * which matches pattern '_a_' and pattern '_b_';
7969 * behaves like
7970 * a {regexp union}[rdoc-ref:Regexp.union]
7971 * (e.g., <tt>'(?:_a_|_b_)'</tt>):
7972 *
7973 * pattern = '{LEGAL,BSDL}'
7974 * Dir.glob(pattern) # => ["LEGAL", "BSDL"]
7975 * Pathname.glob(pattern) # => [#<Pathname:LEGAL>, #<Pathname:BSDL>]
7976 * pathname.glob(pattern) # => [#<Pathname:LEGAL>, #<Pathname:BSDL>]
7977 *
7978 * ==== File::FNM_NOESCAPE
7979 *
7980 * Flag File::FNM_NOESCAPE disables <tt>'\'</tt> escaping.
7981 *
7982 * ==== File::FNM_PATHNAME
7983 *
7984 * Flag File::FNM_PATHNAME specifies that patterns <tt>'*'</tt> and <tt>'?'</tt>
7985 * do not match the directory separator
7986 * (the value of constant File::SEPARATOR).
7987 *
7988 * ==== File::FNM_SHORTNAME
7989 *
7990 * Flag File::FNM_SHORTNAME allows patterns to match short names if they exist.
7991 *
7992 * Windows only.
7993 *
7994 * ==== File::FNM_SYSCASE
7995 *
7996 * Flag File::FNM_SYSCASE specifies that case sensitivity
7997 * is the same as in the underlying operating system;
7998 * effective for File.fnmatch, but not Dir.glob.
7999 *
8000 * == Other \Constants
8001 *
8002 * ==== File::NULL
8003 *
8004 * Flag File::NULL contains the string value of the null device:
8005 *
8006 * - On a Unix-like OS, <tt>'/dev/null'</tt>.
8007 * - On Windows, <tt>'NUL'</tt>.
8008 *
8009 */
8010 rb_mFConst = rb_define_module_under(rb_cFile, "Constants");
8011 rb_include_module(rb_cIO, rb_mFConst);
8012 /* {File::RDONLY}[rdoc-ref:File::Constants@File-3A-3ARDONLY] */
8013 rb_define_const(rb_mFConst, "RDONLY", INT2FIX(O_RDONLY));
8014 /* {File::WRONLY}[rdoc-ref:File::Constants@File-3A-3AWRONLY] */
8015 rb_define_const(rb_mFConst, "WRONLY", INT2FIX(O_WRONLY));
8016 /* {File::RDWR}[rdoc-ref:File::Constants@File-3A-3ARDWR] */
8017 rb_define_const(rb_mFConst, "RDWR", INT2FIX(O_RDWR));
8018 /* {File::APPEND}[rdoc-ref:File::Constants@File-3A-3AAPPEND] */
8019 rb_define_const(rb_mFConst, "APPEND", INT2FIX(O_APPEND));
8020 /* {File::CREAT}[rdoc-ref:File::Constants@File-3A-3ACREAT] */
8021 rb_define_const(rb_mFConst, "CREAT", INT2FIX(O_CREAT));
8022 /* {File::EXCL}[rdoc-ref:File::Constants@File-3A-3AEXCL] */
8023 rb_define_const(rb_mFConst, "EXCL", INT2FIX(O_EXCL));
8024#if defined(O_NDELAY) || defined(O_NONBLOCK)
8025# ifndef O_NONBLOCK
8026# define O_NONBLOCK O_NDELAY
8027# endif
8028 /* {File::NONBLOCK}[rdoc-ref:File::Constants@File-3A-3ANONBLOCK] */
8029 rb_define_const(rb_mFConst, "NONBLOCK", INT2FIX(O_NONBLOCK));
8030#endif
8031 /* {File::TRUNC}[rdoc-ref:File::Constants@File-3A-3ATRUNC] */
8032 rb_define_const(rb_mFConst, "TRUNC", INT2FIX(O_TRUNC));
8033#ifdef O_NOCTTY
8034 /* {File::NOCTTY}[rdoc-ref:File::Constants@File-3A-3ANOCTTY] */
8035 rb_define_const(rb_mFConst, "NOCTTY", INT2FIX(O_NOCTTY));
8036#endif
8037#ifndef O_BINARY
8038# define O_BINARY 0
8039#endif
8040 /* {File::BINARY}[rdoc-ref:File::Constants@File-3A-3ABINARY] */
8041 rb_define_const(rb_mFConst, "BINARY", INT2FIX(O_BINARY));
8042#ifndef O_SHARE_DELETE
8043# define O_SHARE_DELETE 0
8044#endif
8045 /* {File::SHARE_DELETE}[rdoc-ref:File::Constants@File-3A-3ASHARE_DELETE] */
8046 rb_define_const(rb_mFConst, "SHARE_DELETE", INT2FIX(O_SHARE_DELETE));
8047#ifdef O_SYNC
8048 /* {File::SYNC}[rdoc-ref:File::Constants@File-3A-3ASYNC-2C+File-3A-3ARSYNC-2C+and+File-3A-3ADSYNC] */
8049 rb_define_const(rb_mFConst, "SYNC", INT2FIX(O_SYNC));
8050#endif
8051#ifdef O_DSYNC
8052 /* {File::DSYNC}[rdoc-ref:File::Constants@File-3A-3ASYNC-2C+File-3A-3ARSYNC-2C+and+File-3A-3ADSYNC] */
8053 rb_define_const(rb_mFConst, "DSYNC", INT2FIX(O_DSYNC));
8054#endif
8055#ifdef O_RSYNC
8056 /* {File::RSYNC}[rdoc-ref:File::Constants@File-3A-3ASYNC-2C+File-3A-3ARSYNC-2C+and+File-3A-3ADSYNC] */
8057 rb_define_const(rb_mFConst, "RSYNC", INT2FIX(O_RSYNC));
8058#endif
8059#ifdef O_NOFOLLOW
8060 /* {File::NOFOLLOW}[rdoc-ref:File::Constants@File-3A-3ANOFOLLOW] */
8061 rb_define_const(rb_mFConst, "NOFOLLOW", INT2FIX(O_NOFOLLOW)); /* FreeBSD, Linux */
8062#endif
8063#ifdef O_NOATIME
8064 /* {File::NOATIME}[rdoc-ref:File::Constants@File-3A-3ANOATIME] */
8065 rb_define_const(rb_mFConst, "NOATIME", INT2FIX(O_NOATIME)); /* Linux */
8066#endif
8067#ifdef O_DIRECT
8068 /* {File::DIRECT}[rdoc-ref:File::Constants@File-3A-3ADIRECT] */
8069 rb_define_const(rb_mFConst, "DIRECT", INT2FIX(O_DIRECT));
8070#endif
8071#ifdef O_TMPFILE
8072 /* {File::TMPFILE}[rdoc-ref:File::Constants@File-3A-3ATMPFILE] */
8073 rb_define_const(rb_mFConst, "TMPFILE", INT2FIX(O_TMPFILE));
8074#endif
8075
8076 /* {File::LOCK_SH}[rdoc-ref:File::Constants@File-3A-3ALOCK_SH] */
8077 rb_define_const(rb_mFConst, "LOCK_SH", INT2FIX(LOCK_SH));
8078 /* {File::LOCK_EX}[rdoc-ref:File::Constants@File-3A-3ALOCK_EX] */
8079 rb_define_const(rb_mFConst, "LOCK_EX", INT2FIX(LOCK_EX));
8080 /* {File::LOCK_UN}[rdoc-ref:File::Constants@File-3A-3ALOCK_UN] */
8081 rb_define_const(rb_mFConst, "LOCK_UN", INT2FIX(LOCK_UN));
8082 /* {File::LOCK_NB}[rdoc-ref:File::Constants@File-3A-3ALOCK_NB] */
8083 rb_define_const(rb_mFConst, "LOCK_NB", INT2FIX(LOCK_NB));
8084
8085 /* {File::NULL}[rdoc-ref:File::Constants@File-3A-3ANULL] */
8086 rb_define_const(rb_mFConst, "NULL", rb_fstring_cstr(ruby_null_device));
8087
8088 rb_define_global_function("test", rb_f_test, -1);
8089
8090 rb_cStat = rb_define_class_under(rb_cFile, "Stat", rb_cObject);
8091 rb_define_alloc_func(rb_cStat, rb_stat_s_alloc);
8092 rb_define_method(rb_cStat, "initialize", rb_stat_init, 1);
8093 rb_define_method(rb_cStat, "initialize_copy", rb_stat_init_copy, 1);
8094
8096
8097 rb_define_method(rb_cStat, "<=>", rb_stat_cmp, 1);
8098
8099 rb_define_method(rb_cStat, "dev", rb_stat_dev, 0);
8100 rb_define_method(rb_cStat, "dev_major", rb_stat_dev_major, 0);
8101 rb_define_method(rb_cStat, "dev_minor", rb_stat_dev_minor, 0);
8102 rb_define_method(rb_cStat, "ino", rb_stat_ino, 0);
8103 rb_define_method(rb_cStat, "mode", rb_stat_mode, 0);
8104 rb_define_method(rb_cStat, "nlink", rb_stat_nlink, 0);
8105 rb_define_method(rb_cStat, "uid", rb_stat_uid, 0);
8106 rb_define_method(rb_cStat, "gid", rb_stat_gid, 0);
8107 rb_define_method(rb_cStat, "rdev", rb_stat_rdev, 0);
8108 rb_define_method(rb_cStat, "rdev_major", rb_stat_rdev_major, 0);
8109 rb_define_method(rb_cStat, "rdev_minor", rb_stat_rdev_minor, 0);
8110 rb_define_method(rb_cStat, "size", rb_stat_size, 0);
8111 rb_define_method(rb_cStat, "blksize", rb_stat_blksize, 0);
8112 rb_define_method(rb_cStat, "blocks", rb_stat_blocks, 0);
8113 rb_define_method(rb_cStat, "atime", rb_stat_atime, 0);
8114 rb_define_method(rb_cStat, "mtime", rb_stat_mtime, 0);
8115 rb_define_method(rb_cStat, "ctime", rb_stat_ctime, 0);
8116 rb_define_method(rb_cStat, "birthtime", rb_stat_birthtime, 0);
8117
8118 rb_define_method(rb_cStat, "inspect", rb_stat_inspect, 0);
8119
8120 rb_define_method(rb_cStat, "ftype", rb_stat_ftype, 0);
8121
8122 rb_define_method(rb_cStat, "directory?", rb_stat_d, 0);
8123 rb_define_method(rb_cStat, "readable?", rb_stat_r, 0);
8124 rb_define_method(rb_cStat, "readable_real?", rb_stat_R, 0);
8125 rb_define_method(rb_cStat, "world_readable?", rb_stat_wr, 0);
8126 rb_define_method(rb_cStat, "writable?", rb_stat_w, 0);
8127 rb_define_method(rb_cStat, "writable_real?", rb_stat_W, 0);
8128 rb_define_method(rb_cStat, "world_writable?", rb_stat_ww, 0);
8129 rb_define_method(rb_cStat, "executable?", rb_stat_x, 0);
8130 rb_define_method(rb_cStat, "executable_real?", rb_stat_X, 0);
8131 rb_define_method(rb_cStat, "file?", rb_stat_f, 0);
8132 rb_define_method(rb_cStat, "zero?", rb_stat_z, 0);
8133 rb_define_method(rb_cStat, "size?", rb_stat_s, 0);
8134 rb_define_method(rb_cStat, "owned?", rb_stat_owned, 0);
8135 rb_define_method(rb_cStat, "grpowned?", rb_stat_grpowned, 0);
8136
8137 rb_define_method(rb_cStat, "pipe?", rb_stat_p, 0);
8138 rb_define_method(rb_cStat, "symlink?", rb_stat_l, 0);
8139 rb_define_method(rb_cStat, "socket?", rb_stat_S, 0);
8140
8141 rb_define_method(rb_cStat, "blockdev?", rb_stat_b, 0);
8142 rb_define_method(rb_cStat, "chardev?", rb_stat_c, 0);
8143
8144 rb_define_method(rb_cStat, "setuid?", rb_stat_suid, 0);
8145 rb_define_method(rb_cStat, "setgid?", rb_stat_sgid, 0);
8146 rb_define_method(rb_cStat, "sticky?", rb_stat_sticky, 0);
8147}
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
#define rb_define_global_function(mid, func, arity)
Defines rb_mKernel #mid.
#define PATH_SEP
The delimiter of PATH environment variable.
Definition dosish.h:45
#define PATH_SEP_CHAR
Identical to PATH_SEP, except it is of type char.
Definition dosish.h:49
#define GIDT2NUM
Converts a C's gid_t into an instance of rb_cInteger.
Definition gid_t.h:28
#define NUM2GIDT
Converts an instance of rb_cNumeric into C's gid_t.
Definition gid_t.h:33
void rb_include_module(VALUE klass, VALUE module)
Includes a module to a class.
Definition class.c:1693
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
Definition class.c:1476
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition class.c:1512
VALUE rb_define_module(const char *name)
Defines a top-level module.
Definition class.c:1594
VALUE rb_define_module_under(VALUE outer, const char *name)
Defines a module under the namespace of outer.
Definition class.c:1622
#define rb_str_new2
Old name of rb_str_new_cstr.
Definition string.h:1674
#define T_FILE
Old name of RUBY_T_FILE.
Definition value_type.h:62
#define rb_str_buf_cat2
Old name of rb_usascii_str_new_cstr.
Definition string.h:1681
#define NUM2ULONG
Old name of RB_NUM2ULONG.
Definition long.h:52
#define ALLOCV
Old name of RB_ALLOCV.
Definition memory.h:404
#define OBJ_INIT_COPY(obj, orig)
Old name of RB_OBJ_INIT_COPY.
Definition object.h:41
#define T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
#define xfree
Old name of ruby_xfree.
Definition xmalloc.h:58
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
#define rb_str_cat2
Old name of rb_str_cat_cstr.
Definition string.h:1682
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
#define rb_str_buf_new2
Old name of rb_str_buf_new_cstr.
Definition string.h:1678
#define OBJ_FREEZE
Old name of RB_OBJ_FREEZE.
Definition fl_type.h:134
#define ULONG2NUM
Old name of RB_ULONG2NUM.
Definition long.h:60
#define UNREACHABLE_RETURN
Old name of RBIMPL_UNREACHABLE_RETURN.
Definition assume.h:29
#define ENCODING_GET(obj)
Old name of RB_ENCODING_GET.
Definition encoding.h:109
#define LONG2FIX
Old name of RB_INT2FIX.
Definition long.h:49
#define MBCLEN_CHARFOUND_LEN(ret)
Old name of ONIGENC_MBCLEN_CHARFOUND_LEN.
Definition encoding.h:517
#define STRCASECMP
Old name of st_locale_insensitive_strcasecmp.
Definition ctype.h:102
#define rb_usascii_str_new2
Old name of rb_usascii_str_new_cstr.
Definition string.h:1679
#define ISALPHA
Old name of rb_isalpha.
Definition ctype.h:92
#define ULL2NUM
Old name of RB_ULL2NUM.
Definition long_long.h:31
#define TOLOWER
Old name of rb_tolower.
Definition ctype.h:101
#define Qtrue
Old name of RUBY_Qtrue.
#define NUM2INT
Old name of RB_NUM2INT.
Definition int.h:44
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
#define NIL_P
Old name of RB_NIL_P.
#define ALLOCV_N
Old name of RB_ALLOCV_N.
Definition memory.h:405
#define MBCLEN_CHARFOUND_P(ret)
Old name of ONIGENC_MBCLEN_CHARFOUND_P.
Definition encoding.h:516
#define ISPRINT
Old name of rb_isprint.
Definition ctype.h:86
#define NUM2CHR
Old name of RB_NUM2CHR.
Definition char.h:33
#define ENC_CODERANGE_CLEAR(obj)
Old name of RB_ENC_CODERANGE_CLEAR.
Definition coderange.h:187
#define UINT2NUM
Old name of RB_UINT2NUM.
Definition int.h:46
#define CONST_ID
Old name of RUBY_CONST_ID.
Definition symbol.h:47
#define rb_str_new4
Old name of rb_str_new_frozen.
Definition string.h:1676
#define ALLOCV_END
Old name of RB_ALLOCV_END.
Definition memory.h:406
VALUE rb_eNotImpError
NotImplementedError exception.
Definition error.c:1440
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition eval.c:682
VALUE rb_eIOError
IOError exception.
Definition io.c:189
VALUE rb_eTypeError
TypeError exception.
Definition error.c:1430
VALUE rb_eEncCompatError
Encoding::CompatibilityError exception.
Definition error.c:1437
void rb_enc_raise(rb_encoding *enc, VALUE exc, const char *fmt,...)
Identical to rb_raise(), except it additionally takes an encoding.
Definition error.c:3776
VALUE rb_eSystemCallError
SystemCallError exception.
Definition error.c:1450
VALUE rb_class_new_instance(int argc, const VALUE *argv, VALUE klass)
Allocates, then initialises an instance of the given class.
Definition object.c:2193
VALUE rb_cIO
IO class.
Definition io.c:187
VALUE rb_cStat
File::Stat class.
Definition file.c:193
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
Definition object.c:262
VALUE rb_inspect(VALUE obj)
Generates a human-readable textual representation of the given object.
Definition object.c:684
VALUE rb_mFileTest
FileTest module.
Definition file.c:192
VALUE rb_equal(VALUE lhs, VALUE rhs)
This function is an optimised version of calling #==.
Definition object.c:174
VALUE rb_obj_is_kind_of(VALUE obj, VALUE klass)
Queries if the given object is an instance (of possibly descendants) of the given class.
Definition object.c:907
VALUE rb_obj_freeze(VALUE obj)
Just calls rb_obj_freeze_inline() inside.
Definition object.c:1326
VALUE rb_mComparable
Comparable module.
Definition compar.c:19
VALUE rb_cFile
File class.
Definition file.c:191
VALUE rb_cString
String class.
Definition string.c:83
Encoding relates APIs.
static char * rb_enc_left_char_head(const char *s, const char *p, const char *e, rb_encoding *enc)
Queries the left boundary of a character.
Definition encoding.h:683
VALUE rb_str_conv_enc(VALUE str, rb_encoding *from, rb_encoding *to)
Encoding conversion main routine.
Definition string.c:1320
int rb_enc_str_asciionly_p(VALUE str)
Queries if the passed string is "ASCII only".
Definition string.c:951
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1117
VALUE rb_assoc_new(VALUE car, VALUE cdr)
Identical to rb_ary_new_from_values(), except it expects exactly two parameters.
#define INTEGER_PACK_NATIVE_BYTE_ORDER
Means either INTEGER_PACK_MSBYTE_FIRST or INTEGER_PACK_LSBYTE_FIRST, depending on the host processor'...
Definition bignum.h:546
#define INTEGER_PACK_2COMP
Uses 2's complement representation.
Definition bignum.h:549
#define INTEGER_PACK_LSWORD_FIRST
Stores/interprets the least significant word as the first word.
Definition bignum.h:528
static int rb_check_arity(int argc, int min, int max)
Ensures that the passed integer is in the passed range.
Definition error.h:284
void rb_update_max_fd(int fd)
Informs the interpreter that the passed fd can be the max.
Definition io.c:248
int rb_cloexec_open(const char *pathname, int flags, mode_t mode)
Opens a file that closes on exec.
Definition io.c:328
VALUE rb_str_new_shared(VALUE str)
Identical to rb_str_new_cstr(), except it takes a Ruby's string instead of C's.
Definition string.c:1490
VALUE rb_str_plus(VALUE lhs, VALUE rhs)
Generates a new string, concatenating the former to the latter.
Definition string.c:2450
VALUE rb_str_append(VALUE dst, VALUE src)
Identical to rb_str_buf_append(), except it converts the right hand side before concatenating.
Definition string.c:3760
VALUE rb_str_tmp_new(long len)
Allocates a "temporary" string.
Definition string.c:1714
VALUE rb_str_subseq(VALUE str, long beg, long len)
Identical to rb_str_substr(), except the numbers are interpreted as byte offsets instead of character...
Definition string.c:3116
VALUE rb_str_ellipsize(VALUE str, long len)
Shortens str and adds three dots, an ellipsis, if it is longer than len characters.
Definition string.c:11646
#define rb_str_new(str, len)
Allocates an instance of rb_cString.
Definition string.h:1497
size_t rb_str_capacity(VALUE str)
Queries the capacity of the given string.
Definition string.c:986
VALUE rb_str_dup(VALUE str)
Duplicates a string.
Definition string.c:1959
VALUE rb_str_cat(VALUE dst, const char *src, long srclen)
Destructively appends the passed contents to the string.
Definition string.c:3528
VALUE rb_str_replace(VALUE dst, VALUE src)
Replaces the contents of the former object with the stringised contents of the latter.
Definition string.c:6648
VALUE rb_str_buf_append(VALUE dst, VALUE src)
Identical to rb_str_cat_cstr(), except it takes Ruby's string instead of C's.
Definition string.c:3726
void rb_str_set_len(VALUE str, long len)
Overwrites the length of the string.
Definition string.c:3350
VALUE rb_str_inspect(VALUE str)
Generates a "readable" version of the receiver.
Definition string.c:7315
int rb_str_cmp(VALUE lhs, VALUE rhs)
Compares two strings, as in strcmp(3).
Definition string.c:4178
#define rb_str_dup_frozen
Just another name of rb_str_new_frozen.
Definition string.h:630
void rb_str_modify_expand(VALUE str, long capa)
Identical to rb_str_modify(), except it additionally expands the capacity of the receiver.
Definition string.c:2707
VALUE rb_str_buf_new(long capa)
Allocates a "string buffer".
Definition string.c:1686
#define rb_str_new_cstr(str)
Identical to rb_str_new, except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1513
VALUE rb_exec_recursive(VALUE(*f)(VALUE g, VALUE h, int r), VALUE g, VALUE h)
"Recursion" API entry point.
void rb_thread_wait_for(struct timeval time)
Identical to rb_thread_sleep(), except it takes struct timeval instead.
Definition thread.c:1422
VALUE rb_time_nano_new(time_t sec, long nsec)
Identical to rb_time_new(), except it accepts the time in nanoseconds resolution.
Definition time.c:2800
struct timespec rb_time_timespec(VALUE time)
Identical to rb_time_timeval(), except for return type.
Definition time.c:2971
void rb_define_alloc_func(VALUE klass, rb_alloc_func_t func)
Sets the allocator function of a class.
#define GetOpenFile
This is an old name of RB_IO_POINTER.
Definition io.h:442
#define FMODE_WRITABLE
The IO is opened for writing.
Definition io.h:165
#define RB_IO_POINTER(obj, fp)
Queries the underlying IO pointer.
Definition io.h:436
void rb_io_check_closed(rb_io_t *fptr)
This badly named function asserts that the passed IO is open.
Definition io.c:796
int len
Length of the buffer.
Definition io.h:8
char * ruby_getcwd(void)
This is our own version of getcwd(3) that uses ruby_xmalloc() instead of system malloc (benefits our ...
Definition util.c:581
#define strdup(s)
Just another name of ruby_strdup.
Definition util.h:187
#define ALLOCA_N(type, n)
Definition memory.h:292
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
#define PRI_MODET_PREFIX
A rb_sprintf() format prefix to be used for a mode_t parameter.
Definition mode_t.h:38
#define NUM2MODET
Converts a C's mode_t into an instance of rb_cInteger.
Definition mode_t.h:28
#define MODET2NUM
Converts an instance of rb_cNumeric into C's mode_t.
Definition mode_t.h:33
VALUE rb_rescue(type *q, VALUE w, type *e, VALUE r)
An equivalent of rescue clause.
Defines RBIMPL_ATTR_NONSTRING.
#define RBIMPL_ATTR_NONSTRING()
Wraps (or simulates) __attribute__((nonstring))
Definition nonstring.h:34
#define OFFT2NUM
Converts a C's off_t into an instance of rb_cInteger.
Definition off_t.h:33
#define NUM2OFFT
Converts an instance of rb_cNumeric into C's off_t.
Definition off_t.h:44
#define inline
Old Visual Studio versions do not support the inline keyword, so we need to define it to be __inline.
Definition defines.h:91
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:51
#define RARRAY_AREF(a, i)
Definition rarray.h:403
#define StringValue(v)
Ensures that the parameter object is a String.
Definition rstring.h:66
#define StringValuePtr(v)
Identical to StringValue, except it returns a char*.
Definition rstring.h:76
#define RSTRING_GETMEM(str, ptrvar, lenvar)
Convenient macro to obtain the contents and length at once.
Definition rstring.h:488
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
Definition rstring.h:89
#define RUBY_TYPED_DEFAULT_FREE
This is a value you can set to rb_data_type_struct::dfree.
Definition rtypeddata.h:80
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:521
#define TypedData_Make_Struct(klass, type, data_type, sval)
Identical to TypedData_Wrap_Struct, except it allocates a new data region internally instead of takin...
Definition rtypeddata.h:503
const char * rb_obj_classname(VALUE obj)
Queries the name of the class of the passed object.
Definition variable.c:513
#define FilePathValue(v)
Ensures that the parameter object is a path.
Definition ruby.h:90
#define errno
Ractor-aware version of errno.
Definition ruby.h:388
#define FilePathStringValue(v)
This macro actually does the same thing as FilePathValue now.
Definition ruby.h:105
#define _(args)
This was a transition path from K&R to ANSI.
Definition stdarg.h:35
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:202
Ruby's IO, metadata and buffers.
Definition io.h:295
enum rb_io_mode mode
mode flags: FMODE_XXXs
Definition io.h:310
int fd
file descriptor.
Definition io.h:306
VALUE pathv
pathname for file
Definition io.h:322
#define UIDT2NUM
Converts a C's uid_t into an instance of rb_cInteger.
Definition uid_t.h:28
#define NUM2UIDT
Converts an instance of rb_cNumeric into C's uid_t.
Definition uid_t.h:33
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:376
#define RBIMPL_WARNING_IGNORED(flag)
Suppresses a warning.
#define RBIMPL_WARNING_PUSH()
Pushes compiler warning state.
#define RBIMPL_WARNING_POP()
Pops compiler warning state.