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