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