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