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