1 /**********************************************************************
6 created at: Wed Jan 5 09:51:01 JST 1994
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
12 **********************************************************************/
14 #include "ruby/internal/config.h"
18 #include <sys/types.h>
29 #ifndef USE_OPENDIR_AT
30 # if defined(HAVE_FDOPENDIR) && defined(HAVE_DIRFD) && \
31 defined(HAVE_OPENAT) && defined(HAVE_FSTATAT)
32 # define USE_OPENDIR_AT 1
34 # define USE_OPENDIR_AT 0
42 #undef HAVE_DIRENT_NAMLEN
43 #if defined HAVE_DIRENT_H && !defined _WIN32
45 # define NAMLEN(dirent) strlen((dirent)->d_name)
46 #elif defined HAVE_DIRECT_H && !defined _WIN32
48 # define NAMLEN(dirent) strlen((dirent)->d_name)
50 # define dirent direct
51 # define NAMLEN(dirent) (dirent)->d_namlen
52 # define HAVE_DIRENT_NAMLEN 1
53 # ifdef HAVE_SYS_NDIR_H
54 # include <sys/ndir.h>
56 # ifdef HAVE_SYS_DIR_H
63 # include "win32/dir.h"
72 char *strchr(char*,char);
75 #ifdef HAVE_SYS_ATTR_H
79 #define USE_NAME_ON_FS_REAL_BASENAME 1 /* platform dependent APIs to
80 * get real basenames */
81 #define USE_NAME_ON_FS_BY_FNMATCH 2 /* select the matching
82 * basename by fnmatch */
84 #ifdef HAVE_GETATTRLIST
85 # define USE_NAME_ON_FS USE_NAME_ON_FS_REAL_BASENAME
86 # define RUP32(size) ((size)+3/4)
87 # define SIZEUP32(type) RUP32(sizeof(type))
89 # define USE_NAME_ON_FS USE_NAME_ON_FS_REAL_BASENAME
91 # define USE_NAME_ON_FS USE_NAME_ON_FS_BY_FNMATCH
93 # define USE_NAME_ON_FS 0
97 # define NORMALIZE_UTF8PATH 1
98 # include <sys/param.h>
99 # include <sys/mount.h>
100 # include <sys/vnode.h>
102 # define NORMALIZE_UTF8PATH 0
105 #include "encindex.h"
107 #include "internal.h"
108 #include "internal/array.h"
109 #include "internal/dir.h"
110 #include "internal/encoding.h"
111 #include "internal/error.h"
112 #include "internal/file.h"
113 #include "internal/gc.h"
114 #include "internal/io.h"
115 #include "internal/object.h"
116 #include "internal/vm.h"
117 #include "ruby/encoding.h"
118 #include "ruby/ruby.h"
119 #include "ruby/thread.h"
120 #include "ruby/util.h"
127 #define vm_initialized rb_cThread
129 /* define system APIs */
132 # define chdir(p) rb_w32_uchdir(p)
134 # define mkdir(p, m) rb_w32_umkdir((p), (m))
136 # define rmdir(p) rb_w32_urmdir(p)
138 # define opendir(p) rb_w32_uopendir(p)
139 # define ruby_getcwd() rb_w32_ugetcwd(NULL, 0)
145 #if NORMALIZE_UTF8PATH
146 # if defined HAVE_FGETATTRLIST || !defined HAVE_GETATTRLIST
147 # define need_normalization(dirp, path) need_normalization(dirp)
149 # define need_normalization(dirp, path) need_normalization(path)
152 need_normalization(DIR *dirp
, const char *path
)
154 # if defined HAVE_FGETATTRLIST || defined HAVE_GETATTRLIST
155 u_int32_t attrbuf
[SIZEUP32(fsobj_tag_t
)];
156 struct attrlist al
= {ATTR_BIT_MAP_COUNT
, 0, ATTR_CMN_OBJTAG
,};
157 # if defined HAVE_FGETATTRLIST
158 int ret
= fgetattrlist(dirfd(dirp
), &al
, attrbuf
, sizeof(attrbuf
), 0);
160 int ret
= getattrlist(path
, &al
, attrbuf
, sizeof(attrbuf
), 0);
163 const fsobj_tag_t
*tag
= (void *)(attrbuf
+1);
175 has_nonascii(const char *ptr
, size_t len
)
178 if (!ISASCII(*ptr
)) return 1;
185 # define IF_NORMALIZE_UTF8PATH(something) something
187 # define IF_NORMALIZE_UTF8PATH(something) /* nothing */
190 #if defined(IFTODT) && defined(DT_UNKNOWN)
191 # define EMULATE_IFTODT 0
193 # define EMULATE_IFTODT 1
197 # define IFTODT(m) (((m) & S_IFMT) / ((~S_IFMT & (S_IFMT-1)) + 1))
202 path_exist
= DT_UNKNOWN
,
203 path_directory
= DT_DIR
,
204 path_regular
= DT_REG
,
205 path_symlink
= DT_LNK
,
208 path_directory
= IFTODT(S_IFDIR
),
209 path_regular
= IFTODT(S_IFREG
),
210 path_symlink
= IFTODT(S_IFLNK
),
216 #define FNM_NOESCAPE 0x01
217 #define FNM_PATHNAME 0x02
218 #define FNM_DOTMATCH 0x04
219 #define FNM_CASEFOLD 0x08
220 #define FNM_EXTGLOB 0x10
221 #if CASEFOLD_FILESYSTEM
222 #define FNM_SYSCASE FNM_CASEFOLD
224 #define FNM_SYSCASE 0
227 #define FNM_SHORTNAME 0x20
229 #define FNM_SHORTNAME 0
231 #define FNM_GLOB_NOSORT 0x40
232 #define FNM_GLOB_SKIPDOT 0x80
234 #define FNM_NOMATCH 1
237 # define Next(p, e, enc) ((p)+ rb_enc_mbclen((p), (e), (enc)))
238 # define Inc(p, e, enc) ((p) = Next((p), (e), (enc)))
242 const char *p
, /* pattern (next to '[') */
244 const char *s
, /* string */
249 const int nocase
= flags
& FNM_CASEFOLD
;
250 const int escape
= !(flags
& FNM_NOESCAPE
);
255 if (p
>= pend
) return NULL
;
256 if (*p
== '!' || *p
== '^') {
263 if (escape
&& *t1
== '\\')
267 p
= t1
+ (r
= rb_enc_mbclen(t1
, pend
, enc
));
268 if (p
>= pend
) return NULL
;
269 if (p
[0] == '-' && p
[1] != ']') {
270 const char *t2
= p
+ 1;
272 if (escape
&& *t2
== '\\')
276 p
= t2
+ (r2
= rb_enc_mbclen(t2
, pend
, enc
));
278 if ((r
<= (send
-s
) && memcmp(t1
, s
, r
) == 0) ||
279 (r2
<= (send
-s
) && memcmp(t2
, s
, r2
) == 0)) {
283 c1
= rb_enc_codepoint(s
, send
, enc
);
284 if (nocase
) c1
= rb_enc_toupper(c1
, enc
);
285 c2
= rb_enc_codepoint(t1
, pend
, enc
);
286 if (nocase
) c2
= rb_enc_toupper(c2
, enc
);
287 if (c1
< c2
) continue;
288 c2
= rb_enc_codepoint(t2
, pend
, enc
);
289 if (nocase
) c2
= rb_enc_toupper(c2
, enc
);
290 if (c1
> c2
) continue;
294 if (r
<= (send
-s
) && memcmp(t1
, s
, r
) == 0) {
298 if (!nocase
) continue;
299 c1
= rb_enc_toupper(rb_enc_codepoint(s
, send
, enc
), enc
);
300 c2
= rb_enc_toupper(rb_enc_codepoint(p
, pend
, enc
), enc
);
301 if (c1
!= c2
) continue;
306 return ok
== not ? NULL
: (char *)p
+ 1;
309 /* If FNM_PATHNAME is set, only path element will be matched. (up to '/' or '\0')
310 Otherwise, entire string will be matched.
311 End marker itself won't be compared.
312 And if function succeeds, *pcur reaches end marker.
314 #define UNESCAPE(p) (escape && *(p) == '\\' ? (p) + 1 : (p))
315 #define ISEND(p) (!*(p) || (pathname && *(p) == '/'))
316 #define RETURN(val) return *pcur = p, *scur = s, (val);
320 const char **pcur
, /* pattern */
321 const char **scur
, /* string */
325 const int period
= !(flags
& FNM_DOTMATCH
);
326 const int pathname
= flags
& FNM_PATHNAME
;
327 const int escape
= !(flags
& FNM_NOESCAPE
);
328 const int nocase
= flags
& FNM_CASEFOLD
;
330 const char *ptmp
= 0;
331 const char *stmp
= 0;
333 const char *p
= *pcur
;
334 const char *pend
= p
+ strlen(p
);
335 const char *s
= *scur
;
336 const char *send
= s
+ strlen(s
);
340 if (period
&& *s
== '.' && *UNESCAPE(p
) != '.') /* leading period */
346 do { p
++; } while (*p
== '*');
347 if (ISEND(UNESCAPE(p
))) {
368 if ((t
= bracket(p
+ 1, pend
, s
, send
, flags
, enc
)) != 0) {
380 RETURN(ISEND(p
) ? 0 : FNM_NOMATCH
);
383 r
= rb_enc_precise_mbclen(p
, pend
, enc
);
384 if (!MBCLEN_CHARFOUND_P(r
))
386 if (r
<= (send
-s
) && memcmp(p
, s
, r
) == 0) {
391 if (!nocase
) goto failed
;
392 if (rb_enc_toupper(rb_enc_codepoint(p
, pend
, enc
), enc
) !=
393 rb_enc_toupper(rb_enc_codepoint(s
, send
, enc
), enc
))
399 failed
: /* try next '*' position */
402 Inc(stmp
, send
, enc
); /* !ISEND(*stmp) */
417 const char *p
= pattern
;
418 const char *s
= string
;
419 const char *send
= s
+ strlen(string
);
420 const int period
= !(flags
& FNM_DOTMATCH
);
421 const int pathname
= flags
& FNM_PATHNAME
;
423 const char *ptmp
= 0;
424 const char *stmp
= 0;
428 if (p
[0] == '*' && p
[1] == '*' && p
[2] == '/') {
429 do { p
+= 3; } while (p
[0] == '*' && p
[1] == '*' && p
[2] == '/');
433 if (fnmatch_helper(&p
, &s
, flags
, enc
) == 0) {
434 while (*s
&& *s
!= '/') Inc(s
, send
, enc
);
443 /* failed : try next recursion */
444 if (ptmp
&& stmp
&& !(period
&& *stmp
== '.')) {
445 while (*stmp
&& *stmp
!= '/') Inc(stmp
, send
, enc
);
457 return fnmatch_helper(&p
, &s
, flags
, enc
);
471 struct dir_data
*dir
= ptr
;
473 if (dir
->dir
) closedir(dir
->dir
);
476 RUBY_REFERENCES(dir_refs
) = {
477 RUBY_REF_EDGE(struct dir_data
, path
),
481 static const rb_data_type_t dir_data_type
= {
484 RUBY_REFS_LIST_PTR(dir_refs
),
486 NULL
, // Nothing allocated externally, so don't need a memsize function
488 0, NULL
, RUBY_TYPED_WB_PROTECTED
| RUBY_TYPED_FREE_IMMEDIATELY
| RUBY_TYPED_DECL_MARKING
| RUBY_TYPED_EMBEDDABLE
491 static VALUE
dir_close(VALUE
);
494 dir_s_alloc(VALUE klass
)
496 struct dir_data
*dirp
;
497 VALUE obj
= TypedData_Make_Struct(klass
, struct dir_data
, &dir_data_type
, dirp
);
500 RB_OBJ_WRITE(obj
, &dirp
->path
, Qnil
);
507 nogvl_opendir(void *ptr
)
509 const char *path
= ptr
;
511 return (void *)opendir(path
);
515 opendir_without_gvl(const char *path
)
517 if (vm_initialized
) {
518 union { const void *in
; void *out
; } u
;
522 return IO_WITHOUT_GVL(nogvl_opendir
, u
.out
);
525 return opendir(path
);
529 dir_initialize(rb_execution_context_t
*ec
, VALUE dir
, VALUE dirname
, VALUE enc
)
534 rb_encoding
*fsenc
= NIL_P(enc
) ? rb_filesystem_encoding() : rb_to_encoding(enc
);
536 FilePathValue(dirname
);
537 orig
= rb_str_dup_frozen(dirname
);
538 dirname
= rb_str_encode_ospath(dirname
);
539 dirname
= rb_str_dup_frozen(dirname
);
541 TypedData_Get_Struct(dir
, struct dir_data
, &dir_data_type
, dp
);
542 if (dp
->dir
) closedir(dp
->dir
);
544 RB_OBJ_WRITE(dir
, &dp
->path
, Qnil
);
546 path
= RSTRING_PTR(dirname
);
547 dp
->dir
= opendir_without_gvl(path
);
548 if (dp
->dir
== NULL
) {
550 if (rb_gc_for_fd(e
)) {
551 dp
->dir
= opendir_without_gvl(path
);
553 #ifdef HAVE_GETATTRLIST
555 u_int32_t attrbuf
[1];
556 struct attrlist al
= {ATTR_BIT_MAP_COUNT
, 0};
557 if (getattrlist(path
, &al
, attrbuf
, sizeof(attrbuf
), FSOPT_NOFOLLOW
) == 0) {
558 dp
->dir
= opendir_without_gvl(path
);
562 if (dp
->dir
== NULL
) {
563 RB_GC_GUARD(dirname
);
564 rb_syserr_fail_path(e
, orig
);
567 RB_OBJ_WRITE(dir
, &dp
->path
, orig
);
573 dir_s_open(rb_execution_context_t
*ec
, VALUE klass
, VALUE dirname
, VALUE enc
)
576 VALUE dir
= TypedData_Make_Struct(klass
, struct dir_data
, &dir_data_type
, dp
);
578 dir_initialize(ec
, dir
, dirname
, enc
);
584 dir_s_close(rb_execution_context_t
*ec
, VALUE klass
, VALUE dir
)
586 return dir_close(dir
);
589 # if defined(HAVE_FDOPENDIR) && defined(HAVE_DIRFD)
592 * Dir.for_fd(fd) -> dir
594 * Returns a new \Dir object representing the directory specified by the given
595 * integer directory file descriptor +fd+:
598 * d1 = Dir.for_fd(d0.fileno)
600 * Note that the returned +d1+ does not have an associated path:
605 * This method uses the
606 * {fdopendir()}[https://www.man7.org/linux/man-pages/man3/fdopendir.3p.html]
607 * function defined by POSIX 2008;
608 * the method is not implemented on non-POSIX platforms (raises NotImplementedError).
611 dir_s_for_fd(VALUE klass
, VALUE fd
)
614 VALUE dir
= TypedData_Make_Struct(klass
, struct dir_data
, &dir_data_type
, dp
);
616 if (!(dp
->dir
= fdopendir(NUM2INT(fd
)))) {
617 rb_sys_fail("fdopendir");
618 UNREACHABLE_RETURN(Qnil
);
621 RB_OBJ_WRITE(dir
, &dp
->path
, Qnil
);
625 #define dir_s_for_fd rb_f_notimplement
628 NORETURN(static void dir_closed(void));
633 rb_raise(rb_eIOError
, "closed directory");
636 static struct dir_data
*
639 rb_check_frozen(dir
);
640 return rb_check_typeddata(dir
, &dir_data_type
);
643 static struct dir_data
*
646 struct dir_data
*dirp
= dir_get(dir
);
647 if (!dirp
->dir
) dir_closed();
651 #define GetDIR(obj, dirp) ((dirp) = dir_check(obj))
658 * Returns a string description of +self+:
660 * Dir.new('example').inspect # => "#<Dir:example>"
664 dir_inspect(VALUE dir
)
666 struct dir_data
*dirp
;
668 TypedData_Get_Struct(dir
, struct dir_data
, &dir_data_type
, dirp
);
669 if (!NIL_P(dirp
->path
)) {
670 VALUE str
= rb_str_new_cstr("#<");
671 rb_str_append(str
, rb_class_name(CLASS_OF(dir
)));
672 rb_str_cat2(str
, ":");
673 rb_str_append(str
, dirp
->path
);
674 rb_str_cat2(str
, ">");
677 return rb_funcallv(dir
, idTo_s
, 0, 0);
680 /* Workaround for Solaris 10 that does not have dirfd.
681 Note: Solaris 11 (POSIX.1-2008 compliant) has dirfd(3C).
683 #if defined(__sun) && !defined(HAVE_DIRFD)
684 # if defined(HAVE_DIR_D_FD)
685 # define dirfd(x) ((x)->d_fd)
686 # define HAVE_DIRFD 1
687 # elif defined(HAVE_DIR_DD_FD)
688 # define dirfd(x) ((x)->dd_fd)
689 # define HAVE_DIRFD 1
698 * Returns the file descriptor used in <em>dir</em>.
703 * This method uses the
704 * {dirfd()}[https://www.man7.org/linux/man-pages/man3/dirfd.3.html]
705 * function defined by POSIX 2008;
706 * the method is not implemented on non-POSIX platforms (raises NotImplementedError).
709 dir_fileno(VALUE dir
)
711 struct dir_data
*dirp
;
715 fd
= dirfd(dirp
->dir
);
717 rb_sys_fail("dirfd");
721 #define dir_fileno rb_f_notimplement
726 * path -> string or nil
728 * Returns the +dirpath+ string that was used to create +self+
729 * (or +nil+ if created by method Dir.for_fd):
731 * Dir.new('example').path # => "example"
737 struct dir_data
*dirp
;
739 TypedData_Get_Struct(dir
, struct dir_data
, &dir_data_type
, dirp
);
740 if (NIL_P(dirp
->path
)) return Qnil
;
741 return rb_str_dup(dirp
->path
);
746 fundamental_encoding_p(rb_encoding
*enc
)
748 switch (rb_enc_to_index(enc
)) {
749 case ENCINDEX_ASCII_8BIT
:
750 case ENCINDEX_US_ASCII
:
757 # define READDIR(dir, enc) rb_w32_readdir((dir), (enc))
759 # define READDIR(dir, enc) readdir((dir))
762 /* safe to use without GVL */
764 to_be_skipped(const struct dirent
*dp
)
766 const char *name
= dp
->d_name
;
767 if (name
[0] != '.') return FALSE
;
768 #ifdef HAVE_DIRENT_NAMLEN
769 switch (NAMLEN(dp
)) {
771 if (name
[1] != '.') return FALSE
;
778 if (!name
[1]) return TRUE
;
779 if (name
[1] != '.') return FALSE
;
780 if (!name
[2]) return TRUE
;
787 * read -> string or nil
789 * Reads and returns the next entry name from +self+;
790 * returns +nil+ if at end-of-stream;
791 * see {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like]:
793 * dir = Dir.new('example')
796 * dir.read # => "config.h"
802 struct dir_data
*dirp
;
807 if ((dp
= READDIR(dirp
->dir
, dirp
->enc
)) != NULL
) {
808 return rb_external_str_new_with_enc(dp
->d_name
, NAMLEN(dp
), dirp
->enc
);
812 if (e
!= 0) rb_syserr_fail(e
, 0);
813 return Qnil
; /* end of stream */
817 static VALUE
dir_each_entry(VALUE
, VALUE (*)(VALUE
, VALUE
), VALUE
, int);
820 dir_yield(VALUE arg
, VALUE path
)
822 return rb_yield(path
);
827 * each {|entry_name| ... } -> self
829 * Calls the block with each entry name in +self+:
831 * Dir.new('example').each {|entry_name| p entry_name }
841 * With no block given, returns an Enumerator.
847 RETURN_ENUMERATOR(dir
, 0, 0);
848 return dir_each_entry(dir
, dir_yield
, Qnil
, FALSE
);
852 dir_each_entry(VALUE dir
, VALUE (*each
)(VALUE
, VALUE
), VALUE arg
, int children_only
)
854 struct dir_data
*dirp
;
856 IF_NORMALIZE_UTF8PATH(int norm_p
);
859 rewinddir(dirp
->dir
);
860 IF_NORMALIZE_UTF8PATH(norm_p
= need_normalization(dirp
->dir
, RSTRING_PTR(dirp
->path
)));
861 while ((dp
= READDIR(dirp
->dir
, dirp
->enc
)) != NULL
) {
862 const char *name
= dp
->d_name
;
863 size_t namlen
= NAMLEN(dp
);
866 if (children_only
&& name
[0] == '.') {
867 if (namlen
== 1) continue; /* current directory */
868 if (namlen
== 2 && name
[1] == '.') continue; /* parent directory */
870 #if NORMALIZE_UTF8PATH
871 if (norm_p
&& has_nonascii(name
, namlen
) &&
872 !NIL_P(path
= rb_str_normalize_ospath(name
, namlen
))) {
873 path
= rb_external_str_with_enc(path
, dirp
->enc
);
877 path
= rb_external_str_new_with_enc(name
, namlen
, dirp
->enc
);
888 * Returns the current position of +self+;
889 * see {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like]:
891 * dir = Dir.new('example')
900 struct dir_data
*dirp
;
904 pos
= telldir(dirp
->dir
);
905 return rb_int2inum(pos
);
908 #define dir_tell rb_f_notimplement
914 * seek(position) -> self
916 * Sets the position in +self+ and returns +self+.
917 * The value of +position+ should have been returned from an earlier call to #tell;
918 * if not, the return values from subsequent calls to #read are unspecified.
920 * See {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like].
924 * dir = Dir.new('example')
926 * dir.seek(3) # => #<Dir:example>
928 * dir.seek(30) # => #<Dir:example>
933 dir_seek(VALUE dir
, VALUE pos
)
935 struct dir_data
*dirp
;
936 long p
= NUM2LONG(pos
);
939 seekdir(dirp
->dir
, p
);
943 #define dir_seek rb_f_notimplement
949 * pos = position -> integer
951 * Sets the position in +self+ and returns +position+.
952 * The value of +position+ should have been returned from an earlier call to #tell;
953 * if not, the return values from subsequent calls to #read are unspecified.
955 * See {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like].
959 * dir = Dir.new('example')
963 * dir.pos = 30 # => 30
968 dir_set_pos(VALUE dir
, VALUE pos
)
974 #define dir_set_pos rb_f_notimplement
981 * Sets the position in +self+ to zero;
982 * see {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like]:
984 * dir = Dir.new('example')
988 * dir.rewind # => #<Dir:example>
993 dir_rewind(VALUE dir
)
995 struct dir_data
*dirp
;
998 rewinddir(dirp
->dir
);
1006 * Closes the stream in +self+, if it is open, and returns +nil+;
1007 * ignored if +self+ is already closed:
1009 * dir = Dir.new('example')
1011 * dir.close # => nil
1012 * dir.close # => nil
1013 * dir.read # Raises IOError.
1017 dir_close(VALUE dir
)
1019 struct dir_data
*dirp
;
1021 dirp
= dir_get(dir
);
1022 if (!dirp
->dir
) return Qnil
;
1023 closedir(dirp
->dir
);
1030 nogvl_chdir(void *ptr
)
1032 const char *path
= ptr
;
1034 return (void *)(VALUE
)chdir(path
);
1038 dir_chdir0(VALUE path
)
1040 if (chdir(RSTRING_PTR(path
)) < 0)
1041 rb_sys_fail_path(path
);
1050 .blocking
= 0, .thread
= Qnil
,
1051 .path
= Qnil
, .line
= 0,
1057 if (chdir_lock
.blocking
== 0) {
1058 chdir_lock
.path
= rb_source_location(&chdir_lock
.line
);
1060 chdir_lock
.blocking
++;
1061 if (NIL_P(chdir_lock
.thread
)) {
1062 chdir_lock
.thread
= rb_thread_current();
1069 chdir_lock
.blocking
--;
1070 if (chdir_lock
.blocking
== 0) {
1071 chdir_lock
.thread
= Qnil
;
1072 chdir_lock
.path
= Qnil
;
1073 chdir_lock
.line
= 0;
1078 chdir_alone_block_p(void)
1080 int block_given
= rb_block_given_p();
1081 if (chdir_lock
.blocking
> 0) {
1082 if (rb_thread_current() != chdir_lock
.thread
)
1083 rb_raise(rb_eRuntimeError
, "conflicting chdir during another chdir block");
1085 if (!NIL_P(chdir_lock
.path
)) {
1086 rb_warn("conflicting chdir during another chdir block\n"
1087 "%" PRIsVALUE
":%d: note: previous chdir was here",
1088 chdir_lock
.path
, chdir_lock
.line
);
1091 rb_warn("conflicting chdir during another chdir block");
1099 VALUE old_path
, new_path
;
1105 chdir_yield(VALUE v
)
1107 struct chdir_data
*args
= (void *)v
;
1108 dir_chdir0(args
->new_path
);
1111 return args
->yield_path
? rb_yield(args
->new_path
) : rb_yield_values2(0, NULL
);
1115 chdir_restore(VALUE v
)
1117 struct chdir_data
*args
= (void *)v
;
1120 dir_chdir0(args
->old_path
);
1126 chdir_path(VALUE path
, bool yield_path
)
1128 if (chdir_alone_block_p()) {
1129 struct chdir_data args
;
1131 args
.old_path
= rb_str_encode_ospath(rb_dir_getwd());
1132 args
.new_path
= path
;
1134 args
.yield_path
= yield_path
;
1135 return rb_ensure(chdir_yield
, (VALUE
)&args
, chdir_restore
, (VALUE
)&args
);
1138 char *p
= RSTRING_PTR(path
);
1139 int r
= IO_WITHOUT_GVL_INT(nogvl_chdir
, p
);
1141 rb_sys_fail_path(path
);
1149 * Dir.chdir(new_dirpath) -> 0
1151 * Dir.chdir(new_dirpath) {|new_dirpath| ... } -> object
1152 * Dir.chdir {|cur_dirpath| ... } -> object
1154 * Changes the current working directory.
1156 * With argument +new_dirpath+ and no block,
1157 * changes to the given +dirpath+:
1159 * Dir.pwd # => "/example"
1160 * Dir.chdir('..') # => 0
1163 * With no argument and no block:
1165 * - Changes to the value of environment variable +HOME+ if defined.
1166 * - Otherwise changes to the value of environment variable +LOGDIR+ if defined.
1167 * - Otherwise makes no change.
1169 * With argument +new_dirpath+ and a block, temporarily changes the working directory:
1171 * - Calls the block with the argument.
1172 * - Changes to the given directory.
1173 * - Executes the block (yielding the new path).
1174 * - Restores the previous working directory.
1175 * - Returns the block's return value.
1179 * Dir.chdir('/var/spool/mail')
1180 * Dir.pwd # => "/var/spool/mail"
1181 * Dir.chdir('/tmp') do
1182 * Dir.pwd # => "/tmp"
1184 * Dir.pwd # => "/var/spool/mail"
1186 * With no argument and a block,
1187 * calls the block with the current working directory (string)
1188 * and returns the block's return value.
1190 * Calls to \Dir.chdir with blocks may be nested:
1192 * Dir.chdir('/var/spool/mail')
1193 * Dir.pwd # => "/var/spool/mail"
1194 * Dir.chdir('/tmp') do
1195 * Dir.pwd # => "/tmp"
1196 * Dir.chdir('/usr') do
1197 * Dir.pwd # => "/usr"
1199 * Dir.pwd # => "/tmp"
1201 * Dir.pwd # => "/var/spool/mail"
1203 * In a multi-threaded program an error is raised if a thread attempts
1204 * to open a +chdir+ block while another thread has one open,
1205 * or a call to +chdir+ without a block occurs inside
1206 * a block passed to +chdir+ (even in the same thread).
1208 * Raises an exception if the target directory does not exist.
1211 dir_s_chdir(int argc
, VALUE
*argv
, VALUE obj
)
1215 if (rb_check_arity(argc
, 0, 1) == 1) {
1216 path
= rb_str_encode_ospath(rb_get_path(argv
[0]));
1219 const char *dist
= getenv("HOME");
1221 dist
= getenv("LOGDIR");
1222 if (!dist
) rb_raise(rb_eArgError
, "HOME/LOGDIR not set");
1224 path
= rb_str_new2(dist
);
1227 return chdir_path(path
, true);
1230 #if defined(HAVE_FCHDIR) && defined(HAVE_DIRFD) && HAVE_FCHDIR && HAVE_DIRFD
1232 nogvl_fchdir(void *ptr
)
1234 const int *fd
= ptr
;
1236 return (void *)(VALUE
)fchdir(*fd
);
1243 rb_sys_fail("fchdir");
1246 struct fchdir_data
{
1253 fchdir_yield(VALUE v
)
1255 struct fchdir_data
*args
= (void *)v
;
1256 dir_fchdir(args
->fd
);
1259 return rb_yield_values(0);
1263 fchdir_restore(VALUE v
)
1265 struct fchdir_data
*args
= (void *)v
;
1268 dir_fchdir(RB_NUM2INT(dir_fileno(args
->old_dir
)));
1270 dir_close(args
->old_dir
);
1276 * Dir.fchdir(fd) -> 0
1277 * Dir.fchdir(fd) { ... } -> object
1279 * Changes the current working directory to the directory
1280 * specified by the integer file descriptor +fd+.
1282 * When passing a file descriptor over a UNIX socket or to a child process,
1283 * using +fchdir+ instead of +chdir+ avoids the
1284 * {time-of-check to time-of-use vulnerability}[https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use]
1286 * With no block, changes to the directory given by +fd+:
1288 * Dir.chdir('/var/spool/mail')
1289 * Dir.pwd # => "/var/spool/mail"
1290 * dir = Dir.new('/usr')
1293 * Dir.pwd # => "/usr"
1295 * With a block, temporarily changes the working directory:
1297 * - Calls the block with the argument.
1298 * - Changes to the given directory.
1299 * - Executes the block (yields no args).
1300 * - Restores the previous working directory.
1301 * - Returns the block's return value.
1305 * Dir.chdir('/var/spool/mail')
1306 * Dir.pwd # => "/var/spool/mail"
1307 * dir = Dir.new('/tmp')
1310 * Dir.pwd # => "/tmp"
1312 * Dir.pwd # => "/var/spool/mail"
1314 * This method uses the
1315 * {fchdir()}[https://www.man7.org/linux/man-pages/man3/fchdir.3p.html]
1316 * function defined by POSIX 2008;
1317 * the method is not implemented on non-POSIX platforms (raises NotImplementedError).
1319 * Raises an exception if the file descriptor is not valid.
1321 * In a multi-threaded program an error is raised if a thread attempts
1322 * to open a +chdir+ block while another thread has one open,
1323 * or a call to +chdir+ without a block occurs inside
1324 * a block passed to +chdir+ (even in the same thread).
1327 dir_s_fchdir(VALUE klass
, VALUE fd_value
)
1329 int fd
= RB_NUM2INT(fd_value
);
1331 if (chdir_alone_block_p()) {
1332 struct fchdir_data args
;
1333 args
.old_dir
= dir_s_alloc(klass
);
1334 dir_initialize(NULL
, args
.old_dir
, rb_fstring_cstr("."), Qnil
);
1337 return rb_ensure(fchdir_yield
, (VALUE
)&args
, fchdir_restore
, (VALUE
)&args
);
1340 int r
= IO_WITHOUT_GVL_INT(nogvl_fchdir
, &fd
);
1342 rb_sys_fail("fchdir");
1348 #define dir_s_fchdir rb_f_notimplement
1354 * chdir { ... } -> object
1356 * Changes the current working directory to +self+:
1359 * dir = Dir.new('example')
1361 * Dir.pwd # => "/example"
1363 * With a block, temporarily changes the working directory:
1365 * - Calls the block.
1366 * - Changes to the given directory.
1367 * - Executes the block (yields no args).
1368 * - Restores the previous working directory.
1369 * - Returns the block's return value.
1371 * Uses Dir.fchdir if available, and Dir.chdir if not, see those
1372 * methods for caveats.
1375 dir_chdir(VALUE dir
)
1377 #if defined(HAVE_FCHDIR) && defined(HAVE_DIRFD) && HAVE_FCHDIR && HAVE_DIRFD
1378 return dir_s_fchdir(rb_cDir
, dir_fileno(dir
));
1380 return chdir_path(dir_get(dir
)->path
, false);
1386 rb_dir_getwd_ospath(void)
1392 #undef RUBY_UNTYPED_DATA_WARNING
1393 #define RUBY_UNTYPED_DATA_WARNING 0
1394 path_guard
= Data_Wrap_Struct((VALUE
)0, NULL
, RUBY_DEFAULT_FREE
, NULL
);
1395 path
= ruby_getcwd();
1396 DATA_PTR(path_guard
) = path
;
1398 cwd
= rb_str_normalize_ospath(path
, strlen(path
));
1400 cwd
= rb_str_new2(path
);
1402 DATA_PTR(path_guard
) = 0;
1412 rb_encoding
*fs
= rb_filesystem_encoding();
1413 int fsenc
= rb_enc_to_index(fs
);
1414 VALUE cwd
= rb_dir_getwd_ospath();
1417 case ENCINDEX_US_ASCII
:
1418 fsenc
= ENCINDEX_ASCII_8BIT
;
1419 case ENCINDEX_ASCII_8BIT
:
1421 #if defined _WIN32 || defined __APPLE__
1423 return rb_str_conv_enc(cwd
, NULL
, fs
);
1426 return rb_enc_associate_index(cwd
, fsenc
);
1433 * Returns the path to the current working directory:
1435 * Dir.chdir("/tmp") # => 0
1436 * Dir.pwd # => "/tmp"
1440 dir_s_getwd(VALUE dir
)
1442 return rb_dir_getwd();
1446 check_dirname(VALUE dir
)
1454 enc
= rb_enc_get(d
);
1455 RSTRING_GETMEM(d
, path
, len
);
1457 pend
= rb_enc_path_end(rb_enc_path_skip_prefix(path
, pend
, enc
), pend
, enc
);
1458 if (pend
- path
< len
) {
1459 d
= rb_str_subseq(d
, 0, pend
- path
);
1462 return rb_str_encode_ospath(d
);
1465 #if defined(HAVE_CHROOT)
1468 * Dir.chroot(dirpath) -> 0
1470 * Changes the root directory of the calling process to that specified in +dirpath+.
1471 * The new root directory is used for pathnames beginning with <tt>'/'</tt>.
1472 * The root directory is inherited by all children of the calling process.
1474 * Only a privileged process may call +chroot+.
1476 * See {Linux chroot}[https://man7.org/linux/man-pages/man2/chroot.2.html].
1479 dir_s_chroot(VALUE dir
, VALUE path
)
1481 path
= check_dirname(path
);
1482 if (chroot(RSTRING_PTR(path
)) == -1)
1483 rb_sys_fail_path(path
);
1488 #define dir_s_chroot rb_f_notimplement
1497 nogvl_mkdir(void *ptr
)
1499 struct mkdir_arg
*m
= ptr
;
1501 return (void *)(VALUE
)mkdir(m
->path
, m
->mode
);
1506 * Dir.mkdir(dirpath, permissions = 0775) -> 0
1508 * Creates a directory in the underlying file system
1509 * at +dirpath+ with the given +permissions+;
1513 * File.stat(Dir.new('foo')).mode.to_s(8)[1..4] # => "0755"
1514 * Dir.mkdir('bar', 0644)
1515 * File.stat(Dir.new('bar')).mode.to_s(8)[1..4] # => "0644"
1517 * See {File Permissions}[rdoc-ref:File@File+Permissions].
1518 * Note that argument +permissions+ is ignored on Windows.
1521 dir_s_mkdir(int argc
, VALUE
*argv
, VALUE obj
)
1527 if (rb_scan_args(argc
, argv
, "11", &path
, &vmode
) == 2) {
1528 m
.mode
= NUM2MODET(vmode
);
1534 path
= check_dirname(path
);
1535 m
.path
= RSTRING_PTR(path
);
1536 r
= IO_WITHOUT_GVL_INT(nogvl_mkdir
, &m
);
1538 rb_sys_fail_path(path
);
1544 nogvl_rmdir(void *ptr
)
1546 const char *path
= ptr
;
1548 return (void *)(VALUE
)rmdir(path
);
1553 * Dir.rmdir(dirpath) -> 0
1555 * Removes the directory at +dirpath+ from the underlying file system:
1557 * Dir.rmdir('foo') # => 0
1559 * Raises an exception if the directory is not empty.
1562 dir_s_rmdir(VALUE obj
, VALUE dir
)
1567 dir
= check_dirname(dir
);
1568 p
= RSTRING_PTR(dir
);
1569 r
= IO_WITHOUT_GVL_INT(nogvl_rmdir
, (void *)p
);
1571 rb_sys_fail_path(dir
);
1576 struct warning_args
{
1577 #ifdef RUBY_FUNCTION_NAME_STRING
1584 #ifndef RUBY_FUNCTION_NAME_STRING
1585 #define sys_enc_warning_in(func, mesg, enc) sys_enc_warning(mesg, enc)
1589 sys_warning_1(VALUE mesg
)
1591 const struct warning_args
*arg
= (struct warning_args
*)mesg
;
1592 #ifdef RUBY_FUNCTION_NAME_STRING
1593 rb_sys_enc_warning(arg
->enc
, "%s: %s", arg
->func
, arg
->mesg
);
1595 rb_sys_enc_warning(arg
->enc
, "%s", arg
->mesg
);
1601 sys_enc_warning_in(const char *func
, const char *mesg
, rb_encoding
*enc
)
1603 struct warning_args arg
;
1604 #ifdef RUBY_FUNCTION_NAME_STRING
1609 rb_protect(sys_warning_1
, (VALUE
)&arg
, 0);
1612 #define GLOB_VERBOSE (1U << (sizeof(int) * CHAR_BIT - 1))
1613 #define sys_warning(val, enc) \
1614 ((flags & GLOB_VERBOSE) ? sys_enc_warning_in(RUBY_FUNCTION_NAME_STRING, (val), (enc)) :(void)0)
1616 static inline size_t
1617 glob_alloc_size(size_t x
, size_t y
)
1620 if (rb_mul_size_overflow(x
, y
, SSIZE_MAX
, &z
)) {
1621 rb_memerror(); /* or...? */
1628 static inline void *
1629 glob_alloc_n(size_t x
, size_t y
)
1631 return malloc(glob_alloc_size(x
, y
));
1634 static inline void *
1635 glob_realloc_n(void *p
, size_t x
, size_t y
)
1637 return realloc(p
, glob_alloc_size(x
, y
));
1640 #define GLOB_ALLOC(type) ((type *)malloc(sizeof(type)))
1641 #define GLOB_ALLOC_N(type, n) ((type *)glob_alloc_n(sizeof(type), n))
1642 #define GLOB_REALLOC(ptr, size) realloc((ptr), (size))
1643 #define GLOB_REALLOC_N(ptr, n) glob_realloc_n(ptr, sizeof(*(ptr)), n)
1644 #define GLOB_FREE(ptr) free(ptr)
1645 #define GLOB_JUMP_TAG(status) (((status) == -1) ? rb_memerror() : rb_jump_tag(status))
1648 * ENOTDIR can be returned by stat(2) if a non-leaf element of the path
1649 * is not a directory.
1651 ALWAYS_INLINE(static int to_be_ignored(int e
));
1653 to_be_ignored(int e
)
1655 return e
== ENOENT
|| e
== ENOTDIR
;
1659 #define STAT(p, s) rb_w32_ustati128((p), (s))
1661 #define lstat(p, s) rb_w32_ulstati128((p), (s))
1663 #define STAT(p, s) stat((p), (s))
1666 typedef int ruby_glob_errfunc(const char*, VALUE
, const void*, int);
1668 ruby_glob_func
*match
;
1669 ruby_glob_errfunc
*error
;
1670 } ruby_glob_funcs_t
;
1673 at_subpath(int fd
, size_t baselen
, const char *path
)
1676 if (fd
!= (int)AT_FDCWD
&& baselen
> 0) {
1678 if (*path
== '/') ++path
;
1681 return *path
? path
: ".";
1684 /* System call with warning */
1686 do_stat(int fd
, size_t baselen
, const char *path
, struct stat
*pst
, int flags
, rb_encoding
*enc
)
1689 int ret
= fstatat(fd
, at_subpath(fd
, baselen
, path
), pst
, 0);
1691 int ret
= STAT(path
, pst
);
1693 if (ret
< 0 && !to_be_ignored(errno
))
1694 sys_warning(path
, enc
);
1699 #if defined HAVE_LSTAT || defined lstat || USE_OPENDIR_AT
1701 do_lstat(int fd
, size_t baselen
, const char *path
, struct stat
*pst
, int flags
, rb_encoding
*enc
)
1704 int ret
= fstatat(fd
, at_subpath(fd
, baselen
, path
), pst
, AT_SYMLINK_NOFOLLOW
);
1706 int ret
= lstat(path
, pst
);
1708 if (ret
< 0 && !to_be_ignored(errno
))
1709 sys_warning(path
, enc
);
1714 #define do_lstat do_stat
1717 struct opendir_at_arg
{
1723 with_gvl_gc_for_fd(void *ptr
)
1727 return (void *)RBOOL(rb_gc_for_fd(*e
));
1731 gc_for_fd_with_gvl(int e
)
1734 return (int)(VALUE
)rb_thread_call_with_gvl(with_gvl_gc_for_fd
, &e
);
1736 return RBOOL(rb_gc_for_fd(e
));
1740 nogvl_opendir_at(void *ptr
)
1742 const struct opendir_at_arg
*oaa
= ptr
;
1746 const int opendir_flags
= (O_RDONLY
|O_CLOEXEC
|
1749 # endif /* O_DIRECTORY */
1751 int fd
= openat(oaa
->basefd
, oaa
->path
, opendir_flags
);
1753 dirp
= fd
>= 0 ? fdopendir(fd
) : 0;
1757 switch (gc_for_fd_with_gvl(e
)) {
1759 if (fd
< 0) fd
= openat(oaa
->basefd
, oaa
->path
, opendir_flags
);
1760 if (fd
>= 0) dirp
= fdopendir(fd
);
1761 if (dirp
) return dirp
;
1766 if (fd
>= 0) close(fd
);
1770 #else /* !USE_OPENDIR_AT */
1771 dirp
= opendir(oaa
->path
);
1772 if (!dirp
&& gc_for_fd_with_gvl(errno
))
1773 dirp
= opendir(oaa
->path
);
1774 #endif /* !USE_OPENDIR_AT */
1780 opendir_at(int basefd
, const char *path
)
1782 struct opendir_at_arg oaa
;
1784 oaa
.basefd
= basefd
;
1788 return IO_WITHOUT_GVL(nogvl_opendir_at
, &oaa
);
1790 return nogvl_opendir_at(&oaa
);
1794 do_opendir(const int basefd
, size_t baselen
, const char *path
, int flags
, rb_encoding
*enc
,
1795 ruby_glob_errfunc
*errfunc
, VALUE arg
, int *status
)
1800 if (!fundamental_encoding_p(enc
)) {
1801 tmp
= rb_enc_str_new(path
, strlen(path
), enc
);
1802 tmp
= rb_str_encode_ospath(tmp
);
1803 path
= RSTRING_PTR(tmp
);
1806 dirp
= opendir_at(basefd
, at_subpath(basefd
, baselen
, path
));
1811 if (!to_be_ignored(e
)) {
1813 *status
= (*errfunc
)(path
, arg
, enc
, e
);
1816 sys_warning(path
, enc
);
1821 if (tmp
) rb_str_resize(tmp
, 0); /* GC guard */
1827 /* Globing pattern */
1828 enum glob_pattern_type
{ PLAIN
, ALPHA
, BRACE
, MAGICAL
, RECURSIVE
, MATCH_ALL
, MATCH_DIR
};
1830 /* Return nonzero if S has any special globbing chars in it. */
1831 static enum glob_pattern_type
1832 has_magic(const char *p
, const char *pend
, int flags
, rb_encoding
*enc
)
1834 const int escape
= !(flags
& FNM_NOESCAPE
);
1840 while (p
< pend
&& (c
= *p
++) != 0) {
1852 if (escape
&& p
++ >= pend
)
1865 if (IS_WIN32
|| ISALPHA(c
)) {
1871 p
= Next(p
-1, pend
, enc
);
1874 return hasmagical
? MAGICAL
: hasalpha
? ALPHA
: PLAIN
;
1877 /* Find separator in globbing pattern. */
1879 find_dirsep(const char *p
, const char *pend
, int flags
, rb_encoding
*enc
)
1881 const int escape
= !(flags
& FNM_NOESCAPE
);
1886 while ((c
= *p
++) != 0) {
1908 if (escape
&& !(c
= *p
++))
1913 p
= Next(p
-1, pend
, enc
);
1919 /* Remove escaping backslashes */
1921 remove_backslashes(char *p
, register const char *pend
, rb_encoding
*enc
)
1929 memmove(t
, s
, p
- s
);
1940 memmove(t
, s
, p
- s
); /* move '\0' too */
1945 struct glob_pattern
{
1947 enum glob_pattern_type type
;
1948 struct glob_pattern
*next
;
1951 static void glob_free_pattern(struct glob_pattern
*list
);
1953 static struct glob_pattern
*
1954 glob_make_pattern(const char *p
, const char *e
, int flags
, rb_encoding
*enc
)
1956 struct glob_pattern
*list
, *tmp
, **tail
= &list
;
1957 int dirsep
= 0; /* pattern is terminated with '/' */
1960 while (p
< e
&& *p
) {
1961 tmp
= GLOB_ALLOC(struct glob_pattern
);
1962 if (!tmp
) goto error
;
1963 if (p
+ 2 < e
&& p
[0] == '*' && p
[1] == '*' && p
[2] == '/') {
1964 /* fold continuous RECURSIVEs (needed in glob_helper) */
1965 do { p
+= 3; while (*p
== '/') p
++; } while (p
[0] == '*' && p
[1] == '*' && p
[2] == '/');
1966 tmp
->type
= RECURSIVE
;
1972 const char *m
= find_dirsep(p
, e
, flags
, enc
);
1973 const enum glob_pattern_type magic
= has_magic(p
, m
, flags
, enc
);
1974 const enum glob_pattern_type non_magic
= (USE_NAME_ON_FS
|| FNM_SYSCASE
) ? PLAIN
: ALPHA
;
1977 if (!(FNM_SYSCASE
|| magic
> non_magic
) && !recursive
&& *m
) {
1979 while (has_magic(m
+1, m2
= find_dirsep(m
+1, e
, flags
, enc
), flags
, enc
) <= non_magic
&&
1984 buf
= GLOB_ALLOC_N(char, m
-p
+1);
1989 memcpy(buf
, p
, m
-p
);
1991 tmp
->type
= magic
> MAGICAL
? MAGICAL
: magic
> non_magic
? magic
: PLAIN
;
2006 tmp
= GLOB_ALLOC(struct glob_pattern
);
2010 tmp
->type
= dirsep
? MATCH_DIR
: MATCH_ALL
;
2019 glob_free_pattern(list
);
2024 glob_free_pattern(struct glob_pattern
*list
)
2027 struct glob_pattern
*tmp
= list
;
2030 GLOB_FREE(tmp
->str
);
2036 join_path(const char *path
, size_t len
, int dirsep
, const char *name
, size_t namlen
)
2038 char *buf
= GLOB_ALLOC_N(char, len
+namlen
+(dirsep
?1:0)+1);
2041 memcpy(buf
, path
, len
);
2045 memcpy(buf
+len
, name
, namlen
);
2046 buf
[len
+namlen
] = '\0';
2050 #ifdef HAVE_GETATTRLIST
2051 # if defined HAVE_FGETATTRLIST
2052 # define is_case_sensitive(dirp, path) is_case_sensitive(dirp)
2054 # define is_case_sensitive(dirp, path) is_case_sensitive(path)
2057 is_case_sensitive(DIR *dirp
, const char *path
)
2061 vol_capabilities_attr_t cap
[1];
2062 } __attribute__((aligned(4), packed
)) attrbuf
[1];
2063 struct attrlist al
= {ATTR_BIT_MAP_COUNT
, 0, 0, ATTR_VOL_INFO
|ATTR_VOL_CAPABILITIES
};
2064 const vol_capabilities_attr_t
*const cap
= attrbuf
[0].cap
;
2065 const int idx
= VOL_CAPABILITIES_FORMAT
;
2066 const uint32_t mask
= VOL_CAP_FMT_CASE_SENSITIVE
;
2068 # if defined HAVE_FGETATTRLIST
2069 if (fgetattrlist(dirfd(dirp
), &al
, attrbuf
, sizeof(attrbuf
), FSOPT_NOFOLLOW
))
2072 if (getattrlist(path
, &al
, attrbuf
, sizeof(attrbuf
), FSOPT_NOFOLLOW
))
2075 if (!(cap
->valid
[idx
] & mask
))
2077 return (cap
->capabilities
[idx
] & mask
) != 0;
2081 replace_real_basename(char *path
, long base
, rb_encoding
*enc
, int norm_p
, int flags
, rb_pathtype_t
*type
)
2085 attrreference_t ref
[1];
2086 fsobj_type_t objtype
;
2087 char path
[MAXPATHLEN
* 3];
2088 } __attribute__((aligned(4), packed
)) attrbuf
[1];
2089 struct attrlist al
= {ATTR_BIT_MAP_COUNT
, 0, ATTR_CMN_NAME
|ATTR_CMN_OBJTYPE
};
2090 const attrreference_t
*const ar
= attrbuf
[0].ref
;
2094 IF_NORMALIZE_UTF8PATH(VALUE utf8str
= Qnil
);
2097 if (getattrlist(path
, &al
, attrbuf
, sizeof(attrbuf
), FSOPT_NOFOLLOW
)) {
2098 if (!to_be_ignored(errno
))
2099 sys_warning(path
, enc
);
2103 switch (attrbuf
[0].objtype
) {
2104 case VREG
: *type
= path_regular
; break;
2105 case VDIR
: *type
= path_directory
; break;
2106 case VLNK
: *type
= path_symlink
; break;
2107 default: *type
= path_exist
; break;
2109 name
= (char *)ar
+ ar
->attr_dataoffset
;
2110 len
= (long)ar
->attr_length
- 1;
2111 if (name
+ len
> (char *)attrbuf
+ sizeof(attrbuf
))
2114 # if NORMALIZE_UTF8PATH
2115 if (norm_p
&& has_nonascii(name
, len
)) {
2116 if (!NIL_P(utf8str
= rb_str_normalize_ospath(name
, len
))) {
2117 RSTRING_GETMEM(utf8str
, name
, len
);
2122 tmp
= GLOB_REALLOC(path
, base
+ len
+ 1);
2125 memcpy(path
+ base
, name
, len
);
2126 path
[base
+ len
] = '\0';
2128 IF_NORMALIZE_UTF8PATH(if (!NIL_P(utf8str
)) rb_str_resize(utf8str
, 0));
2131 #elif defined _WIN32
2132 VALUE
rb_w32_conv_from_wchar(const WCHAR
*wstr
, rb_encoding
*enc
);
2133 int rb_w32_reparse_symlink_p(const WCHAR
*path
);
2136 replace_real_basename(char *path
, long base
, rb_encoding
*enc
, int norm_p
, int flags
, rb_pathtype_t
*type
)
2138 char *plainname
= path
;
2139 volatile VALUE tmp
= 0;
2140 WIN32_FIND_DATAW fd
;
2141 WIN32_FILE_ATTRIBUTE_DATA fa
;
2143 HANDLE h
= INVALID_HANDLE_VALUE
;
2146 if (!fundamental_encoding_p(enc
)) {
2147 tmp
= rb_enc_str_new_cstr(plainname
, enc
);
2148 tmp
= rb_str_encode_ospath(tmp
);
2149 plainname
= RSTRING_PTR(tmp
);
2151 wplain
= rb_w32_mbstr_to_wstr(CP_UTF8
, plainname
, -1, &wlen
);
2152 if (tmp
) rb_str_resize(tmp
, 0);
2153 if (!wplain
) return path
;
2154 if (GetFileAttributesExW(wplain
, GetFileExInfoStandard
, &fa
)) {
2155 h
= FindFirstFileW(wplain
, &fd
);
2156 e
= rb_w32_map_errno(GetLastError());
2158 if (fa
.dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
) {
2159 if (!rb_w32_reparse_symlink_p(wplain
))
2160 fa
.dwFileAttributes
&= ~FILE_ATTRIBUTE_REPARSE_POINT
;
2163 if (h
== INVALID_HANDLE_VALUE
) {
2165 if (e
&& !to_be_ignored(e
)) {
2167 sys_warning(path
, enc
);
2173 (fa
.dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
) ? path_symlink
:
2174 (fa
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) ? path_directory
:
2178 tmp
= rb_w32_conv_from_wchar(fd
.cFileName
, enc
);
2179 wlen
= RSTRING_LEN(tmp
);
2180 buf
= GLOB_REALLOC(path
, base
+ wlen
+ 1);
2183 memcpy(path
+ base
, RSTRING_PTR(tmp
), wlen
);
2184 path
[base
+ wlen
] = 0;
2186 rb_str_resize(tmp
, 0);
2190 wlen
= WideCharToMultiByte(CP_UTF8
, 0, fd
.cFileName
, -1, NULL
, 0, NULL
, NULL
);
2191 utf8filename
= GLOB_REALLOC(0, wlen
);
2194 WideCharToMultiByte(CP_UTF8
, 0, fd
.cFileName
, -1, utf8filename
, wlen
, NULL
, NULL
);
2195 buf
= GLOB_REALLOC(path
, base
+ wlen
+ 1);
2198 memcpy(path
+ base
, utf8filename
, wlen
);
2199 path
[base
+ wlen
] = 0;
2201 GLOB_FREE(utf8filename
);
2206 #elif USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME
2207 # error not implemented
2211 # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
2216 # define S_ISLNK(m) (0)
2218 # define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
2223 void (*func
)(const char *, VALUE
, void *);
2231 #define glob_call_func(func, path, arg, enc) (*(func))((path), (arg), (void *)(enc))
2234 glob_func_caller(VALUE val
)
2236 struct glob_args
*args
= (struct glob_args
*)val
;
2238 glob_call_func(args
->func
, args
->path
, args
->value
, args
->enc
);
2242 struct glob_error_args
{
2249 glob_func_warning(VALUE val
)
2251 struct glob_error_args
*arg
= (struct glob_error_args
*)val
;
2252 rb_syserr_enc_warning(arg
->error
, arg
->enc
, "%s", arg
->path
);
2258 rb_glob_warning(const char *path
, VALUE a
, const void *enc
, int error
)
2261 struct glob_error_args args
;
2266 rb_protect(glob_func_warning
, (VALUE
)&args
, &status
);
2271 NORETURN(static VALUE
glob_func_error(VALUE val
));
2274 glob_func_error(VALUE val
)
2276 struct glob_error_args
*arg
= (struct glob_error_args
*)val
;
2277 VALUE path
= rb_enc_str_new_cstr(arg
->path
, arg
->enc
);
2278 rb_syserr_fail_str(arg
->error
, path
);
2279 UNREACHABLE_RETURN(Qnil
);
2283 rb_glob_error(const char *path
, VALUE a
, const void *enc
, int error
)
2286 struct glob_error_args args
;
2287 VALUE (*errfunc
)(VALUE
) = glob_func_error
;
2294 errfunc
= glob_func_warning
;
2299 rb_protect(errfunc
, (VALUE
)&args
, &status
);
2303 typedef struct rb_dirent
{
2307 const char *d_altname
;
2313 dirent_match(const char *pat
, rb_encoding
*enc
, const char *name
, const rb_dirent_t
*dp
, int flags
)
2315 if (fnmatch(pat
, enc
, name
, flags
) == 0) return 1;
2317 if (dp
->d_altname
&& (flags
& FNM_SHORTNAME
)) {
2318 if (fnmatch(pat
, enc
, dp
->d_altname
, flags
) == 0) return 1;
2324 struct push_glob_args
{
2329 int dirsep
; /* '/' should be placed before appending child entry's name to 'path'. */
2330 rb_pathtype_t pathtype
; /* type of 'path' */
2332 const ruby_glob_funcs_t
*funcs
;
2336 struct dirent_brace_args
{
2338 const rb_dirent_t
*dp
;
2343 dirent_match_brace(const char *pattern
, VALUE val
, void *enc
)
2345 struct dirent_brace_args
*arg
= (struct dirent_brace_args
*)val
;
2347 return dirent_match(pattern
, enc
, arg
->name
, arg
->dp
, arg
->flags
);
2350 /* join paths from pattern list of glob_make_pattern() */
2352 join_path_from_pattern(struct glob_pattern
**beg
)
2354 struct glob_pattern
*p
;
2356 size_t path_len
= 0;
2358 for (p
= *beg
; p
; p
= p
->next
) {
2365 /* append last slash */
2373 path_len
= strlen(str
);
2374 path
= GLOB_ALLOC_N(char, path_len
+ 1);
2376 memcpy(path
, str
, path_len
);
2377 path
[path_len
] = '\0';
2381 size_t len
= strlen(str
);
2383 tmp
= GLOB_REALLOC(path
, path_len
+ len
+ 2);
2386 path
[path_len
++] = '/';
2387 memcpy(path
+ path_len
, str
, len
);
2389 path
[path_len
] = '\0';
2396 static int push_caller(const char *path
, VALUE val
, void *enc
);
2398 static int ruby_brace_expand(const char *str
, int flags
, ruby_glob_func
*func
, VALUE arg
,
2399 rb_encoding
*enc
, VALUE var
);
2401 static const size_t rb_dirent_name_offset
=
2402 offsetof(rb_dirent_t
, d_type
) + sizeof(uint8_t);
2404 static rb_dirent_t
*
2405 dirent_copy(const struct dirent
*dp
, rb_dirent_t
*rdp
)
2407 if (!dp
) return NULL
;
2408 size_t namlen
= NAMLEN(dp
);
2409 const size_t altlen
=
2411 dp
->d_altlen
? dp
->d_altlen
+ 1 :
2414 rb_dirent_t
*newrdp
= rdp
;
2415 if (!rdp
&& !(newrdp
= malloc(rb_dirent_name_offset
+ namlen
+ 1 + altlen
)))
2417 newrdp
->d_namlen
= namlen
;
2419 char *name
= (char *)newrdp
+ rb_dirent_name_offset
;
2420 memcpy(name
, dp
->d_name
, namlen
);
2421 name
[namlen
] = '\0';
2423 newrdp
->d_altname
= NULL
;
2425 char *const altname
= name
+ namlen
+ 1;
2426 memcpy(altname
, dp
->d_altname
, altlen
- 1);
2427 altname
[altlen
- 1] = '\0';
2428 newrdp
->d_altname
= altname
;
2431 newrdp
->d_name
= name
;
2434 newrdp
->d_name
= dp
->d_name
;
2436 newrdp
->d_altname
= dp
->d_altname
;
2440 newrdp
->d_type
= dp
->d_type
;
2454 rb_dirent_t
**entries
;
2456 } ruby_glob_entries_t
;
2459 glob_sort_cmp(const void *a
, const void *b
, void *e
)
2461 const rb_dirent_t
*ent1
= *(void **)a
;
2462 const rb_dirent_t
*ent2
= *(void **)b
;
2463 return strcmp(ent1
->d_name
, ent2
->d_name
);
2467 glob_dir_finish(ruby_glob_entries_t
*ent
, int flags
)
2469 if (flags
& FNM_GLOB_NOSORT
) {
2470 closedir(ent
->nosort
.dirp
);
2471 ent
->nosort
.dirp
= NULL
;
2473 else if (ent
->sort
.entries
) {
2474 for (size_t i
= 0, count
= ent
->sort
.count
; i
< count
;) {
2475 GLOB_FREE(ent
->sort
.entries
[i
++]);
2477 GLOB_FREE(ent
->sort
.entries
);
2478 ent
->sort
.entries
= NULL
;
2479 ent
->sort
.count
= ent
->sort
.idx
= 0;
2483 static ruby_glob_entries_t
*
2484 glob_opendir(ruby_glob_entries_t
*ent
, DIR *dirp
, int flags
, rb_encoding
*enc
)
2486 MEMZERO(ent
, ruby_glob_entries_t
, 1);
2487 if (flags
& FNM_GLOB_NOSORT
) {
2488 ent
->nosort
.dirp
= dirp
;
2494 size_t count
= 0, capacity
= 0;
2495 ent
->sort
.count
= 0;
2497 ent
->sort
.entries
= 0;
2499 if ((capacity
= dirp
->nfiles
) > 0) {
2500 if (!(newp
= GLOB_ALLOC_N(rb_dirent_t
, capacity
))) {
2504 ent
->sort
.entries
= newp
;
2507 while ((dp
= READDIR(dirp
, enc
)) != NULL
) {
2508 rb_dirent_t
*rdp
= dirent_copy(dp
, NULL
);
2512 if (count
>= capacity
) {
2514 if (!(newp
= GLOB_REALLOC_N(ent
->sort
.entries
, capacity
)))
2516 ent
->sort
.entries
= newp
;
2518 ent
->sort
.entries
[count
++] = rdp
;
2519 ent
->sort
.count
= count
;
2522 if (count
< capacity
) {
2523 if (!(newp
= GLOB_REALLOC_N(ent
->sort
.entries
, count
))) {
2524 glob_dir_finish(ent
, 0);
2527 ent
->sort
.entries
= newp
;
2529 ruby_qsort(ent
->sort
.entries
, ent
->sort
.count
, sizeof(ent
->sort
.entries
[0]),
2530 glob_sort_cmp
, NULL
);
2535 glob_dir_finish(ent
, 0);
2540 static rb_dirent_t
*
2541 glob_getent(ruby_glob_entries_t
*ent
, int flags
, rb_encoding
*enc
)
2543 if (flags
& FNM_GLOB_NOSORT
) {
2544 return dirent_copy(READDIR(ent
->nosort
.dirp
, enc
), &ent
->nosort
.ent
);
2546 else if (ent
->sort
.idx
< ent
->sort
.count
) {
2547 return ent
->sort
.entries
[ent
->sort
.idx
++];
2560 int dirsep
, /* '/' should be placed before appending child entry's name to 'path'. */
2561 rb_pathtype_t pathtype
, /* type of 'path' */
2562 struct glob_pattern
**beg
,
2563 struct glob_pattern
**end
,
2565 const ruby_glob_funcs_t
*funcs
,
2571 struct glob_pattern
**cur
, **new_beg
, **new_end
;
2572 int plain
= 0, brace
= 0, magical
= 0, recursive
= 0, match_all
= 0, match_dir
= 0;
2573 int escape
= !(flags
& FNM_NOESCAPE
);
2574 size_t pathlen
= baselen
+ namelen
;
2576 rb_check_stack_overflow();
2578 for (cur
= beg
; cur
< end
; ++cur
) {
2579 struct glob_pattern
*p
= *cur
;
2580 if (p
->type
== RECURSIVE
) {
2589 #if USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME
2596 if (!recursive
|| strchr(p
->str
, '/')) {
2610 rb_bug("continuous RECURSIVEs");
2615 struct push_glob_args args
;
2616 char* brace_path
= join_path_from_pattern(beg
);
2617 if (!brace_path
) return -1;
2620 args
.baselen
= baselen
;
2621 args
.namelen
= namelen
;
2622 args
.dirsep
= dirsep
;
2623 args
.pathtype
= pathtype
;
2627 status
= ruby_brace_expand(brace_path
, flags
, push_caller
, (VALUE
)&args
, enc
, Qfalse
);
2628 GLOB_FREE(brace_path
);
2633 if (match_all
&& pathtype
== path_unknown
) {
2634 if (do_lstat(fd
, baselen
, path
, &st
, flags
, enc
) == 0) {
2635 pathtype
= IFTODT(st
.st_mode
);
2638 pathtype
= path_noent
;
2641 if (match_dir
&& (pathtype
== path_unknown
|| pathtype
== path_symlink
)) {
2642 if (do_stat(fd
, baselen
, path
, &st
, flags
, enc
) == 0) {
2643 pathtype
= IFTODT(st
.st_mode
);
2646 pathtype
= path_noent
;
2649 if (match_all
&& pathtype
> path_noent
) {
2650 const char *subpath
= path
+ baselen
+ (baselen
&& path
[baselen
] == '/');
2651 status
= glob_call_func(funcs
->match
, subpath
, arg
, enc
);
2652 if (status
) return status
;
2654 if (match_dir
&& pathtype
== path_directory
) {
2655 int seplen
= (baselen
&& path
[baselen
] == '/');
2656 const char *subpath
= path
+ baselen
+ seplen
;
2657 char *tmp
= join_path(subpath
, namelen
- seplen
, dirsep
, "", 0);
2658 if (!tmp
) return -1;
2659 status
= glob_call_func(funcs
->match
, tmp
, arg
, enc
);
2661 if (status
) return status
;
2665 if (pathtype
== path_noent
) return 0;
2667 if (magical
|| recursive
) {
2670 # if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH
2671 char *plainname
= 0;
2673 IF_NORMALIZE_UTF8PATH(int norm_p
);
2674 # if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH
2675 if (cur
+ 1 == end
&& (*cur
)->type
<= ALPHA
) {
2676 plainname
= join_path(path
, pathlen
, dirsep
, (*cur
)->str
, strlen((*cur
)->str
));
2677 if (!plainname
) return -1;
2678 dirp
= do_opendir(fd
, basename
, plainname
, flags
, enc
, funcs
->error
, arg
, &status
);
2679 GLOB_FREE(plainname
);
2685 dirp
= do_opendir(fd
, baselen
, path
, flags
, enc
, funcs
->error
, arg
, &status
);
2687 # if FNM_SYSCASE || NORMALIZE_UTF8PATH
2688 if ((magical
< 2) && !recursive
&& (errno
== EACCES
)) {
2689 /* no read permission, fallback */
2695 IF_NORMALIZE_UTF8PATH(norm_p
= need_normalization(dirp
, *path
? path
: "."));
2697 # if NORMALIZE_UTF8PATH
2698 if (!(norm_p
|| magical
|| recursive
)) {
2703 # ifdef HAVE_GETATTRLIST
2704 if (is_case_sensitive(dirp
, path
) == 0)
2705 flags
|= FNM_CASEFOLD
;
2707 ruby_glob_entries_t globent
;
2708 if (!glob_opendir(&globent
, dirp
, flags
, enc
)) {
2711 status
= (*funcs
->error
)(path
, arg
, enc
, ENOMEM
);
2714 sys_warning(path
, enc
);
2719 int skipdot
= (flags
& FNM_GLOB_SKIPDOT
);
2720 flags
|= FNM_GLOB_SKIPDOT
;
2722 while ((dp
= glob_getent(&globent
, flags
, enc
)) != NULL
) {
2724 rb_pathtype_t new_pathtype
= path_unknown
;
2728 IF_NORMALIZE_UTF8PATH(VALUE utf8str
= Qnil
);
2731 namlen
= dp
->d_namlen
;
2732 if (name
[0] == '.') {
2735 /* unless DOTMATCH, skip current directories not to recurse infinitely */
2736 if (recursive
&& !(flags
& FNM_DOTMATCH
)) continue;
2737 if (skipdot
) continue;
2739 new_pathtype
= path_directory
; /* force to skip stat/lstat */
2741 else if (namlen
== 2 && name
[1] == '.') {
2742 /* always skip parent directories not to recurse infinitely */
2747 # if NORMALIZE_UTF8PATH
2748 if (norm_p
&& has_nonascii(name
, namlen
)) {
2749 if (!NIL_P(utf8str
= rb_str_normalize_ospath(name
, namlen
))) {
2750 RSTRING_GETMEM(utf8str
, name
, namlen
);
2754 buf
= join_path(path
, pathlen
, dirsep
, name
, namlen
);
2755 IF_NORMALIZE_UTF8PATH(if (!NIL_P(utf8str
)) rb_str_resize(utf8str
, 0));
2760 name
= buf
+ pathlen
+ (dirsep
!= 0);
2762 if (dp
->d_type
!= DT_UNKNOWN
) {
2763 /* Got it. We need no more lstat. */
2764 new_pathtype
= dp
->d_type
;
2767 if (recursive
&& dotfile
< ((flags
& FNM_DOTMATCH
) ? 2 : 1) &&
2768 new_pathtype
== path_unknown
) {
2769 /* RECURSIVE never match dot files unless FNM_DOTMATCH is set */
2770 if (do_lstat(fd
, baselen
, buf
, &st
, flags
, enc
) == 0)
2771 new_pathtype
= IFTODT(st
.st_mode
);
2773 new_pathtype
= path_noent
;
2776 new_beg
= new_end
= GLOB_ALLOC_N(struct glob_pattern
*, (end
- beg
) * 2);
2783 for (cur
= beg
; cur
< end
; ++cur
) {
2784 struct glob_pattern
*p
= *cur
;
2785 struct dirent_brace_args args
;
2786 if (p
->type
== RECURSIVE
) {
2787 if (new_pathtype
== path_directory
|| /* not symlink but real directory */
2788 new_pathtype
== path_exist
) {
2789 if (dotfile
< ((flags
& FNM_DOTMATCH
) ? 2 : 1))
2790 *new_end
++ = p
; /* append recursive pattern */
2792 p
= p
->next
; /* 0 times recursion */
2799 if (ruby_brace_expand(p
->str
, flags
, dirent_match_brace
,
2800 (VALUE
)&args
, enc
, Qfalse
) > 0)
2801 *new_end
++ = p
->next
;
2804 # if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH
2806 *new_end
++ = p
->next
;
2812 if (dirent_match(p
->str
, enc
, name
, dp
, flags
))
2813 *new_end
++ = p
->next
;
2819 status
= glob_helper(fd
, buf
, baselen
, name
- buf
- baselen
+ namlen
, 1,
2820 new_pathtype
, new_beg
, new_end
,
2821 flags
, funcs
, arg
, enc
);
2827 glob_dir_finish(&globent
, flags
);
2830 struct glob_pattern
**copy_beg
, **copy_end
, **cur2
;
2832 # if FNM_SYSCASE || NORMALIZE_UTF8PATH
2835 copy_beg
= copy_end
= GLOB_ALLOC_N(struct glob_pattern
*, end
- beg
);
2836 if (!copy_beg
) return -1;
2837 for (cur
= beg
; cur
< end
; ++cur
)
2838 *copy_end
++ = (*cur
)->type
<= ALPHA
? *cur
: 0;
2840 for (cur
= copy_beg
; cur
< copy_end
; ++cur
) {
2842 rb_pathtype_t new_pathtype
= path_unknown
;
2845 size_t len
= strlen((*cur
)->str
) + 1;
2846 name
= GLOB_ALLOC_N(char, len
);
2851 memcpy(name
, (*cur
)->str
, len
);
2853 len
= remove_backslashes(name
, name
+len
-1, enc
) - name
;
2855 new_beg
= new_end
= GLOB_ALLOC_N(struct glob_pattern
*, end
- beg
);
2861 *new_end
++ = (*cur
)->next
;
2862 for (cur2
= cur
+ 1; cur2
< copy_end
; ++cur2
) {
2863 if (*cur2
&& fnmatch((*cur2
)->str
, enc
, name
, flags
) == 0) {
2864 *new_end
++ = (*cur2
)->next
;
2869 buf
= join_path(path
, pathlen
, dirsep
, name
, len
);
2876 #if USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME
2877 if ((*cur
)->type
== ALPHA
) {
2878 buf
= replace_real_basename(buf
, pathlen
+ (dirsep
!= 0), enc
,
2879 IF_NORMALIZE_UTF8PATH(1)+0,
2880 flags
, &new_pathtype
);
2884 status
= glob_helper(fd
, buf
, baselen
,
2885 namelen
+ strlen(buf
+ pathlen
), 1,
2886 new_pathtype
, new_beg
, new_end
,
2887 flags
, funcs
, arg
, enc
);
2894 GLOB_FREE(copy_beg
);
2901 push_caller(const char *path
, VALUE val
, void *enc
)
2903 struct push_glob_args
*arg
= (struct push_glob_args
*)val
;
2904 struct glob_pattern
*list
;
2907 list
= glob_make_pattern(path
, path
+ strlen(path
), arg
->flags
, enc
);
2911 status
= glob_helper(arg
->fd
, arg
->path
, arg
->baselen
, arg
->namelen
, arg
->dirsep
,
2912 arg
->pathtype
, &list
, &list
+ 1, arg
->flags
, arg
->funcs
,
2914 glob_free_pattern(list
);
2918 static int ruby_glob0(const char *path
, int fd
, const char *base
, int flags
,
2919 const ruby_glob_funcs_t
*funcs
, VALUE arg
, rb_encoding
*enc
);
2921 struct push_glob0_args
{
2925 const ruby_glob_funcs_t
*funcs
;
2930 push_glob0_caller(const char *path
, VALUE val
, void *enc
)
2932 struct push_glob0_args
*arg
= (struct push_glob0_args
*)val
;
2933 return ruby_glob0(path
, arg
->fd
, arg
->base
, arg
->flags
, arg
->funcs
, arg
->arg
, enc
);
2937 ruby_glob0(const char *path
, int fd
, const char *base
, int flags
,
2938 const ruby_glob_funcs_t
*funcs
, VALUE arg
,
2941 struct glob_pattern
*list
;
2942 const char *root
, *start
;
2944 size_t n
, baselen
= 0;
2945 int status
, dirsep
= FALSE
;
2947 start
= root
= path
;
2950 struct push_glob0_args args
;
2956 return ruby_brace_expand(path
, flags
, push_glob0_caller
, (VALUE
)&args
, enc
, Qfalse
);
2959 flags
|= FNM_SYSCASE
;
2961 root
= rb_enc_path_skip_prefix(root
, root
+ strlen(root
), enc
);
2964 if (*root
== '/') root
++;
2973 buf
= GLOB_ALLOC_N(char, n
+ 1);
2974 if (!buf
) return -1;
2975 MEMCPY(buf
, start
, char, n
);
2978 list
= glob_make_pattern(root
, root
+ strlen(root
), flags
, enc
);
2983 status
= glob_helper(fd
, buf
, baselen
, n
-baselen
, dirsep
,
2984 path_unknown
, &list
, &list
+ 1,
2985 flags
, funcs
, arg
, enc
);
2986 glob_free_pattern(list
);
2993 ruby_glob(const char *path
, int flags
, ruby_glob_func
*func
, VALUE arg
)
2995 ruby_glob_funcs_t funcs
;
2998 return ruby_glob0(path
, AT_FDCWD
, 0, flags
& ~GLOB_VERBOSE
,
2999 &funcs
, arg
, rb_ascii8bit_encoding());
3003 rb_glob_caller(const char *path
, VALUE a
, void *enc
)
3006 struct glob_args
*args
= (struct glob_args
*)a
;
3009 rb_protect(glob_func_caller
, a
, &status
);
3013 static const ruby_glob_funcs_t rb_glob_funcs
= {
3014 rb_glob_caller
, rb_glob_error
,
3018 rb_glob(const char *path
, void (*func
)(const char *, VALUE
, void *), VALUE arg
)
3020 struct glob_args args
;
3025 args
.enc
= rb_ascii8bit_encoding();
3027 status
= ruby_glob0(path
, AT_FDCWD
, 0, GLOB_VERBOSE
, &rb_glob_funcs
,
3028 (VALUE
)&args
, args
.enc
);
3029 if (status
) GLOB_JUMP_TAG(status
);
3033 push_pattern(const char *path
, VALUE ary
, void *enc
)
3035 #if defined _WIN32 || defined __APPLE__
3036 VALUE name
= rb_utf8_str_new_cstr(path
);
3037 rb_encoding
*eenc
= rb_default_internal_encoding();
3038 name
= rb_str_conv_enc(name
, NULL
, eenc
? eenc
: enc
);
3040 VALUE name
= rb_external_str_new_with_enc(path
, strlen(path
), enc
);
3042 rb_ary_push(ary
, name
);
3046 ruby_brace_expand(const char *str
, int flags
, ruby_glob_func
*func
, VALUE arg
,
3047 rb_encoding
*enc
, VALUE var
)
3049 const int escape
= !(flags
& FNM_NOESCAPE
);
3050 const char *p
= str
;
3051 const char *pend
= p
+ strlen(p
);
3053 const char *lbrace
= 0, *rbrace
= 0;
3054 int nest
= 0, status
= 0;
3057 if (*p
== '{' && nest
++ == 0) {
3060 if (*p
== '}' && lbrace
&& --nest
== 0) {
3064 if (*p
== '\\' && escape
) {
3070 if (lbrace
&& rbrace
) {
3071 size_t len
= strlen(s
) + 1;
3072 char *buf
= GLOB_ALLOC_N(char, len
);
3075 if (!buf
) return -1;
3076 memcpy(buf
, s
, lbrace
-s
);
3079 while (p
< rbrace
) {
3080 const char *t
= ++p
;
3082 while (p
< rbrace
&& !(*p
== ',' && nest
== 0)) {
3083 if (*p
== '{') nest
++;
3084 if (*p
== '}') nest
--;
3085 if (*p
== '\\' && escape
) {
3086 if (++p
== rbrace
) break;
3090 memcpy(buf
+shift
, t
, p
-t
);
3091 strlcpy(buf
+shift
+(p
-t
), rbrace
+1, len
-(shift
+(p
-t
)));
3092 status
= ruby_brace_expand(buf
, flags
, func
, arg
, enc
, var
);
3097 else if (!lbrace
&& !rbrace
) {
3098 status
= glob_call_func(func
, s
, arg
, enc
);
3106 ruby_glob_funcs_t funcs
;
3112 glob_brace(const char *path
, VALUE val
, void *enc
)
3114 struct brace_args
*arg
= (struct brace_args
*)val
;
3116 return ruby_glob0(path
, AT_FDCWD
, 0, arg
->flags
, &arg
->funcs
, arg
->value
, enc
);
3120 ruby_brace_glob_with_enc(const char *str
, int flags
, ruby_glob_func
*func
, VALUE arg
, rb_encoding
*enc
)
3122 struct brace_args args
;
3124 flags
&= ~GLOB_VERBOSE
;
3125 args
.funcs
.match
= func
;
3126 args
.funcs
.error
= 0;
3129 return ruby_brace_expand(str
, flags
, glob_brace
, (VALUE
)&args
, enc
, Qfalse
);
3133 ruby_brace_glob(const char *str
, int flags
, ruby_glob_func
*func
, VALUE arg
)
3135 return ruby_brace_glob_with_enc(str
, flags
, func
, arg
, rb_ascii8bit_encoding());
3139 push_glob(VALUE ary
, VALUE str
, VALUE base
, int flags
)
3141 struct glob_args args
;
3143 rb_encoding
*enc
= rb_enc_get(str
);
3145 #if defined _WIN32 || defined __APPLE__
3146 str
= rb_str_encode_ospath(str
);
3148 if (rb_enc_to_index(enc
) == ENCINDEX_US_ASCII
)
3149 enc
= rb_filesystem_encoding();
3150 if (rb_enc_to_index(enc
) == ENCINDEX_US_ASCII
)
3151 enc
= rb_ascii8bit_encoding();
3152 flags
|= GLOB_VERBOSE
;
3153 args
.func
= push_pattern
;
3159 if (!RB_TYPE_P(base
, T_STRING
) || !rb_enc_check(str
, base
)) {
3160 struct dir_data
*dirp
= RTYPEDDATA_GET_DATA(base
);
3161 if (!dirp
->dir
) dir_closed();
3163 if ((fd
= dirfd(dirp
->dir
)) == -1)
3164 rb_sys_fail_path(dir_inspect(base
));
3168 args
.base
= RSTRING_PTR(base
);
3170 #if defined _WIN32 || defined __APPLE__
3171 enc
= rb_utf8_encoding();
3174 return ruby_glob0(RSTRING_PTR(str
), fd
, args
.base
, flags
, &rb_glob_funcs
,
3179 rb_push_glob(VALUE str
, VALUE base
, int flags
) /* '\0' is delimiter */
3184 /* can contain null bytes as separators */
3185 if (!RB_TYPE_P(str
, T_STRING
)) {
3188 else if (!rb_str_to_cstr(str
)) {
3189 rb_raise(rb_eArgError
, "nul-separated glob pattern is deprecated");
3192 rb_enc_check(str
, rb_enc_from_encoding(rb_usascii_encoding()));
3196 status
= push_glob(ary
, str
, base
, flags
);
3197 if (status
) GLOB_JUMP_TAG(status
);
3203 dir_globs(VALUE args
, VALUE base
, int flags
)
3205 VALUE ary
= rb_ary_new();
3208 for (i
= 0; i
< RARRAY_LEN(args
); ++i
) {
3210 VALUE str
= RARRAY_AREF(args
, i
);
3212 status
= push_glob(ary
, str
, base
, flags
);
3213 if (status
) GLOB_JUMP_TAG(status
);
3221 dir_glob_option_base(VALUE base
)
3223 if (NIL_OR_UNDEF_P(base
)) {
3227 if (rb_typeddata_is_kind_of(base
, &dir_data_type
)) {
3231 FilePathValue(base
);
3232 if (!RSTRING_LEN(base
)) return Qnil
;
3237 dir_glob_option_sort(VALUE sort
)
3239 return (rb_bool_expected(sort
, "sort", TRUE
) ? 0 : FNM_GLOB_NOSORT
);
3243 dir_s_aref(rb_execution_context_t
*ec
, VALUE obj
, VALUE args
, VALUE base
, VALUE sort
)
3245 const int flags
= dir_glob_option_sort(sort
);
3246 base
= dir_glob_option_base(base
);
3247 if (RARRAY_LEN(args
) == 1) {
3248 return rb_push_glob(RARRAY_AREF(args
, 0), base
, flags
);
3250 return dir_globs(args
, base
, flags
);
3254 dir_s_glob(rb_execution_context_t
*ec
, VALUE obj
, VALUE str
, VALUE rflags
, VALUE base
, VALUE sort
)
3256 VALUE ary
= rb_check_array_type(str
);
3257 const int flags
= (NUM2INT(rflags
) | dir_glob_option_sort(sort
)) & ~FNM_CASEFOLD
;
3258 base
= dir_glob_option_base(base
);
3260 ary
= rb_push_glob(str
, base
, flags
);
3263 ary
= dir_globs(ary
, base
, flags
);
3266 if (rb_block_given_p()) {
3274 dir_open_dir(int argc
, VALUE
*argv
)
3276 VALUE dir
= rb_funcallv_kw(rb_cDir
, rb_intern("open"), argc
, argv
, RB_PASS_CALLED_KEYWORDS
);
3278 rb_check_typeddata(dir
, &dir_data_type
);
3285 * Dir.foreach(dirpath, encoding: 'UTF-8') {|entry_name| ... } -> nil
3287 * Calls the block with each entry name in the directory at +dirpath+;
3288 * sets the given encoding onto each passed +entry_name+:
3290 * Dir.foreach('/example') {|entry_name| p entry_name }
3302 * Dir.foreach('/example') {|entry_name| p entry_name.encoding; break }
3303 * Dir.foreach('/example', encoding: 'US-ASCII') {|entry_name| p entry_name.encoding; break }
3308 * #<Encoding:US-ASCII>
3310 * See {String Encoding}[rdoc-ref:encodings.rdoc@String+Encoding].
3312 * Returns an enumerator if no block is given.
3315 dir_foreach(int argc
, VALUE
*argv
, VALUE io
)
3319 RETURN_ENUMERATOR(io
, argc
, argv
);
3320 dir
= dir_open_dir(argc
, argv
);
3321 rb_ensure(dir_each
, dir
, dir_close
, dir
);
3326 dir_collect(VALUE dir
)
3328 VALUE ary
= rb_ary_new();
3329 dir_each_entry(dir
, rb_ary_push
, ary
, FALSE
);
3335 * Dir.entries(dirname, encoding: 'UTF-8') -> array
3337 * Returns an array of the entry names in the directory at +dirpath+;
3338 * sets the given encoding onto each returned entry name:
3340 * Dir.entries('/example') # => ["config.h", "lib", "main.rb", "..", "."]
3341 * Dir.entries('/example').first.encoding
3342 * # => #<Encoding:UTF-8>
3343 * Dir.entries('/example', encoding: 'US-ASCII').first.encoding
3344 * # => #<Encoding:US-ASCII>
3346 * See {String Encoding}[rdoc-ref:encodings.rdoc@String+Encoding].
3348 * Raises an exception if the directory does not exist.
3351 dir_entries(int argc
, VALUE
*argv
, VALUE io
)
3355 dir
= dir_open_dir(argc
, argv
);
3356 return rb_ensure(dir_collect
, dir
, dir_close
, dir
);
3360 dir_each_child(VALUE dir
)
3362 return dir_each_entry(dir
, dir_yield
, Qnil
, TRUE
);
3367 * Dir.each_child(dirpath) {|entry_name| ... } -> nil
3368 * Dir.each_child(dirpath, encoding: 'UTF-8') {|entry_name| ... } -> nil
3370 * Like Dir.foreach, except that entries <tt>'.'</tt> and <tt>'..'</tt>
3374 dir_s_each_child(int argc
, VALUE
*argv
, VALUE io
)
3378 RETURN_ENUMERATOR(io
, argc
, argv
);
3379 dir
= dir_open_dir(argc
, argv
);
3380 rb_ensure(dir_each_child
, dir
, dir_close
, dir
);
3386 * each_child {|entry_name| ... } -> self
3388 * Calls the block with each entry name in +self+
3389 * except <tt>'.'</tt> and <tt>'..'</tt>:
3391 * dir = Dir.new('/example')
3392 * dir.each_child {|entry_name| p entry_name }
3400 * If no block is given, returns an enumerator.
3403 dir_each_child_m(VALUE dir
)
3405 RETURN_ENUMERATOR(dir
, 0, 0);
3406 return dir_each_entry(dir
, dir_yield
, Qnil
, TRUE
);
3413 * Returns an array of the entry names in +self+
3414 * except for <tt>'.'</tt> and <tt>'..'</tt>:
3416 * dir = Dir.new('/example')
3417 * dir.children # => ["config.h", "lib", "main.rb"]
3421 dir_collect_children(VALUE dir
)
3423 VALUE ary
= rb_ary_new();
3424 dir_each_entry(dir
, rb_ary_push
, ary
, TRUE
);
3430 * Dir.children(dirpath) -> array
3431 * Dir.children(dirpath, encoding: 'UTF-8') -> array
3433 * Returns an array of the entry names in the directory at +dirpath+
3434 * except for <tt>'.'</tt> and <tt>'..'</tt>;
3435 * sets the given encoding onto each returned entry name:
3437 * Dir.children('/example') # => ["config.h", "lib", "main.rb"]
3438 * Dir.children('/example').first.encoding
3439 * # => #<Encoding:UTF-8>
3440 * Dir.children('/example', encoding: 'US-ASCII').first.encoding
3441 * # => #<Encoding:US-ASCII>
3443 * See {String Encoding}[rdoc-ref:encodings.rdoc@String+Encoding].
3445 * Raises an exception if the directory does not exist.
3448 dir_s_children(int argc
, VALUE
*argv
, VALUE io
)
3452 dir
= dir_open_dir(argc
, argv
);
3453 return rb_ensure(dir_collect_children
, dir
, dir_close
, dir
);
3457 fnmatch_brace(const char *pattern
, VALUE val
, void *enc
)
3459 struct brace_args
*arg
= (struct brace_args
*)val
;
3460 VALUE path
= arg
->value
;
3461 rb_encoding
*enc_pattern
= enc
;
3462 rb_encoding
*enc_path
= rb_enc_get(path
);
3464 if (enc_pattern
!= enc_path
) {
3465 if (!rb_enc_asciicompat(enc_pattern
))
3467 if (!rb_enc_asciicompat(enc_path
))
3469 if (!rb_enc_str_asciionly_p(path
)) {
3470 int cr
= ENC_CODERANGE_7BIT
;
3471 long len
= strlen(pattern
);
3472 if (rb_str_coderange_scan_restartable(pattern
, pattern
+ len
,
3473 enc_pattern
, &cr
) != len
)
3475 if (cr
!= ENC_CODERANGE_7BIT
)
3479 return (fnmatch(pattern
, enc
, RSTRING_PTR(path
), arg
->flags
) == 0);
3484 file_s_fnmatch(int argc
, VALUE
*argv
, VALUE obj
)
3486 VALUE pattern
, path
;
3490 if (rb_scan_args(argc
, argv
, "21", &pattern
, &path
, &rflags
) == 3)
3491 flags
= NUM2INT(rflags
);
3495 StringValueCStr(pattern
);
3496 FilePathStringValue(path
);
3498 if (flags
& FNM_EXTGLOB
) {
3499 struct brace_args args
;
3503 if (ruby_brace_expand(RSTRING_PTR(pattern
), flags
, fnmatch_brace
,
3504 (VALUE
)&args
, rb_enc_get(pattern
), pattern
) > 0)
3508 rb_encoding
*enc
= rb_enc_compatible(pattern
, path
);
3509 if (!enc
) return Qfalse
;
3510 if (fnmatch(RSTRING_PTR(pattern
), enc
, RSTRING_PTR(path
), flags
) == 0)
3513 RB_GC_GUARD(pattern
);
3520 * Dir.home(user_name = nil) -> dirpath
3522 * Retruns the home directory path of the user specified with +user_name+
3523 * if it is not +nil+, or the current login user:
3525 * Dir.home # => "/home/me"
3526 * Dir.home('root') # => "/root"
3528 * Raises ArgumentError if +user_name+ is not a user name.
3531 dir_s_home(int argc
, VALUE
*argv
, VALUE obj
)
3536 rb_check_arity(argc
, 0, 1);
3537 user
= (argc
> 0) ? argv
[0] : Qnil
;
3540 rb_must_asciicompat(user
);
3541 u
= StringValueCStr(user
);
3543 return rb_home_dir_of(user
, rb_str_new(0, 0));
3546 return rb_default_home_dir(rb_str_new(0, 0));
3553 * Dir.exist?(dirpath) -> true or false
3555 * Returns whether +dirpath+ is a directory in the underlying file system:
3557 * Dir.exist?('/example') # => true
3558 * Dir.exist?('/nosuch') # => false
3559 * Dir.exist?('/example/main.rb') # => false
3561 * Same as File.directory?.
3565 rb_file_directory_p(void)
3571 nogvl_dir_empty_p(void *ptr
)
3573 const char *path
= ptr
;
3574 DIR *dir
= opendir(path
);
3576 VALUE result
= Qtrue
;
3580 switch (gc_for_fd_with_gvl(e
)) {
3582 dir
= opendir(path
);
3587 if (e
== ENOTDIR
) return (void *)Qfalse
;
3588 return (void *)INT2FIX(e
);
3591 while ((dp
= READDIR(dir
, NULL
)) != NULL
) {
3592 if (!to_be_skipped(dp
)) {
3598 return (void *)result
;
3603 * Dir.empty?(dirpath) -> true or false
3605 * Returns whether +dirpath+ specifies an empty directory:
3607 * dirpath = '/tmp/foo'
3608 * Dir.mkdir(dirpath)
3609 * Dir.empty?(dirpath) # => true
3610 * Dir.empty?('/example') # => false
3611 * Dir.empty?('/example/main.rb') # => false
3613 * Raises an exception if +dirpath+ does not specify a directory or file
3614 * in the underlying file system.
3617 rb_dir_s_empty_p(VALUE obj
, VALUE dirname
)
3621 enum {false_on_notdir
= 1};
3623 FilePathValue(dirname
);
3624 orig
= rb_str_dup_frozen(dirname
);
3625 dirname
= rb_str_encode_ospath(dirname
);
3626 dirname
= rb_str_dup_frozen(dirname
);
3627 path
= RSTRING_PTR(dirname
);
3629 #if defined HAVE_GETATTRLIST && defined ATTR_DIR_ENTRYCOUNT
3631 u_int32_t attrbuf
[SIZEUP32(fsobj_tag_t
)];
3632 struct attrlist al
= {ATTR_BIT_MAP_COUNT
, 0, ATTR_CMN_OBJTAG
,};
3633 if (getattrlist(path
, &al
, attrbuf
, sizeof(attrbuf
), 0) != 0)
3634 rb_sys_fail_path(orig
);
3635 if (*(const fsobj_tag_t
*)(attrbuf
+1) == VT_HFS
) {
3637 al
.dirattr
= ATTR_DIR_ENTRYCOUNT
;
3638 if (getattrlist(path
, &al
, attrbuf
, sizeof(attrbuf
), 0) == 0) {
3639 if (attrbuf
[0] >= 2 * sizeof(u_int32_t
))
3640 return RBOOL(attrbuf
[1] == 0);
3641 if (false_on_notdir
) return Qfalse
;
3643 rb_sys_fail_path(orig
);
3648 result
= (VALUE
)IO_WITHOUT_GVL(nogvl_dir_empty_p
, (void *)path
);
3649 if (FIXNUM_P(result
)) {
3650 rb_syserr_fail_path((int)FIX2LONG(result
), orig
);
3658 rb_gc_register_address(&chdir_lock
.path
);
3659 rb_gc_register_address(&chdir_lock
.thread
);
3661 rb_cDir
= rb_define_class("Dir", rb_cObject
);
3663 rb_include_module(rb_cDir
, rb_mEnumerable
);
3665 rb_define_alloc_func(rb_cDir
, dir_s_alloc
);
3666 rb_define_singleton_method(rb_cDir
,"for_fd", dir_s_for_fd
, 1);
3667 rb_define_singleton_method(rb_cDir
, "foreach", dir_foreach
, -1);
3668 rb_define_singleton_method(rb_cDir
, "entries", dir_entries
, -1);
3669 rb_define_singleton_method(rb_cDir
, "each_child", dir_s_each_child
, -1);
3670 rb_define_singleton_method(rb_cDir
, "children", dir_s_children
, -1);
3672 rb_define_method(rb_cDir
,"fileno", dir_fileno
, 0);
3673 rb_define_method(rb_cDir
,"path", dir_path
, 0);
3674 rb_define_method(rb_cDir
,"to_path", dir_path
, 0);
3675 rb_define_method(rb_cDir
,"inspect", dir_inspect
, 0);
3676 rb_define_method(rb_cDir
,"read", dir_read
, 0);
3677 rb_define_method(rb_cDir
,"each", dir_each
, 0);
3678 rb_define_method(rb_cDir
,"each_child", dir_each_child_m
, 0);
3679 rb_define_method(rb_cDir
,"children", dir_collect_children
, 0);
3680 rb_define_method(rb_cDir
,"rewind", dir_rewind
, 0);
3681 rb_define_method(rb_cDir
,"tell", dir_tell
, 0);
3682 rb_define_method(rb_cDir
,"seek", dir_seek
, 1);
3683 rb_define_method(rb_cDir
,"pos", dir_tell
, 0);
3684 rb_define_method(rb_cDir
,"pos=", dir_set_pos
, 1);
3685 rb_define_method(rb_cDir
,"close", dir_close
, 0);
3686 rb_define_method(rb_cDir
,"chdir", dir_chdir
, 0);
3688 rb_define_singleton_method(rb_cDir
,"fchdir", dir_s_fchdir
, 1);
3689 rb_define_singleton_method(rb_cDir
,"chdir", dir_s_chdir
, -1);
3690 rb_define_singleton_method(rb_cDir
,"getwd", dir_s_getwd
, 0);
3691 rb_define_singleton_method(rb_cDir
,"pwd", dir_s_getwd
, 0);
3692 rb_define_singleton_method(rb_cDir
,"chroot", dir_s_chroot
, 1);
3693 rb_define_singleton_method(rb_cDir
,"mkdir", dir_s_mkdir
, -1);
3694 rb_define_singleton_method(rb_cDir
,"rmdir", dir_s_rmdir
, 1);
3695 rb_define_singleton_method(rb_cDir
,"delete", dir_s_rmdir
, 1);
3696 rb_define_singleton_method(rb_cDir
,"unlink", dir_s_rmdir
, 1);
3697 rb_define_singleton_method(rb_cDir
,"home", dir_s_home
, -1);
3699 rb_define_singleton_method(rb_cDir
,"exist?", rb_file_directory_p
, 1);
3700 rb_define_singleton_method(rb_cDir
,"empty?", rb_dir_s_empty_p
, 1);
3702 rb_define_singleton_method(rb_cFile
,"fnmatch", file_s_fnmatch
, -1);
3703 rb_define_singleton_method(rb_cFile
,"fnmatch?", file_s_fnmatch
, -1);
3705 /* Document-const: FNM_NOESCAPE
3706 * {File::FNM_NOESCAPE}[rdoc-ref:File::Constants@File-3A-3AFNM_NOESCAPE] */
3707 rb_file_const("FNM_NOESCAPE", INT2FIX(FNM_NOESCAPE
));
3708 /* Document-const: FNM_PATHNAME
3709 * {File::FNM_PATHNAME}[rdoc-ref:File::Constants@File-3A-3AFNM_PATHNAME] */
3710 rb_file_const("FNM_PATHNAME", INT2FIX(FNM_PATHNAME
));
3711 /* Document-const: FNM_DOTMATCH
3712 * {File::FNM_DOTMATCH}[rdoc-ref:File::Constants@File-3A-3AFNM_DOTMATCH] */
3713 rb_file_const("FNM_DOTMATCH", INT2FIX(FNM_DOTMATCH
));
3714 /* Document-const: FNM_CASEFOLD
3715 * {File::FNM_CASEFOLD}[rdoc-ref:File::Constants@File-3A-3AFNM_CASEFOLD] */
3716 rb_file_const("FNM_CASEFOLD", INT2FIX(FNM_CASEFOLD
));
3717 /* Document-const: FNM_EXTGLOB
3718 * {File::FNM_EXTGLOB}[rdoc-ref:File::Constants@File-3A-3AFNM_EXTGLOB] */
3719 rb_file_const("FNM_EXTGLOB", INT2FIX(FNM_EXTGLOB
));
3720 /* Document-const: FNM_SYSCASE
3721 * {File::FNM_SYSCASE}[rdoc-ref:File::Constants@File-3A-3AFNM_SYSCASE] */
3722 rb_file_const("FNM_SYSCASE", INT2FIX(FNM_SYSCASE
));
3723 /* Document-const: FNM_SHORTNAME
3724 * {File::FNM_SHORTNAME}[rdoc-ref:File::Constants@File-3A-3AFNM_SHORTNAME] */
3725 rb_file_const("FNM_SHORTNAME", INT2FIX(FNM_SHORTNAME
));
3728 #include "dir.rbinc"