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 */
191 # define IFTODT(m) (((m) & S_IFMT) / ((~S_IFMT & (S_IFMT-1)) + 1))
196 path_exist
= DT_UNKNOWN
,
197 path_directory
= DT_DIR
,
198 path_regular
= DT_REG
,
199 path_symlink
= DT_LNK
,
202 path_directory
= IFTODT(S_IFDIR
),
203 path_regular
= IFTODT(S_IFREG
),
204 path_symlink
= IFTODT(S_IFLNK
),
210 #define FNM_NOESCAPE 0x01
211 #define FNM_PATHNAME 0x02
212 #define FNM_DOTMATCH 0x04
213 #define FNM_CASEFOLD 0x08
214 #define FNM_EXTGLOB 0x10
215 #if CASEFOLD_FILESYSTEM
216 #define FNM_SYSCASE FNM_CASEFOLD
218 #define FNM_SYSCASE 0
221 #define FNM_SHORTNAME 0x20
223 #define FNM_SHORTNAME 0
225 #define FNM_GLOB_NOSORT 0x40
226 #define FNM_GLOB_SKIPDOT 0x80
228 #define FNM_NOMATCH 1
231 # define Next(p, e, enc) ((p)+ rb_enc_mbclen((p), (e), (enc)))
232 # define Inc(p, e, enc) ((p) = Next((p), (e), (enc)))
236 const char *p
, /* pattern (next to '[') */
238 const char *s
, /* string */
243 const int nocase
= flags
& FNM_CASEFOLD
;
244 const int escape
= !(flags
& FNM_NOESCAPE
);
249 if (p
>= pend
) return NULL
;
250 if (*p
== '!' || *p
== '^') {
257 if (escape
&& *t1
== '\\')
261 p
= t1
+ (r
= rb_enc_mbclen(t1
, pend
, enc
));
262 if (p
>= pend
) return NULL
;
263 if (p
[0] == '-' && p
[1] != ']') {
264 const char *t2
= p
+ 1;
266 if (escape
&& *t2
== '\\')
270 p
= t2
+ (r2
= rb_enc_mbclen(t2
, pend
, enc
));
272 if ((r
<= (send
-s
) && memcmp(t1
, s
, r
) == 0) ||
273 (r2
<= (send
-s
) && memcmp(t2
, s
, r2
) == 0)) {
277 c1
= rb_enc_codepoint(s
, send
, enc
);
278 if (nocase
) c1
= rb_enc_toupper(c1
, enc
);
279 c2
= rb_enc_codepoint(t1
, pend
, enc
);
280 if (nocase
) c2
= rb_enc_toupper(c2
, enc
);
281 if (c1
< c2
) continue;
282 c2
= rb_enc_codepoint(t2
, pend
, enc
);
283 if (nocase
) c2
= rb_enc_toupper(c2
, enc
);
284 if (c1
> c2
) continue;
288 if (r
<= (send
-s
) && memcmp(t1
, s
, r
) == 0) {
292 if (!nocase
) continue;
293 c1
= rb_enc_toupper(rb_enc_codepoint(s
, send
, enc
), enc
);
294 c2
= rb_enc_toupper(rb_enc_codepoint(p
, pend
, enc
), enc
);
295 if (c1
!= c2
) continue;
300 return ok
== not ? NULL
: (char *)p
+ 1;
303 /* If FNM_PATHNAME is set, only path element will be matched. (up to '/' or '\0')
304 Otherwise, entire string will be matched.
305 End marker itself won't be compared.
306 And if function succeeds, *pcur reaches end marker.
308 #define UNESCAPE(p) (escape && *(p) == '\\' ? (p) + 1 : (p))
309 #define ISEND(p) (!*(p) || (pathname && *(p) == '/'))
310 #define RETURN(val) return *pcur = p, *scur = s, (val);
314 const char **pcur
, /* pattern */
315 const char **scur
, /* string */
319 const int period
= !(flags
& FNM_DOTMATCH
);
320 const int pathname
= flags
& FNM_PATHNAME
;
321 const int escape
= !(flags
& FNM_NOESCAPE
);
322 const int nocase
= flags
& FNM_CASEFOLD
;
324 const char *ptmp
= 0;
325 const char *stmp
= 0;
327 const char *p
= *pcur
;
328 const char *pend
= p
+ strlen(p
);
329 const char *s
= *scur
;
330 const char *send
= s
+ strlen(s
);
334 if (period
&& *s
== '.' && *UNESCAPE(p
) != '.') /* leading period */
340 do { p
++; } while (*p
== '*');
341 if (ISEND(UNESCAPE(p
))) {
362 if ((t
= bracket(p
+ 1, pend
, s
, send
, flags
, enc
)) != 0) {
374 RETURN(ISEND(p
) ? 0 : FNM_NOMATCH
);
377 r
= rb_enc_precise_mbclen(p
, pend
, enc
);
378 if (!MBCLEN_CHARFOUND_P(r
))
380 if (r
<= (send
-s
) && memcmp(p
, s
, r
) == 0) {
385 if (!nocase
) goto failed
;
386 if (rb_enc_toupper(rb_enc_codepoint(p
, pend
, enc
), enc
) !=
387 rb_enc_toupper(rb_enc_codepoint(s
, send
, enc
), enc
))
393 failed
: /* try next '*' position */
396 Inc(stmp
, send
, enc
); /* !ISEND(*stmp) */
411 const char *p
= pattern
;
412 const char *s
= string
;
413 const char *send
= s
+ strlen(string
);
414 const int period
= !(flags
& FNM_DOTMATCH
);
415 const int pathname
= flags
& FNM_PATHNAME
;
417 const char *ptmp
= 0;
418 const char *stmp
= 0;
422 if (p
[0] == '*' && p
[1] == '*' && p
[2] == '/') {
423 do { p
+= 3; } while (p
[0] == '*' && p
[1] == '*' && p
[2] == '/');
427 if (fnmatch_helper(&p
, &s
, flags
, enc
) == 0) {
428 while (*s
&& *s
!= '/') Inc(s
, send
, enc
);
437 /* failed : try next recursion */
438 if (ptmp
&& stmp
&& !(period
&& *stmp
== '.')) {
439 while (*stmp
&& *stmp
!= '/') Inc(stmp
, send
, enc
);
451 return fnmatch_helper(&p
, &s
, flags
, enc
);
465 struct dir_data
*dir
= ptr
;
466 rb_gc_mark(dir
->path
);
472 struct dir_data
*dir
= ptr
;
474 if (dir
->dir
) closedir(dir
->dir
);
479 dir_memsize(const void *ptr
)
481 return sizeof(struct dir_data
);
484 static const rb_data_type_t dir_data_type
= {
486 {dir_mark
, dir_free
, dir_memsize
,},
487 0, 0, RUBY_TYPED_WB_PROTECTED
| RUBY_TYPED_FREE_IMMEDIATELY
490 static VALUE
dir_close(VALUE
);
493 dir_s_alloc(VALUE klass
)
495 struct dir_data
*dirp
;
496 VALUE obj
= TypedData_Make_Struct(klass
, struct dir_data
, &dir_data_type
, dirp
);
499 RB_OBJ_WRITE(obj
, &dirp
->path
, Qnil
);
506 nogvl_opendir(void *ptr
)
508 const char *path
= ptr
;
510 return (void *)opendir(path
);
514 opendir_without_gvl(const char *path
)
516 if (vm_initialized
) {
517 union { const void *in
; void *out
; } u
;
521 return rb_thread_call_without_gvl(nogvl_opendir
, u
.out
, RUBY_UBF_IO
, 0);
524 return opendir(path
);
528 dir_initialize(rb_execution_context_t
*ec
, VALUE dir
, VALUE dirname
, VALUE enc
)
533 rb_encoding
*fsenc
= NIL_P(enc
) ? rb_filesystem_encoding() : rb_to_encoding(enc
);
535 FilePathValue(dirname
);
536 orig
= rb_str_dup_frozen(dirname
);
537 dirname
= rb_str_encode_ospath(dirname
);
538 dirname
= rb_str_dup_frozen(dirname
);
540 TypedData_Get_Struct(dir
, struct dir_data
, &dir_data_type
, dp
);
541 if (dp
->dir
) closedir(dp
->dir
);
543 RB_OBJ_WRITE(dir
, &dp
->path
, Qnil
);
545 path
= RSTRING_PTR(dirname
);
546 dp
->dir
= opendir_without_gvl(path
);
547 if (dp
->dir
== NULL
) {
549 if (rb_gc_for_fd(e
)) {
550 dp
->dir
= opendir_without_gvl(path
);
552 #ifdef HAVE_GETATTRLIST
554 u_int32_t attrbuf
[1];
555 struct attrlist al
= {ATTR_BIT_MAP_COUNT
, 0};
556 if (getattrlist(path
, &al
, attrbuf
, sizeof(attrbuf
), FSOPT_NOFOLLOW
) == 0) {
557 dp
->dir
= opendir_without_gvl(path
);
561 if (dp
->dir
== NULL
) {
562 RB_GC_GUARD(dirname
);
563 rb_syserr_fail_path(e
, orig
);
566 RB_OBJ_WRITE(dir
, &dp
->path
, orig
);
572 dir_s_open(rb_execution_context_t
*ec
, VALUE klass
, VALUE dirname
, VALUE enc
)
575 VALUE dir
= TypedData_Make_Struct(klass
, struct dir_data
, &dir_data_type
, dp
);
577 dir_initialize(ec
, dir
, dirname
, enc
);
583 dir_s_close(rb_execution_context_t
*ec
, VALUE klass
, VALUE dir
)
585 return dir_close(dir
);
588 NORETURN(static void dir_closed(void));
593 rb_raise(rb_eIOError
, "closed directory");
596 static struct dir_data
*
599 rb_check_frozen(dir
);
600 return rb_check_typeddata(dir
, &dir_data_type
);
603 static struct dir_data
*
606 struct dir_data
*dirp
= dir_get(dir
);
607 if (!dirp
->dir
) dir_closed();
611 #define GetDIR(obj, dirp) ((dirp) = dir_check(obj))
616 * dir.inspect -> string
618 * Return a string describing this Dir object.
621 dir_inspect(VALUE dir
)
623 struct dir_data
*dirp
;
625 TypedData_Get_Struct(dir
, struct dir_data
, &dir_data_type
, dirp
);
626 if (!NIL_P(dirp
->path
)) {
627 VALUE str
= rb_str_new_cstr("#<");
628 rb_str_append(str
, rb_class_name(CLASS_OF(dir
)));
629 rb_str_cat2(str
, ":");
630 rb_str_append(str
, dirp
->path
);
631 rb_str_cat2(str
, ">");
634 return rb_funcallv(dir
, idTo_s
, 0, 0);
637 /* Workaround for Solaris 10 that does not have dirfd.
638 Note: Solaris 11 (POSIX.1-2008 compliant) has dirfd(3C).
640 #if defined(__sun) && !defined(HAVE_DIRFD)
641 # if defined(HAVE_DIR_D_FD)
642 # define dirfd(x) ((x)->d_fd)
643 # define HAVE_DIRFD 1
644 # elif defined(HAVE_DIR_DD_FD)
645 # define dirfd(x) ((x)->dd_fd)
646 # define HAVE_DIRFD 1
653 * dir.fileno -> integer
655 * Returns the file descriptor used in <em>dir</em>.
660 * This method uses dirfd() function defined by POSIX 2008.
661 * NotImplementedError is raised on other platforms, such as Windows,
662 * which doesn't provide the function.
666 dir_fileno(VALUE dir
)
668 struct dir_data
*dirp
;
672 fd
= dirfd(dirp
->dir
);
674 rb_sys_fail("dirfd");
678 #define dir_fileno rb_f_notimplement
683 * dir.path -> string or nil
684 * dir.to_path -> string or nil
686 * Returns the path parameter passed to <em>dir</em>'s constructor.
694 struct dir_data
*dirp
;
696 TypedData_Get_Struct(dir
, struct dir_data
, &dir_data_type
, dirp
);
697 if (NIL_P(dirp
->path
)) return Qnil
;
698 return rb_str_dup(dirp
->path
);
703 fundamental_encoding_p(rb_encoding
*enc
)
705 switch (rb_enc_to_index(enc
)) {
707 case ENCINDEX_US_ASCII
:
714 # define READDIR(dir, enc) rb_w32_readdir((dir), (enc))
716 # define READDIR(dir, enc) readdir((dir))
719 /* safe to use without GVL */
721 to_be_skipped(const struct dirent
*dp
)
723 const char *name
= dp
->d_name
;
724 if (name
[0] != '.') return FALSE
;
725 #ifdef HAVE_DIRENT_NAMLEN
726 switch (NAMLEN(dp
)) {
728 if (name
[1] != '.') return FALSE
;
735 if (!name
[1]) return TRUE
;
736 if (name
[1] != '.') return FALSE
;
737 if (!name
[2]) return TRUE
;
744 * dir.read -> string or nil
746 * Reads the next entry from <em>dir</em> and returns it as a string.
747 * Returns <code>nil</code> at the end of the stream.
749 * d = Dir.new("testdir")
752 * d.read #=> "config.h"
757 struct dir_data
*dirp
;
762 if ((dp
= READDIR(dirp
->dir
, dirp
->enc
)) != NULL
) {
763 return rb_external_str_new_with_enc(dp
->d_name
, NAMLEN(dp
), dirp
->enc
);
767 if (e
!= 0) rb_syserr_fail(e
, 0);
768 return Qnil
; /* end of stream */
772 static VALUE
dir_each_entry(VALUE
, VALUE (*)(VALUE
, VALUE
), VALUE
, int);
775 dir_yield(VALUE arg
, VALUE path
)
777 return rb_yield(path
);
782 * dir.each { |filename| block } -> dir
783 * dir.each -> an_enumerator
785 * Calls the block once for each entry in this directory, passing the
786 * filename of each entry as a parameter to the block.
788 * If no block is given, an enumerator is returned instead.
790 * d = Dir.new("testdir")
791 * d.each {|x| puts "Got #{x}" }
803 RETURN_ENUMERATOR(dir
, 0, 0);
804 return dir_each_entry(dir
, dir_yield
, Qnil
, FALSE
);
808 dir_each_entry(VALUE dir
, VALUE (*each
)(VALUE
, VALUE
), VALUE arg
, int children_only
)
810 struct dir_data
*dirp
;
812 IF_NORMALIZE_UTF8PATH(int norm_p
);
815 rewinddir(dirp
->dir
);
816 IF_NORMALIZE_UTF8PATH(norm_p
= need_normalization(dirp
->dir
, RSTRING_PTR(dirp
->path
)));
817 while ((dp
= READDIR(dirp
->dir
, dirp
->enc
)) != NULL
) {
818 const char *name
= dp
->d_name
;
819 size_t namlen
= NAMLEN(dp
);
822 if (children_only
&& name
[0] == '.') {
823 if (namlen
== 1) continue; /* current directory */
824 if (namlen
== 2 && name
[1] == '.') continue; /* parent directory */
826 #if NORMALIZE_UTF8PATH
827 if (norm_p
&& has_nonascii(name
, namlen
) &&
828 !NIL_P(path
= rb_str_normalize_ospath(name
, namlen
))) {
829 path
= rb_external_str_with_enc(path
, dirp
->enc
);
833 path
= rb_external_str_new_with_enc(name
, namlen
, dirp
->enc
);
843 * dir.tell -> integer
845 * Returns the current position in <em>dir</em>. See also Dir#seek.
847 * d = Dir.new("testdir")
855 struct dir_data
*dirp
;
859 pos
= telldir(dirp
->dir
);
860 return rb_int2inum(pos
);
863 #define dir_tell rb_f_notimplement
869 * dir.seek( integer ) -> dir
871 * Seeks to a particular location in <em>dir</em>. <i>integer</i>
872 * must be a value returned by Dir#tell.
874 * d = Dir.new("testdir") #=> #<Dir:0x401b3c40>
878 * d.seek(i) #=> #<Dir:0x401b3c40>
882 dir_seek(VALUE dir
, VALUE pos
)
884 struct dir_data
*dirp
;
885 long p
= NUM2LONG(pos
);
888 seekdir(dirp
->dir
, p
);
892 #define dir_seek rb_f_notimplement
898 * dir.pos = integer -> integer
900 * Synonym for Dir#seek, but returns the position parameter.
902 * d = Dir.new("testdir") #=> #<Dir:0x401b3c40>
910 dir_set_pos(VALUE dir
, VALUE pos
)
916 #define dir_set_pos rb_f_notimplement
923 * Repositions <em>dir</em> to the first entry.
925 * d = Dir.new("testdir")
927 * d.rewind #=> #<Dir:0x401b3fb0>
931 dir_rewind(VALUE dir
)
933 struct dir_data
*dirp
;
936 rewinddir(dirp
->dir
);
944 * Closes the directory stream.
945 * Calling this method on closed Dir object is ignored since Ruby 2.3.
947 * d = Dir.new("testdir")
953 struct dir_data
*dirp
;
956 if (!dirp
->dir
) return Qnil
;
964 nogvl_chdir(void *ptr
)
966 const char *path
= ptr
;
968 return (void *)(VALUE
)chdir(path
);
972 dir_chdir(VALUE path
)
974 if (chdir(RSTRING_PTR(path
)) < 0)
975 rb_sys_fail_path(path
);
978 static int chdir_blocking
= 0;
979 static VALUE chdir_thread
= Qnil
;
982 VALUE old_path
, new_path
;
989 struct chdir_data
*args
= (void *)v
;
990 dir_chdir(args
->new_path
);
993 if (NIL_P(chdir_thread
))
994 chdir_thread
= rb_thread_current();
995 return rb_yield(args
->new_path
);
999 chdir_restore(VALUE v
)
1001 struct chdir_data
*args
= (void *)v
;
1004 if (chdir_blocking
== 0)
1005 chdir_thread
= Qnil
;
1006 dir_chdir(args
->old_path
);
1013 * Dir.chdir( [ string] ) -> 0
1014 * Dir.chdir( [ string] ) {| path | block } -> anObject
1016 * Changes the current working directory of the process to the given
1017 * string. When called without an argument, changes the directory to
1018 * the value of the environment variable <code>HOME</code>, or
1019 * <code>LOGDIR</code>. SystemCallError (probably Errno::ENOENT) if
1020 * the target directory does not exist.
1022 * If a block is given, it is passed the name of the new current
1023 * directory, and the block is executed with that as the current
1024 * directory. The original working directory is restored when the block
1025 * exits. The return value of <code>chdir</code> is the value of the
1026 * block. <code>chdir</code> blocks can be nested, but in a
1027 * multi-threaded program an error will be raised if a thread attempts
1028 * to open a <code>chdir</code> block while another thread has one
1029 * open or a call to <code>chdir</code> without a block occurs inside
1030 * a block passed to <code>chdir</code> (even in the same thread).
1032 * Dir.chdir("/var/spool/mail")
1034 * Dir.chdir("/tmp") do
1036 * Dir.chdir("/usr") do
1043 * <em>produces:</em>
1052 dir_s_chdir(int argc
, VALUE
*argv
, VALUE obj
)
1056 if (rb_check_arity(argc
, 0, 1) == 1) {
1057 path
= rb_str_encode_ospath(rb_get_path(argv
[0]));
1060 const char *dist
= getenv("HOME");
1062 dist
= getenv("LOGDIR");
1063 if (!dist
) rb_raise(rb_eArgError
, "HOME/LOGDIR not set");
1065 path
= rb_str_new2(dist
);
1068 if (chdir_blocking
> 0) {
1069 if (rb_thread_current() != chdir_thread
)
1070 rb_raise(rb_eRuntimeError
, "conflicting chdir during another chdir block");
1071 if (!rb_block_given_p())
1072 rb_warn("conflicting chdir during another chdir block");
1075 if (rb_block_given_p()) {
1076 struct chdir_data args
;
1078 args
.old_path
= rb_str_encode_ospath(rb_dir_getwd());
1079 args
.new_path
= path
;
1081 return rb_ensure(chdir_yield
, (VALUE
)&args
, chdir_restore
, (VALUE
)&args
);
1084 char *p
= RSTRING_PTR(path
);
1085 int r
= (int)(VALUE
)rb_thread_call_without_gvl(nogvl_chdir
, p
,
1088 rb_sys_fail_path(path
);
1096 rb_dir_getwd_ospath(void)
1102 #undef RUBY_UNTYPED_DATA_WARNING
1103 #define RUBY_UNTYPED_DATA_WARNING 0
1104 path_guard
= Data_Wrap_Struct((VALUE
)0, NULL
, RUBY_DEFAULT_FREE
, NULL
);
1105 path
= ruby_getcwd();
1106 DATA_PTR(path_guard
) = path
;
1108 cwd
= rb_str_normalize_ospath(path
, strlen(path
));
1110 cwd
= rb_str_new2(path
);
1112 DATA_PTR(path_guard
) = 0;
1122 rb_encoding
*fs
= rb_filesystem_encoding();
1123 int fsenc
= rb_enc_to_index(fs
);
1124 VALUE cwd
= rb_dir_getwd_ospath();
1127 case ENCINDEX_US_ASCII
:
1128 fsenc
= ENCINDEX_ASCII
;
1129 case ENCINDEX_ASCII
:
1131 #if defined _WIN32 || defined __APPLE__
1133 return rb_str_conv_enc(cwd
, NULL
, fs
);
1136 return rb_enc_associate_index(cwd
, fsenc
);
1141 * Dir.getwd -> string
1144 * Returns the path to the current working directory of this process as
1147 * Dir.chdir("/tmp") #=> 0
1148 * Dir.getwd #=> "/tmp"
1149 * Dir.pwd #=> "/tmp"
1152 dir_s_getwd(VALUE dir
)
1154 return rb_dir_getwd();
1158 check_dirname(VALUE dir
)
1166 enc
= rb_enc_get(d
);
1167 RSTRING_GETMEM(d
, path
, len
);
1169 pend
= rb_enc_path_end(rb_enc_path_skip_prefix(path
, pend
, enc
), pend
, enc
);
1170 if (pend
- path
< len
) {
1171 d
= rb_str_subseq(d
, 0, pend
- path
);
1174 return rb_str_encode_ospath(d
);
1177 #if defined(HAVE_CHROOT)
1180 * Dir.chroot( string ) -> 0
1182 * Changes this process's idea of the file system root. Only a
1183 * privileged process may make this call. Not available on all
1184 * platforms. On Unix systems, see <code>chroot(2)</code> for more
1188 dir_s_chroot(VALUE dir
, VALUE path
)
1190 path
= check_dirname(path
);
1191 if (chroot(RSTRING_PTR(path
)) == -1)
1192 rb_sys_fail_path(path
);
1197 #define dir_s_chroot rb_f_notimplement
1206 nogvl_mkdir(void *ptr
)
1208 struct mkdir_arg
*m
= ptr
;
1210 return (void *)(VALUE
)mkdir(m
->path
, m
->mode
);
1215 * Dir.mkdir( string [, integer] ) -> 0
1217 * Makes a new directory named by <i>string</i>, with permissions
1218 * specified by the optional parameter <i>anInteger</i>. The
1219 * permissions may be modified by the value of File::umask, and are
1220 * ignored on NT. Raises a SystemCallError if the directory cannot be
1221 * created. See also the discussion of permissions in the class
1222 * documentation for File.
1224 * Dir.mkdir(File.join(Dir.home, ".foo"), 0700) #=> 0
1228 dir_s_mkdir(int argc
, VALUE
*argv
, VALUE obj
)
1234 if (rb_scan_args(argc
, argv
, "11", &path
, &vmode
) == 2) {
1235 m
.mode
= NUM2MODET(vmode
);
1241 path
= check_dirname(path
);
1242 m
.path
= RSTRING_PTR(path
);
1243 r
= (int)(VALUE
)rb_thread_call_without_gvl(nogvl_mkdir
, &m
, RUBY_UBF_IO
, 0);
1245 rb_sys_fail_path(path
);
1251 nogvl_rmdir(void *ptr
)
1253 const char *path
= ptr
;
1255 return (void *)(VALUE
)rmdir(path
);
1260 * Dir.delete( string ) -> 0
1261 * Dir.rmdir( string ) -> 0
1262 * Dir.unlink( string ) -> 0
1264 * Deletes the named directory. Raises a subclass of SystemCallError
1265 * if the directory isn't empty.
1268 dir_s_rmdir(VALUE obj
, VALUE dir
)
1273 dir
= check_dirname(dir
);
1274 p
= RSTRING_PTR(dir
);
1275 r
= (int)(VALUE
)rb_thread_call_without_gvl(nogvl_rmdir
, (void *)p
, RUBY_UBF_IO
, 0);
1277 rb_sys_fail_path(dir
);
1282 struct warning_args
{
1283 #ifdef RUBY_FUNCTION_NAME_STRING
1290 #ifndef RUBY_FUNCTION_NAME_STRING
1291 #define sys_enc_warning_in(func, mesg, enc) sys_enc_warning(mesg, enc)
1295 sys_warning_1(VALUE mesg
)
1297 const struct warning_args
*arg
= (struct warning_args
*)mesg
;
1298 #ifdef RUBY_FUNCTION_NAME_STRING
1299 rb_sys_enc_warning(arg
->enc
, "%s: %s", arg
->func
, arg
->mesg
);
1301 rb_sys_enc_warning(arg
->enc
, "%s", arg
->mesg
);
1307 sys_enc_warning_in(const char *func
, const char *mesg
, rb_encoding
*enc
)
1309 struct warning_args arg
;
1310 #ifdef RUBY_FUNCTION_NAME_STRING
1315 rb_protect(sys_warning_1
, (VALUE
)&arg
, 0);
1318 #define GLOB_VERBOSE (1U << (sizeof(int) * CHAR_BIT - 1))
1319 #define sys_warning(val, enc) \
1320 ((flags & GLOB_VERBOSE) ? sys_enc_warning_in(RUBY_FUNCTION_NAME_STRING, (val), (enc)) :(void)0)
1322 static inline size_t
1323 glob_alloc_size(size_t x
, size_t y
)
1326 if (rb_mul_size_overflow(x
, y
, SSIZE_MAX
, &z
)) {
1327 rb_memerror(); /* or...? */
1334 static inline void *
1335 glob_alloc_n(size_t x
, size_t y
)
1337 return malloc(glob_alloc_size(x
, y
));
1340 static inline void *
1341 glob_realloc_n(void *p
, size_t x
, size_t y
)
1343 return realloc(p
, glob_alloc_size(x
, y
));
1346 #define GLOB_ALLOC(type) ((type *)malloc(sizeof(type)))
1347 #define GLOB_ALLOC_N(type, n) ((type *)glob_alloc_n(sizeof(type), n))
1348 #define GLOB_REALLOC(ptr, size) realloc((ptr), (size))
1349 #define GLOB_REALLOC_N(ptr, n) glob_realloc_n(ptr, sizeof(*(ptr)), n)
1350 #define GLOB_FREE(ptr) free(ptr)
1351 #define GLOB_JUMP_TAG(status) (((status) == -1) ? rb_memerror() : rb_jump_tag(status))
1354 * ENOTDIR can be returned by stat(2) if a non-leaf element of the path
1355 * is not a directory.
1357 ALWAYS_INLINE(static int to_be_ignored(int e
));
1359 to_be_ignored(int e
)
1361 return e
== ENOENT
|| e
== ENOTDIR
;
1365 #define STAT(p, s) rb_w32_ustati128((p), (s))
1367 #define lstat(p, s) rb_w32_ulstati128((p), (s))
1369 #define STAT(p, s) stat((p), (s))
1372 typedef int ruby_glob_errfunc(const char*, VALUE
, const void*, int);
1374 ruby_glob_func
*match
;
1375 ruby_glob_errfunc
*error
;
1376 } ruby_glob_funcs_t
;
1379 at_subpath(int fd
, size_t baselen
, const char *path
)
1382 if (fd
!= (int)AT_FDCWD
&& baselen
> 0) {
1384 if (*path
== '/') ++path
;
1387 return *path
? path
: ".";
1390 /* System call with warning */
1392 do_stat(int fd
, size_t baselen
, const char *path
, struct stat
*pst
, int flags
, rb_encoding
*enc
)
1395 int ret
= fstatat(fd
, at_subpath(fd
, baselen
, path
), pst
, 0);
1397 int ret
= STAT(path
, pst
);
1399 if (ret
< 0 && !to_be_ignored(errno
))
1400 sys_warning(path
, enc
);
1405 #if defined HAVE_LSTAT || defined lstat || USE_OPENDIR_AT
1407 do_lstat(int fd
, size_t baselen
, const char *path
, struct stat
*pst
, int flags
, rb_encoding
*enc
)
1410 int ret
= fstatat(fd
, at_subpath(fd
, baselen
, path
), pst
, AT_SYMLINK_NOFOLLOW
);
1412 int ret
= lstat(path
, pst
);
1414 if (ret
< 0 && !to_be_ignored(errno
))
1415 sys_warning(path
, enc
);
1420 #define do_lstat do_stat
1423 struct opendir_at_arg
{
1429 with_gvl_gc_for_fd(void *ptr
)
1433 return (void *)RBOOL(rb_gc_for_fd(*e
));
1437 gc_for_fd_with_gvl(int e
)
1440 return (int)(VALUE
)rb_thread_call_with_gvl(with_gvl_gc_for_fd
, &e
);
1442 return RBOOL(rb_gc_for_fd(e
));
1446 nogvl_opendir_at(void *ptr
)
1448 const struct opendir_at_arg
*oaa
= ptr
;
1452 const int opendir_flags
= (O_RDONLY
|O_CLOEXEC
|
1455 # endif /* O_DIRECTORY */
1457 int fd
= openat(oaa
->basefd
, oaa
->path
, opendir_flags
);
1459 dirp
= fd
>= 0 ? fdopendir(fd
) : 0;
1463 switch (gc_for_fd_with_gvl(e
)) {
1465 if (fd
< 0) fd
= openat(oaa
->basefd
, oaa
->path
, opendir_flags
);
1466 if (fd
>= 0) dirp
= fdopendir(fd
);
1467 if (dirp
) return dirp
;
1472 if (fd
>= 0) close(fd
);
1476 #else /* !USE_OPENDIR_AT */
1477 dirp
= opendir(oaa
->path
);
1478 if (!dirp
&& gc_for_fd_with_gvl(errno
))
1479 dirp
= opendir(oaa
->path
);
1480 #endif /* !USE_OPENDIR_AT */
1486 opendir_at(int basefd
, const char *path
)
1488 struct opendir_at_arg oaa
;
1490 oaa
.basefd
= basefd
;
1494 return rb_thread_call_without_gvl(nogvl_opendir_at
, &oaa
, RUBY_UBF_IO
, 0);
1496 return nogvl_opendir_at(&oaa
);
1500 do_opendir(const int basefd
, size_t baselen
, const char *path
, int flags
, rb_encoding
*enc
,
1501 ruby_glob_errfunc
*errfunc
, VALUE arg
, int *status
)
1506 if (!fundamental_encoding_p(enc
)) {
1507 tmp
= rb_enc_str_new(path
, strlen(path
), enc
);
1508 tmp
= rb_str_encode_ospath(tmp
);
1509 path
= RSTRING_PTR(tmp
);
1512 dirp
= opendir_at(basefd
, at_subpath(basefd
, baselen
, path
));
1517 if (!to_be_ignored(e
)) {
1519 *status
= (*errfunc
)(path
, arg
, enc
, e
);
1522 sys_warning(path
, enc
);
1527 if (tmp
) rb_str_resize(tmp
, 0); /* GC guard */
1533 /* Globing pattern */
1534 enum glob_pattern_type
{ PLAIN
, ALPHA
, BRACE
, MAGICAL
, RECURSIVE
, MATCH_ALL
, MATCH_DIR
};
1536 /* Return nonzero if S has any special globbing chars in it. */
1537 static enum glob_pattern_type
1538 has_magic(const char *p
, const char *pend
, int flags
, rb_encoding
*enc
)
1540 const int escape
= !(flags
& FNM_NOESCAPE
);
1546 while (p
< pend
&& (c
= *p
++) != 0) {
1558 if (escape
&& p
++ >= pend
)
1571 if (IS_WIN32
|| ISALPHA(c
)) {
1577 p
= Next(p
-1, pend
, enc
);
1580 return hasmagical
? MAGICAL
: hasalpha
? ALPHA
: PLAIN
;
1583 /* Find separator in globbing pattern. */
1585 find_dirsep(const char *p
, const char *pend
, int flags
, rb_encoding
*enc
)
1587 const int escape
= !(flags
& FNM_NOESCAPE
);
1592 while ((c
= *p
++) != 0) {
1614 if (escape
&& !(c
= *p
++))
1619 p
= Next(p
-1, pend
, enc
);
1625 /* Remove escaping backslashes */
1627 remove_backslashes(char *p
, register const char *pend
, rb_encoding
*enc
)
1635 memmove(t
, s
, p
- s
);
1646 memmove(t
, s
, p
- s
); /* move '\0' too */
1651 struct glob_pattern
{
1653 enum glob_pattern_type type
;
1654 struct glob_pattern
*next
;
1657 static void glob_free_pattern(struct glob_pattern
*list
);
1659 static struct glob_pattern
*
1660 glob_make_pattern(const char *p
, const char *e
, int flags
, rb_encoding
*enc
)
1662 struct glob_pattern
*list
, *tmp
, **tail
= &list
;
1663 int dirsep
= 0; /* pattern is terminated with '/' */
1666 while (p
< e
&& *p
) {
1667 tmp
= GLOB_ALLOC(struct glob_pattern
);
1668 if (!tmp
) goto error
;
1669 if (p
+ 2 < e
&& p
[0] == '*' && p
[1] == '*' && p
[2] == '/') {
1670 /* fold continuous RECURSIVEs (needed in glob_helper) */
1671 do { p
+= 3; while (*p
== '/') p
++; } while (p
[0] == '*' && p
[1] == '*' && p
[2] == '/');
1672 tmp
->type
= RECURSIVE
;
1678 const char *m
= find_dirsep(p
, e
, flags
, enc
);
1679 const enum glob_pattern_type magic
= has_magic(p
, m
, flags
, enc
);
1680 const enum glob_pattern_type non_magic
= (USE_NAME_ON_FS
|| FNM_SYSCASE
) ? PLAIN
: ALPHA
;
1683 if (!(FNM_SYSCASE
|| magic
> non_magic
) && !recursive
&& *m
) {
1685 while (has_magic(m
+1, m2
= find_dirsep(m
+1, e
, flags
, enc
), flags
, enc
) <= non_magic
&&
1690 buf
= GLOB_ALLOC_N(char, m
-p
+1);
1695 memcpy(buf
, p
, m
-p
);
1697 tmp
->type
= magic
> MAGICAL
? MAGICAL
: magic
> non_magic
? magic
: PLAIN
;
1712 tmp
= GLOB_ALLOC(struct glob_pattern
);
1716 tmp
->type
= dirsep
? MATCH_DIR
: MATCH_ALL
;
1725 glob_free_pattern(list
);
1730 glob_free_pattern(struct glob_pattern
*list
)
1733 struct glob_pattern
*tmp
= list
;
1736 GLOB_FREE(tmp
->str
);
1742 join_path(const char *path
, size_t len
, int dirsep
, const char *name
, size_t namlen
)
1744 char *buf
= GLOB_ALLOC_N(char, len
+namlen
+(dirsep
?1:0)+1);
1747 memcpy(buf
, path
, len
);
1751 memcpy(buf
+len
, name
, namlen
);
1752 buf
[len
+namlen
] = '\0';
1756 #ifdef HAVE_GETATTRLIST
1757 # if defined HAVE_FGETATTRLIST
1758 # define is_case_sensitive(dirp, path) is_case_sensitive(dirp)
1760 # define is_case_sensitive(dirp, path) is_case_sensitive(path)
1763 is_case_sensitive(DIR *dirp
, const char *path
)
1767 vol_capabilities_attr_t cap
[1];
1768 } __attribute__((aligned(4), packed
)) attrbuf
[1];
1769 struct attrlist al
= {ATTR_BIT_MAP_COUNT
, 0, 0, ATTR_VOL_INFO
|ATTR_VOL_CAPABILITIES
};
1770 const vol_capabilities_attr_t
*const cap
= attrbuf
[0].cap
;
1771 const int idx
= VOL_CAPABILITIES_FORMAT
;
1772 const uint32_t mask
= VOL_CAP_FMT_CASE_SENSITIVE
;
1774 # if defined HAVE_FGETATTRLIST
1775 if (fgetattrlist(dirfd(dirp
), &al
, attrbuf
, sizeof(attrbuf
), FSOPT_NOFOLLOW
))
1778 if (getattrlist(path
, &al
, attrbuf
, sizeof(attrbuf
), FSOPT_NOFOLLOW
))
1781 if (!(cap
->valid
[idx
] & mask
))
1783 return (cap
->capabilities
[idx
] & mask
) != 0;
1787 replace_real_basename(char *path
, long base
, rb_encoding
*enc
, int norm_p
, int flags
, rb_pathtype_t
*type
)
1791 attrreference_t ref
[1];
1792 fsobj_type_t objtype
;
1793 char path
[MAXPATHLEN
* 3];
1794 } __attribute__((aligned(4), packed
)) attrbuf
[1];
1795 struct attrlist al
= {ATTR_BIT_MAP_COUNT
, 0, ATTR_CMN_NAME
|ATTR_CMN_OBJTYPE
};
1796 const attrreference_t
*const ar
= attrbuf
[0].ref
;
1800 IF_NORMALIZE_UTF8PATH(VALUE utf8str
= Qnil
);
1803 if (getattrlist(path
, &al
, attrbuf
, sizeof(attrbuf
), FSOPT_NOFOLLOW
)) {
1804 if (!to_be_ignored(errno
))
1805 sys_warning(path
, enc
);
1809 switch (attrbuf
[0].objtype
) {
1810 case VREG
: *type
= path_regular
; break;
1811 case VDIR
: *type
= path_directory
; break;
1812 case VLNK
: *type
= path_symlink
; break;
1813 default: *type
= path_exist
; break;
1815 name
= (char *)ar
+ ar
->attr_dataoffset
;
1816 len
= (long)ar
->attr_length
- 1;
1817 if (name
+ len
> (char *)attrbuf
+ sizeof(attrbuf
))
1820 # if NORMALIZE_UTF8PATH
1821 if (norm_p
&& has_nonascii(name
, len
)) {
1822 if (!NIL_P(utf8str
= rb_str_normalize_ospath(name
, len
))) {
1823 RSTRING_GETMEM(utf8str
, name
, len
);
1828 tmp
= GLOB_REALLOC(path
, base
+ len
+ 1);
1831 memcpy(path
+ base
, name
, len
);
1832 path
[base
+ len
] = '\0';
1834 IF_NORMALIZE_UTF8PATH(if (!NIL_P(utf8str
)) rb_str_resize(utf8str
, 0));
1837 #elif defined _WIN32
1838 VALUE
rb_w32_conv_from_wchar(const WCHAR
*wstr
, rb_encoding
*enc
);
1839 int rb_w32_reparse_symlink_p(const WCHAR
*path
);
1842 replace_real_basename(char *path
, long base
, rb_encoding
*enc
, int norm_p
, int flags
, rb_pathtype_t
*type
)
1844 char *plainname
= path
;
1845 volatile VALUE tmp
= 0;
1846 WIN32_FIND_DATAW fd
;
1847 WIN32_FILE_ATTRIBUTE_DATA fa
;
1849 HANDLE h
= INVALID_HANDLE_VALUE
;
1852 if (!fundamental_encoding_p(enc
)) {
1853 tmp
= rb_enc_str_new_cstr(plainname
, enc
);
1854 tmp
= rb_str_encode_ospath(tmp
);
1855 plainname
= RSTRING_PTR(tmp
);
1857 wplain
= rb_w32_mbstr_to_wstr(CP_UTF8
, plainname
, -1, &wlen
);
1858 if (tmp
) rb_str_resize(tmp
, 0);
1859 if (!wplain
) return path
;
1860 if (GetFileAttributesExW(wplain
, GetFileExInfoStandard
, &fa
)) {
1861 h
= FindFirstFileW(wplain
, &fd
);
1862 e
= rb_w32_map_errno(GetLastError());
1864 if (fa
.dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
) {
1865 if (!rb_w32_reparse_symlink_p(wplain
))
1866 fa
.dwFileAttributes
&= ~FILE_ATTRIBUTE_REPARSE_POINT
;
1869 if (h
== INVALID_HANDLE_VALUE
) {
1871 if (e
&& !to_be_ignored(e
)) {
1873 sys_warning(path
, enc
);
1879 (fa
.dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
) ? path_symlink
:
1880 (fa
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) ? path_directory
:
1884 tmp
= rb_w32_conv_from_wchar(fd
.cFileName
, enc
);
1885 wlen
= RSTRING_LEN(tmp
);
1886 buf
= GLOB_REALLOC(path
, base
+ wlen
+ 1);
1889 memcpy(path
+ base
, RSTRING_PTR(tmp
), wlen
);
1890 path
[base
+ wlen
] = 0;
1892 rb_str_resize(tmp
, 0);
1896 wlen
= WideCharToMultiByte(CP_UTF8
, 0, fd
.cFileName
, -1, NULL
, 0, NULL
, NULL
);
1897 utf8filename
= GLOB_REALLOC(0, wlen
);
1900 WideCharToMultiByte(CP_UTF8
, 0, fd
.cFileName
, -1, utf8filename
, wlen
, NULL
, NULL
);
1901 buf
= GLOB_REALLOC(path
, base
+ wlen
+ 1);
1904 memcpy(path
+ base
, utf8filename
, wlen
);
1905 path
[base
+ wlen
] = 0;
1907 GLOB_FREE(utf8filename
);
1912 #elif USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME
1913 # error not implemented
1917 # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
1922 # define S_ISLNK(m) (0)
1924 # define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
1929 void (*func
)(const char *, VALUE
, void *);
1937 #define glob_call_func(func, path, arg, enc) (*(func))((path), (arg), (void *)(enc))
1940 glob_func_caller(VALUE val
)
1942 struct glob_args
*args
= (struct glob_args
*)val
;
1944 glob_call_func(args
->func
, args
->path
, args
->value
, args
->enc
);
1948 struct glob_error_args
{
1955 glob_func_warning(VALUE val
)
1957 struct glob_error_args
*arg
= (struct glob_error_args
*)val
;
1958 rb_syserr_enc_warning(arg
->error
, arg
->enc
, "%s", arg
->path
);
1964 rb_glob_warning(const char *path
, VALUE a
, const void *enc
, int error
)
1967 struct glob_error_args args
;
1972 rb_protect(glob_func_warning
, (VALUE
)&args
, &status
);
1977 NORETURN(static VALUE
glob_func_error(VALUE val
));
1980 glob_func_error(VALUE val
)
1982 struct glob_error_args
*arg
= (struct glob_error_args
*)val
;
1983 VALUE path
= rb_enc_str_new_cstr(arg
->path
, arg
->enc
);
1984 rb_syserr_fail_str(arg
->error
, path
);
1985 UNREACHABLE_RETURN(Qnil
);
1989 rb_glob_error(const char *path
, VALUE a
, const void *enc
, int error
)
1992 struct glob_error_args args
;
1993 VALUE (*errfunc
)(VALUE
) = glob_func_error
;
1995 if (error
== EACCES
) {
1996 errfunc
= glob_func_warning
;
2001 rb_protect(errfunc
, (VALUE
)&args
, &status
);
2005 typedef struct rb_dirent
{
2009 const char *d_altname
;
2015 dirent_match(const char *pat
, rb_encoding
*enc
, const char *name
, const rb_dirent_t
*dp
, int flags
)
2017 if (fnmatch(pat
, enc
, name
, flags
) == 0) return 1;
2019 if (dp
->d_altname
&& (flags
& FNM_SHORTNAME
)) {
2020 if (fnmatch(pat
, enc
, dp
->d_altname
, flags
) == 0) return 1;
2026 struct push_glob_args
{
2031 int dirsep
; /* '/' should be placed before appending child entry's name to 'path'. */
2032 rb_pathtype_t pathtype
; /* type of 'path' */
2034 const ruby_glob_funcs_t
*funcs
;
2038 struct dirent_brace_args
{
2040 const rb_dirent_t
*dp
;
2045 dirent_match_brace(const char *pattern
, VALUE val
, void *enc
)
2047 struct dirent_brace_args
*arg
= (struct dirent_brace_args
*)val
;
2049 return dirent_match(pattern
, enc
, arg
->name
, arg
->dp
, arg
->flags
);
2052 /* join paths from pattern list of glob_make_pattern() */
2054 join_path_from_pattern(struct glob_pattern
**beg
)
2056 struct glob_pattern
*p
;
2058 size_t path_len
= 0;
2060 for (p
= *beg
; p
; p
= p
->next
) {
2067 /* append last slash */
2075 path_len
= strlen(str
);
2076 path
= GLOB_ALLOC_N(char, path_len
+ 1);
2078 memcpy(path
, str
, path_len
);
2079 path
[path_len
] = '\0';
2083 size_t len
= strlen(str
);
2085 tmp
= GLOB_REALLOC(path
, path_len
+ len
+ 2);
2088 path
[path_len
++] = '/';
2089 memcpy(path
+ path_len
, str
, len
);
2091 path
[path_len
] = '\0';
2098 static int push_caller(const char *path
, VALUE val
, void *enc
);
2100 static int ruby_brace_expand(const char *str
, int flags
, ruby_glob_func
*func
, VALUE arg
,
2101 rb_encoding
*enc
, VALUE var
);
2103 static const size_t rb_dirent_name_offset
=
2104 offsetof(rb_dirent_t
, d_type
) + sizeof(uint8_t);
2106 static rb_dirent_t
*
2107 dirent_copy(const struct dirent
*dp
, rb_dirent_t
*rdp
)
2109 if (!dp
) return NULL
;
2110 size_t namlen
= NAMLEN(dp
);
2111 const size_t altlen
=
2113 dp
->d_altlen
? dp
->d_altlen
+ 1 :
2116 rb_dirent_t
*newrdp
= rdp
;
2117 if (!rdp
&& !(newrdp
= malloc(rb_dirent_name_offset
+ namlen
+ 1 + altlen
)))
2119 newrdp
->d_namlen
= namlen
;
2121 char *name
= (char *)newrdp
+ rb_dirent_name_offset
;
2122 memcpy(name
, dp
->d_name
, namlen
);
2123 name
[namlen
] = '\0';
2125 newrdp
->d_altname
= NULL
;
2127 char *const altname
= name
+ namlen
+ 1;
2128 memcpy(altname
, dp
->d_altname
, altlen
- 1);
2129 altname
[altlen
- 1] = '\0';
2130 newrdp
->d_altname
= altname
;
2133 newrdp
->d_name
= name
;
2136 newrdp
->d_name
= dp
->d_name
;
2138 newrdp
->d_altname
= dp
->d_altname
;
2142 newrdp
->d_type
= dp
->d_type
;
2156 rb_dirent_t
**entries
;
2158 } ruby_glob_entries_t
;
2161 glob_sort_cmp(const void *a
, const void *b
, void *e
)
2163 const rb_dirent_t
*ent1
= *(void **)a
;
2164 const rb_dirent_t
*ent2
= *(void **)b
;
2165 return strcmp(ent1
->d_name
, ent2
->d_name
);
2169 glob_dir_finish(ruby_glob_entries_t
*ent
, int flags
)
2171 if (flags
& FNM_GLOB_NOSORT
) {
2172 closedir(ent
->nosort
.dirp
);
2173 ent
->nosort
.dirp
= NULL
;
2175 else if (ent
->sort
.entries
) {
2176 for (size_t i
= 0, count
= ent
->sort
.count
; i
< count
;) {
2177 GLOB_FREE(ent
->sort
.entries
[i
++]);
2179 GLOB_FREE(ent
->sort
.entries
);
2180 ent
->sort
.entries
= NULL
;
2181 ent
->sort
.count
= ent
->sort
.idx
= 0;
2185 static ruby_glob_entries_t
*
2186 glob_opendir(ruby_glob_entries_t
*ent
, DIR *dirp
, int flags
, rb_encoding
*enc
)
2188 MEMZERO(ent
, ruby_glob_entries_t
, 1);
2189 if (flags
& FNM_GLOB_NOSORT
) {
2190 ent
->nosort
.dirp
= dirp
;
2196 size_t count
= 0, capacity
= 0;
2197 ent
->sort
.count
= 0;
2199 ent
->sort
.entries
= 0;
2201 if ((capacity
= dirp
->nfiles
) > 0) {
2202 if (!(newp
= GLOB_ALLOC_N(rb_dirent_t
, capacity
))) {
2206 ent
->sort
.entries
= newp
;
2209 while ((dp
= READDIR(dirp
, enc
)) != NULL
) {
2210 rb_dirent_t
*rdp
= dirent_copy(dp
, NULL
);
2214 if (count
>= capacity
) {
2216 if (!(newp
= GLOB_REALLOC_N(ent
->sort
.entries
, capacity
)))
2218 ent
->sort
.entries
= newp
;
2220 ent
->sort
.entries
[count
++] = rdp
;
2221 ent
->sort
.count
= count
;
2224 if (count
< capacity
) {
2225 if (!(newp
= GLOB_REALLOC_N(ent
->sort
.entries
, count
))) {
2226 glob_dir_finish(ent
, 0);
2229 ent
->sort
.entries
= newp
;
2231 ruby_qsort(ent
->sort
.entries
, ent
->sort
.count
, sizeof(ent
->sort
.entries
[0]),
2232 glob_sort_cmp
, NULL
);
2237 glob_dir_finish(ent
, 0);
2242 static rb_dirent_t
*
2243 glob_getent(ruby_glob_entries_t
*ent
, int flags
, rb_encoding
*enc
)
2245 if (flags
& FNM_GLOB_NOSORT
) {
2246 return dirent_copy(READDIR(ent
->nosort
.dirp
, enc
), &ent
->nosort
.ent
);
2248 else if (ent
->sort
.idx
< ent
->sort
.count
) {
2249 return ent
->sort
.entries
[ent
->sort
.idx
++];
2262 int dirsep
, /* '/' should be placed before appending child entry's name to 'path'. */
2263 rb_pathtype_t pathtype
, /* type of 'path' */
2264 struct glob_pattern
**beg
,
2265 struct glob_pattern
**end
,
2267 const ruby_glob_funcs_t
*funcs
,
2273 struct glob_pattern
**cur
, **new_beg
, **new_end
;
2274 int plain
= 0, brace
= 0, magical
= 0, recursive
= 0, match_all
= 0, match_dir
= 0;
2275 int escape
= !(flags
& FNM_NOESCAPE
);
2276 size_t pathlen
= baselen
+ namelen
;
2278 rb_check_stack_overflow();
2280 for (cur
= beg
; cur
< end
; ++cur
) {
2281 struct glob_pattern
*p
= *cur
;
2282 if (p
->type
== RECURSIVE
) {
2291 #if USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME
2312 rb_bug("continuous RECURSIVEs");
2317 struct push_glob_args args
;
2318 char* brace_path
= join_path_from_pattern(beg
);
2319 if (!brace_path
) return -1;
2322 args
.baselen
= baselen
;
2323 args
.namelen
= namelen
;
2324 args
.dirsep
= dirsep
;
2325 args
.pathtype
= pathtype
;
2329 status
= ruby_brace_expand(brace_path
, flags
, push_caller
, (VALUE
)&args
, enc
, Qfalse
);
2330 GLOB_FREE(brace_path
);
2335 if (match_all
&& pathtype
== path_unknown
) {
2336 if (do_lstat(fd
, baselen
, path
, &st
, flags
, enc
) == 0) {
2337 pathtype
= IFTODT(st
.st_mode
);
2340 pathtype
= path_noent
;
2343 if (match_dir
&& (pathtype
== path_unknown
|| pathtype
== path_symlink
)) {
2344 if (do_stat(fd
, baselen
, path
, &st
, flags
, enc
) == 0) {
2345 pathtype
= IFTODT(st
.st_mode
);
2348 pathtype
= path_noent
;
2351 if (match_all
&& pathtype
> path_noent
) {
2352 const char *subpath
= path
+ baselen
+ (baselen
&& path
[baselen
] == '/');
2353 status
= glob_call_func(funcs
->match
, subpath
, arg
, enc
);
2354 if (status
) return status
;
2356 if (match_dir
&& pathtype
== path_directory
) {
2357 int seplen
= (baselen
&& path
[baselen
] == '/');
2358 const char *subpath
= path
+ baselen
+ seplen
;
2359 char *tmp
= join_path(subpath
, namelen
- seplen
, dirsep
, "", 0);
2360 if (!tmp
) return -1;
2361 status
= glob_call_func(funcs
->match
, tmp
, arg
, enc
);
2363 if (status
) return status
;
2367 if (pathtype
== path_noent
) return 0;
2369 if (magical
|| recursive
) {
2372 # if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH
2373 char *plainname
= 0;
2375 IF_NORMALIZE_UTF8PATH(int norm_p
);
2376 # if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH
2377 if (cur
+ 1 == end
&& (*cur
)->type
<= ALPHA
) {
2378 plainname
= join_path(path
, pathlen
, dirsep
, (*cur
)->str
, strlen((*cur
)->str
));
2379 if (!plainname
) return -1;
2380 dirp
= do_opendir(fd
, basename
, plainname
, flags
, enc
, funcs
->error
, arg
, &status
);
2381 GLOB_FREE(plainname
);
2387 dirp
= do_opendir(fd
, baselen
, path
, flags
, enc
, funcs
->error
, arg
, &status
);
2389 # if FNM_SYSCASE || NORMALIZE_UTF8PATH
2390 if ((magical
< 2) && !recursive
&& (errno
== EACCES
)) {
2391 /* no read permission, fallback */
2397 IF_NORMALIZE_UTF8PATH(norm_p
= need_normalization(dirp
, *path
? path
: "."));
2399 # if NORMALIZE_UTF8PATH
2400 if (!(norm_p
|| magical
|| recursive
)) {
2405 # ifdef HAVE_GETATTRLIST
2406 if (is_case_sensitive(dirp
, path
) == 0)
2407 flags
|= FNM_CASEFOLD
;
2409 ruby_glob_entries_t globent
;
2410 if (!glob_opendir(&globent
, dirp
, flags
, enc
)) {
2413 status
= (*funcs
->error
)(path
, arg
, enc
, ENOMEM
);
2416 sys_warning(path
, enc
);
2421 int skipdot
= (flags
& FNM_GLOB_SKIPDOT
);
2422 flags
|= FNM_GLOB_SKIPDOT
;
2424 while ((dp
= glob_getent(&globent
, flags
, enc
)) != NULL
) {
2426 rb_pathtype_t new_pathtype
= path_unknown
;
2430 IF_NORMALIZE_UTF8PATH(VALUE utf8str
= Qnil
);
2433 namlen
= dp
->d_namlen
;
2434 if (name
[0] == '.') {
2437 /* unless DOTMATCH, skip current directories not to recurse infinitely */
2438 if (recursive
&& !(flags
& FNM_DOTMATCH
)) continue;
2439 if (skipdot
) continue;
2441 new_pathtype
= path_directory
; /* force to skip stat/lstat */
2443 else if (namlen
== 2 && name
[1] == '.') {
2444 /* always skip parent directories not to recurse infinitely */
2449 # if NORMALIZE_UTF8PATH
2450 if (norm_p
&& has_nonascii(name
, namlen
)) {
2451 if (!NIL_P(utf8str
= rb_str_normalize_ospath(name
, namlen
))) {
2452 RSTRING_GETMEM(utf8str
, name
, namlen
);
2456 buf
= join_path(path
, pathlen
, dirsep
, name
, namlen
);
2457 IF_NORMALIZE_UTF8PATH(if (!NIL_P(utf8str
)) rb_str_resize(utf8str
, 0));
2462 name
= buf
+ pathlen
+ (dirsep
!= 0);
2464 if (dp
->d_type
!= DT_UNKNOWN
) {
2465 /* Got it. We need no more lstat. */
2466 new_pathtype
= dp
->d_type
;
2469 if (recursive
&& dotfile
< ((flags
& FNM_DOTMATCH
) ? 2 : 1) &&
2470 new_pathtype
== path_unknown
) {
2471 /* RECURSIVE never match dot files unless FNM_DOTMATCH is set */
2472 if (do_lstat(fd
, baselen
, buf
, &st
, flags
, enc
) == 0)
2473 new_pathtype
= IFTODT(st
.st_mode
);
2475 new_pathtype
= path_noent
;
2478 new_beg
= new_end
= GLOB_ALLOC_N(struct glob_pattern
*, (end
- beg
) * 2);
2485 for (cur
= beg
; cur
< end
; ++cur
) {
2486 struct glob_pattern
*p
= *cur
;
2487 struct dirent_brace_args args
;
2488 if (p
->type
== RECURSIVE
) {
2489 if (new_pathtype
== path_directory
|| /* not symlink but real directory */
2490 new_pathtype
== path_exist
) {
2491 if (dotfile
< ((flags
& FNM_DOTMATCH
) ? 2 : 1))
2492 *new_end
++ = p
; /* append recursive pattern */
2494 p
= p
->next
; /* 0 times recursion */
2501 if (ruby_brace_expand(p
->str
, flags
, dirent_match_brace
,
2502 (VALUE
)&args
, enc
, Qfalse
) > 0)
2503 *new_end
++ = p
->next
;
2506 # if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH
2508 *new_end
++ = p
->next
;
2514 if (dirent_match(p
->str
, enc
, name
, dp
, flags
))
2515 *new_end
++ = p
->next
;
2521 status
= glob_helper(fd
, buf
, baselen
, name
- buf
- baselen
+ namlen
, 1,
2522 new_pathtype
, new_beg
, new_end
,
2523 flags
, funcs
, arg
, enc
);
2529 glob_dir_finish(&globent
, flags
);
2532 struct glob_pattern
**copy_beg
, **copy_end
, **cur2
;
2534 # if FNM_SYSCASE || NORMALIZE_UTF8PATH
2537 copy_beg
= copy_end
= GLOB_ALLOC_N(struct glob_pattern
*, end
- beg
);
2538 if (!copy_beg
) return -1;
2539 for (cur
= beg
; cur
< end
; ++cur
)
2540 *copy_end
++ = (*cur
)->type
<= ALPHA
? *cur
: 0;
2542 for (cur
= copy_beg
; cur
< copy_end
; ++cur
) {
2544 rb_pathtype_t new_pathtype
= path_unknown
;
2547 size_t len
= strlen((*cur
)->str
) + 1;
2548 name
= GLOB_ALLOC_N(char, len
);
2553 memcpy(name
, (*cur
)->str
, len
);
2555 len
= remove_backslashes(name
, name
+len
-1, enc
) - name
;
2557 new_beg
= new_end
= GLOB_ALLOC_N(struct glob_pattern
*, end
- beg
);
2563 *new_end
++ = (*cur
)->next
;
2564 for (cur2
= cur
+ 1; cur2
< copy_end
; ++cur2
) {
2565 if (*cur2
&& fnmatch((*cur2
)->str
, enc
, name
, flags
) == 0) {
2566 *new_end
++ = (*cur2
)->next
;
2571 buf
= join_path(path
, pathlen
, dirsep
, name
, len
);
2578 #if USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME
2579 if ((*cur
)->type
== ALPHA
) {
2580 buf
= replace_real_basename(buf
, pathlen
+ (dirsep
!= 0), enc
,
2581 IF_NORMALIZE_UTF8PATH(1)+0,
2582 flags
, &new_pathtype
);
2586 status
= glob_helper(fd
, buf
, baselen
,
2587 namelen
+ strlen(buf
+ pathlen
), 1,
2588 new_pathtype
, new_beg
, new_end
,
2589 flags
, funcs
, arg
, enc
);
2596 GLOB_FREE(copy_beg
);
2603 push_caller(const char *path
, VALUE val
, void *enc
)
2605 struct push_glob_args
*arg
= (struct push_glob_args
*)val
;
2606 struct glob_pattern
*list
;
2609 list
= glob_make_pattern(path
, path
+ strlen(path
), arg
->flags
, enc
);
2613 status
= glob_helper(arg
->fd
, arg
->path
, arg
->baselen
, arg
->namelen
, arg
->dirsep
,
2614 arg
->pathtype
, &list
, &list
+ 1, arg
->flags
, arg
->funcs
,
2616 glob_free_pattern(list
);
2620 static int ruby_glob0(const char *path
, int fd
, const char *base
, int flags
,
2621 const ruby_glob_funcs_t
*funcs
, VALUE arg
, rb_encoding
*enc
);
2623 struct push_glob0_args
{
2627 const ruby_glob_funcs_t
*funcs
;
2632 push_glob0_caller(const char *path
, VALUE val
, void *enc
)
2634 struct push_glob0_args
*arg
= (struct push_glob0_args
*)val
;
2635 return ruby_glob0(path
, arg
->fd
, arg
->base
, arg
->flags
, arg
->funcs
, arg
->arg
, enc
);
2639 ruby_glob0(const char *path
, int fd
, const char *base
, int flags
,
2640 const ruby_glob_funcs_t
*funcs
, VALUE arg
,
2643 struct glob_pattern
*list
;
2644 const char *root
, *start
;
2646 size_t n
, baselen
= 0;
2647 int status
, dirsep
= FALSE
;
2649 start
= root
= path
;
2652 struct push_glob0_args args
;
2658 return ruby_brace_expand(path
, flags
, push_glob0_caller
, (VALUE
)&args
, enc
, Qfalse
);
2661 flags
|= FNM_SYSCASE
;
2663 root
= rb_enc_path_skip_prefix(root
, root
+ strlen(root
), enc
);
2666 if (*root
== '/') root
++;
2675 buf
= GLOB_ALLOC_N(char, n
+ 1);
2676 if (!buf
) return -1;
2677 MEMCPY(buf
, start
, char, n
);
2680 list
= glob_make_pattern(root
, root
+ strlen(root
), flags
, enc
);
2685 status
= glob_helper(fd
, buf
, baselen
, n
-baselen
, dirsep
,
2686 path_unknown
, &list
, &list
+ 1,
2687 flags
, funcs
, arg
, enc
);
2688 glob_free_pattern(list
);
2695 ruby_glob(const char *path
, int flags
, ruby_glob_func
*func
, VALUE arg
)
2697 ruby_glob_funcs_t funcs
;
2700 return ruby_glob0(path
, AT_FDCWD
, 0, flags
& ~GLOB_VERBOSE
,
2701 &funcs
, arg
, rb_ascii8bit_encoding());
2705 rb_glob_caller(const char *path
, VALUE a
, void *enc
)
2708 struct glob_args
*args
= (struct glob_args
*)a
;
2711 rb_protect(glob_func_caller
, a
, &status
);
2715 static const ruby_glob_funcs_t rb_glob_funcs
= {
2716 rb_glob_caller
, rb_glob_error
,
2720 rb_glob(const char *path
, void (*func
)(const char *, VALUE
, void *), VALUE arg
)
2722 struct glob_args args
;
2727 args
.enc
= rb_ascii8bit_encoding();
2729 status
= ruby_glob0(path
, AT_FDCWD
, 0, GLOB_VERBOSE
, &rb_glob_funcs
,
2730 (VALUE
)&args
, args
.enc
);
2731 if (status
) GLOB_JUMP_TAG(status
);
2735 push_pattern(const char *path
, VALUE ary
, void *enc
)
2737 #if defined _WIN32 || defined __APPLE__
2738 VALUE name
= rb_utf8_str_new_cstr(path
);
2739 rb_encoding
*eenc
= rb_default_internal_encoding();
2740 name
= rb_str_conv_enc(name
, NULL
, eenc
? eenc
: enc
);
2742 VALUE name
= rb_external_str_new_with_enc(path
, strlen(path
), enc
);
2744 rb_ary_push(ary
, name
);
2748 ruby_brace_expand(const char *str
, int flags
, ruby_glob_func
*func
, VALUE arg
,
2749 rb_encoding
*enc
, VALUE var
)
2751 const int escape
= !(flags
& FNM_NOESCAPE
);
2752 const char *p
= str
;
2753 const char *pend
= p
+ strlen(p
);
2755 const char *lbrace
= 0, *rbrace
= 0;
2756 int nest
= 0, status
= 0;
2759 if (*p
== '{' && nest
++ == 0) {
2762 if (*p
== '}' && lbrace
&& --nest
== 0) {
2766 if (*p
== '\\' && escape
) {
2772 if (lbrace
&& rbrace
) {
2773 size_t len
= strlen(s
) + 1;
2774 char *buf
= GLOB_ALLOC_N(char, len
);
2777 if (!buf
) return -1;
2778 memcpy(buf
, s
, lbrace
-s
);
2781 while (p
< rbrace
) {
2782 const char *t
= ++p
;
2784 while (p
< rbrace
&& !(*p
== ',' && nest
== 0)) {
2785 if (*p
== '{') nest
++;
2786 if (*p
== '}') nest
--;
2787 if (*p
== '\\' && escape
) {
2788 if (++p
== rbrace
) break;
2792 memcpy(buf
+shift
, t
, p
-t
);
2793 strlcpy(buf
+shift
+(p
-t
), rbrace
+1, len
-(shift
+(p
-t
)));
2794 status
= ruby_brace_expand(buf
, flags
, func
, arg
, enc
, var
);
2799 else if (!lbrace
&& !rbrace
) {
2800 status
= glob_call_func(func
, s
, arg
, enc
);
2808 ruby_glob_funcs_t funcs
;
2814 glob_brace(const char *path
, VALUE val
, void *enc
)
2816 struct brace_args
*arg
= (struct brace_args
*)val
;
2818 return ruby_glob0(path
, AT_FDCWD
, 0, arg
->flags
, &arg
->funcs
, arg
->value
, enc
);
2822 ruby_brace_glob_with_enc(const char *str
, int flags
, ruby_glob_func
*func
, VALUE arg
, rb_encoding
*enc
)
2824 struct brace_args args
;
2826 flags
&= ~GLOB_VERBOSE
;
2827 args
.funcs
.match
= func
;
2828 args
.funcs
.error
= 0;
2831 return ruby_brace_expand(str
, flags
, glob_brace
, (VALUE
)&args
, enc
, Qfalse
);
2835 ruby_brace_glob(const char *str
, int flags
, ruby_glob_func
*func
, VALUE arg
)
2837 return ruby_brace_glob_with_enc(str
, flags
, func
, arg
, rb_ascii8bit_encoding());
2841 push_glob(VALUE ary
, VALUE str
, VALUE base
, int flags
)
2843 struct glob_args args
;
2845 rb_encoding
*enc
= rb_enc_get(str
);
2847 #if defined _WIN32 || defined __APPLE__
2848 str
= rb_str_encode_ospath(str
);
2850 if (rb_enc_to_index(enc
) == ENCINDEX_US_ASCII
)
2851 enc
= rb_filesystem_encoding();
2852 if (rb_enc_to_index(enc
) == ENCINDEX_US_ASCII
)
2853 enc
= rb_ascii8bit_encoding();
2854 flags
|= GLOB_VERBOSE
;
2855 args
.func
= push_pattern
;
2861 if (!RB_TYPE_P(base
, T_STRING
) || !rb_enc_check(str
, base
)) {
2862 struct dir_data
*dirp
= DATA_PTR(base
);
2863 if (!dirp
->dir
) dir_closed();
2865 if ((fd
= dirfd(dirp
->dir
)) == -1)
2866 rb_sys_fail_path(dir_inspect(base
));
2870 args
.base
= RSTRING_PTR(base
);
2872 #if defined _WIN32 || defined __APPLE__
2873 enc
= rb_utf8_encoding();
2876 return ruby_glob0(RSTRING_PTR(str
), fd
, args
.base
, flags
, &rb_glob_funcs
,
2881 rb_push_glob(VALUE str
, VALUE base
, int flags
) /* '\0' is delimiter */
2886 /* can contain null bytes as separators */
2887 if (!RB_TYPE_P(str
, T_STRING
)) {
2890 else if (!rb_str_to_cstr(str
)) {
2891 rb_raise(rb_eArgError
, "nul-separated glob pattern is deprecated");
2894 rb_enc_check(str
, rb_enc_from_encoding(rb_usascii_encoding()));
2898 status
= push_glob(ary
, str
, base
, flags
);
2899 if (status
) GLOB_JUMP_TAG(status
);
2905 dir_globs(VALUE args
, VALUE base
, int flags
)
2907 VALUE ary
= rb_ary_new();
2910 for (i
= 0; i
< RARRAY_LEN(args
); ++i
) {
2912 VALUE str
= RARRAY_AREF(args
, i
);
2914 status
= push_glob(ary
, str
, base
, flags
);
2915 if (status
) GLOB_JUMP_TAG(status
);
2923 dir_glob_option_base(VALUE base
)
2925 if (base
== Qundef
|| NIL_P(base
)) {
2929 if (rb_typeddata_is_kind_of(base
, &dir_data_type
)) {
2933 FilePathValue(base
);
2934 if (!RSTRING_LEN(base
)) return Qnil
;
2939 dir_glob_option_sort(VALUE sort
)
2941 return (rb_bool_expected(sort
, "sort") ? 0 : FNM_GLOB_NOSORT
);
2945 dir_s_aref(rb_execution_context_t
*ec
, VALUE obj
, VALUE args
, VALUE base
, VALUE sort
)
2947 const int flags
= dir_glob_option_sort(sort
);
2948 base
= dir_glob_option_base(base
);
2949 if (RARRAY_LEN(args
) == 1) {
2950 return rb_push_glob(RARRAY_AREF(args
, 0), base
, flags
);
2952 return dir_globs(args
, base
, flags
);
2956 dir_s_glob(rb_execution_context_t
*ec
, VALUE obj
, VALUE str
, VALUE rflags
, VALUE base
, VALUE sort
)
2958 VALUE ary
= rb_check_array_type(str
);
2959 const int flags
= (NUM2INT(rflags
) | dir_glob_option_sort(sort
)) & ~FNM_CASEFOLD
;
2960 base
= dir_glob_option_base(base
);
2962 ary
= rb_push_glob(str
, base
, flags
);
2965 ary
= dir_globs(ary
, base
, flags
);
2968 if (rb_block_given_p()) {
2976 dir_open_dir(int argc
, VALUE
*argv
)
2978 VALUE dir
= rb_funcallv_kw(rb_cDir
, rb_intern("open"), argc
, argv
, RB_PASS_CALLED_KEYWORDS
);
2980 rb_check_typeddata(dir
, &dir_data_type
);
2987 * Dir.foreach( dirname ) {| filename | block } -> nil
2988 * Dir.foreach( dirname, encoding: enc ) {| filename | block } -> nil
2989 * Dir.foreach( dirname ) -> an_enumerator
2990 * Dir.foreach( dirname, encoding: enc ) -> an_enumerator
2992 * Calls the block once for each entry in the named directory, passing
2993 * the filename of each entry as a parameter to the block.
2995 * If no block is given, an enumerator is returned instead.
2997 * Dir.foreach("testdir") {|x| puts "Got #{x}" }
2999 * <em>produces:</em>
3008 dir_foreach(int argc
, VALUE
*argv
, VALUE io
)
3012 RETURN_ENUMERATOR(io
, argc
, argv
);
3013 dir
= dir_open_dir(argc
, argv
);
3014 rb_ensure(dir_each
, dir
, dir_close
, dir
);
3019 dir_collect(VALUE dir
)
3021 VALUE ary
= rb_ary_new();
3022 dir_each_entry(dir
, rb_ary_push
, ary
, FALSE
);
3028 * Dir.entries( dirname ) -> array
3029 * Dir.entries( dirname, encoding: enc ) -> array
3031 * Returns an array containing all of the filenames in the given
3032 * directory. Will raise a SystemCallError if the named directory
3035 * The optional <i>encoding</i> keyword argument specifies the encoding of the
3036 * directory. If not specified, the filesystem encoding is used.
3038 * Dir.entries("testdir") #=> [".", "..", "config.h", "main.rb"]
3042 dir_entries(int argc
, VALUE
*argv
, VALUE io
)
3046 dir
= dir_open_dir(argc
, argv
);
3047 return rb_ensure(dir_collect
, dir
, dir_close
, dir
);
3051 dir_each_child(VALUE dir
)
3053 return dir_each_entry(dir
, dir_yield
, Qnil
, TRUE
);
3058 * Dir.each_child( dirname ) {| filename | block } -> nil
3059 * Dir.each_child( dirname, encoding: enc ) {| filename | block } -> nil
3060 * Dir.each_child( dirname ) -> an_enumerator
3061 * Dir.each_child( dirname, encoding: enc ) -> an_enumerator
3063 * Calls the block once for each entry except for "." and ".." in the
3064 * named directory, passing the filename of each entry as a parameter
3067 * If no block is given, an enumerator is returned instead.
3069 * Dir.each_child("testdir") {|x| puts "Got #{x}" }
3071 * <em>produces:</em>
3078 dir_s_each_child(int argc
, VALUE
*argv
, VALUE io
)
3082 RETURN_ENUMERATOR(io
, argc
, argv
);
3083 dir
= dir_open_dir(argc
, argv
);
3084 rb_ensure(dir_each_child
, dir
, dir_close
, dir
);
3090 * dir.each_child {| filename | block } -> dir
3091 * dir.each_child -> an_enumerator
3093 * Calls the block once for each entry except for "." and ".." in
3094 * this directory, passing the filename of each entry as a parameter
3097 * If no block is given, an enumerator is returned instead.
3099 * d = Dir.new("testdir")
3100 * d.each_child {|x| puts "Got #{x}" }
3102 * <em>produces:</em>
3109 dir_each_child_m(VALUE dir
)
3111 RETURN_ENUMERATOR(dir
, 0, 0);
3112 return dir_each_entry(dir
, dir_yield
, Qnil
, TRUE
);
3117 * dir.children -> array
3119 * Returns an array containing all of the filenames except for "."
3120 * and ".." in this directory.
3122 * d = Dir.new("testdir")
3123 * d.children #=> ["config.h", "main.rb"]
3127 dir_collect_children(VALUE dir
)
3129 VALUE ary
= rb_ary_new();
3130 dir_each_entry(dir
, rb_ary_push
, ary
, TRUE
);
3136 * Dir.children( dirname ) -> array
3137 * Dir.children( dirname, encoding: enc ) -> array
3139 * Returns an array containing all of the filenames except for "."
3140 * and ".." in the given directory. Will raise a SystemCallError if
3141 * the named directory doesn't exist.
3143 * The optional <i>encoding</i> keyword argument specifies the encoding of the
3144 * directory. If not specified, the filesystem encoding is used.
3146 * Dir.children("testdir") #=> ["config.h", "main.rb"]
3150 dir_s_children(int argc
, VALUE
*argv
, VALUE io
)
3154 dir
= dir_open_dir(argc
, argv
);
3155 return rb_ensure(dir_collect_children
, dir
, dir_close
, dir
);
3159 fnmatch_brace(const char *pattern
, VALUE val
, void *enc
)
3161 struct brace_args
*arg
= (struct brace_args
*)val
;
3162 VALUE path
= arg
->value
;
3163 rb_encoding
*enc_pattern
= enc
;
3164 rb_encoding
*enc_path
= rb_enc_get(path
);
3166 if (enc_pattern
!= enc_path
) {
3167 if (!rb_enc_asciicompat(enc_pattern
))
3169 if (!rb_enc_asciicompat(enc_path
))
3171 if (!rb_enc_str_asciionly_p(path
)) {
3172 int cr
= ENC_CODERANGE_7BIT
;
3173 long len
= strlen(pattern
);
3174 if (rb_str_coderange_scan_restartable(pattern
, pattern
+ len
,
3175 enc_pattern
, &cr
) != len
)
3177 if (cr
!= ENC_CODERANGE_7BIT
)
3181 return (fnmatch(pattern
, enc
, RSTRING_PTR(path
), arg
->flags
) == 0);
3186 file_s_fnmatch(int argc
, VALUE
*argv
, VALUE obj
)
3188 VALUE pattern
, path
;
3192 if (rb_scan_args(argc
, argv
, "21", &pattern
, &path
, &rflags
) == 3)
3193 flags
= NUM2INT(rflags
);
3197 StringValueCStr(pattern
);
3198 FilePathStringValue(path
);
3200 if (flags
& FNM_EXTGLOB
) {
3201 struct brace_args args
;
3205 if (ruby_brace_expand(RSTRING_PTR(pattern
), flags
, fnmatch_brace
,
3206 (VALUE
)&args
, rb_enc_get(pattern
), pattern
) > 0)
3210 rb_encoding
*enc
= rb_enc_compatible(pattern
, path
);
3211 if (!enc
) return Qfalse
;
3212 if (fnmatch(RSTRING_PTR(pattern
), enc
, RSTRING_PTR(path
), flags
) == 0)
3215 RB_GC_GUARD(pattern
);
3222 * Dir.home() -> "/home/me"
3223 * Dir.home("root") -> "/root"
3225 * Returns the home directory of the current user or the named user
3229 dir_s_home(int argc
, VALUE
*argv
, VALUE obj
)
3234 rb_check_arity(argc
, 0, 1);
3235 user
= (argc
> 0) ? argv
[0] : Qnil
;
3237 SafeStringValue(user
);
3238 rb_must_asciicompat(user
);
3239 u
= StringValueCStr(user
);
3241 return rb_home_dir_of(user
, rb_str_new(0, 0));
3244 return rb_default_home_dir(rb_str_new(0, 0));
3251 * Dir.exist?(file_name) -> true or false
3253 * Returns <code>true</code> if the named file is a directory,
3254 * <code>false</code> otherwise.
3258 rb_file_directory_p(void)
3264 nogvl_dir_empty_p(void *ptr
)
3266 const char *path
= ptr
;
3267 DIR *dir
= opendir(path
);
3269 VALUE result
= Qtrue
;
3273 switch (gc_for_fd_with_gvl(e
)) {
3275 dir
= opendir(path
);
3280 if (e
== ENOTDIR
) return (void *)Qfalse
;
3281 errno
= e
; /* for rb_sys_fail_path */
3282 return (void *)Qundef
;
3285 while ((dp
= READDIR(dir
, NULL
)) != NULL
) {
3286 if (!to_be_skipped(dp
)) {
3292 return (void *)result
;
3297 * Dir.empty?(path_name) -> true or false
3299 * Returns <code>true</code> if the named file is an empty directory,
3300 * <code>false</code> if it is not a directory or non-empty.
3303 rb_dir_s_empty_p(VALUE obj
, VALUE dirname
)
3307 enum {false_on_notdir
= 1};
3309 FilePathValue(dirname
);
3310 orig
= rb_str_dup_frozen(dirname
);
3311 dirname
= rb_str_encode_ospath(dirname
);
3312 dirname
= rb_str_dup_frozen(dirname
);
3313 path
= RSTRING_PTR(dirname
);
3315 #if defined HAVE_GETATTRLIST && defined ATTR_DIR_ENTRYCOUNT
3317 u_int32_t attrbuf
[SIZEUP32(fsobj_tag_t
)];
3318 struct attrlist al
= {ATTR_BIT_MAP_COUNT
, 0, ATTR_CMN_OBJTAG
,};
3319 if (getattrlist(path
, &al
, attrbuf
, sizeof(attrbuf
), 0) != 0)
3320 rb_sys_fail_path(orig
);
3321 if (*(const fsobj_tag_t
*)(attrbuf
+1) == VT_HFS
) {
3323 al
.dirattr
= ATTR_DIR_ENTRYCOUNT
;
3324 if (getattrlist(path
, &al
, attrbuf
, sizeof(attrbuf
), 0) == 0) {
3325 if (attrbuf
[0] >= 2 * sizeof(u_int32_t
))
3326 return RBOOL(attrbuf
[1] == 0);
3327 if (false_on_notdir
) return Qfalse
;
3329 rb_sys_fail_path(orig
);
3334 result
= (VALUE
)rb_thread_call_without_gvl(nogvl_dir_empty_p
, (void *)path
,
3336 if (result
== Qundef
) {
3337 rb_sys_fail_path(orig
);
3345 rb_cDir
= rb_define_class("Dir", rb_cObject
);
3347 rb_include_module(rb_cDir
, rb_mEnumerable
);
3349 rb_define_alloc_func(rb_cDir
, dir_s_alloc
);
3350 rb_define_singleton_method(rb_cDir
, "foreach", dir_foreach
, -1);
3351 rb_define_singleton_method(rb_cDir
, "entries", dir_entries
, -1);
3352 rb_define_singleton_method(rb_cDir
, "each_child", dir_s_each_child
, -1);
3353 rb_define_singleton_method(rb_cDir
, "children", dir_s_children
, -1);
3355 rb_define_method(rb_cDir
,"fileno", dir_fileno
, 0);
3356 rb_define_method(rb_cDir
,"path", dir_path
, 0);
3357 rb_define_method(rb_cDir
,"to_path", dir_path
, 0);
3358 rb_define_method(rb_cDir
,"inspect", dir_inspect
, 0);
3359 rb_define_method(rb_cDir
,"read", dir_read
, 0);
3360 rb_define_method(rb_cDir
,"each", dir_each
, 0);
3361 rb_define_method(rb_cDir
,"each_child", dir_each_child_m
, 0);
3362 rb_define_method(rb_cDir
,"children", dir_collect_children
, 0);
3363 rb_define_method(rb_cDir
,"rewind", dir_rewind
, 0);
3364 rb_define_method(rb_cDir
,"tell", dir_tell
, 0);
3365 rb_define_method(rb_cDir
,"seek", dir_seek
, 1);
3366 rb_define_method(rb_cDir
,"pos", dir_tell
, 0);
3367 rb_define_method(rb_cDir
,"pos=", dir_set_pos
, 1);
3368 rb_define_method(rb_cDir
,"close", dir_close
, 0);
3370 rb_define_singleton_method(rb_cDir
,"chdir", dir_s_chdir
, -1);
3371 rb_define_singleton_method(rb_cDir
,"getwd", dir_s_getwd
, 0);
3372 rb_define_singleton_method(rb_cDir
,"pwd", dir_s_getwd
, 0);
3373 rb_define_singleton_method(rb_cDir
,"chroot", dir_s_chroot
, 1);
3374 rb_define_singleton_method(rb_cDir
,"mkdir", dir_s_mkdir
, -1);
3375 rb_define_singleton_method(rb_cDir
,"rmdir", dir_s_rmdir
, 1);
3376 rb_define_singleton_method(rb_cDir
,"delete", dir_s_rmdir
, 1);
3377 rb_define_singleton_method(rb_cDir
,"unlink", dir_s_rmdir
, 1);
3378 rb_define_singleton_method(rb_cDir
,"home", dir_s_home
, -1);
3380 rb_define_singleton_method(rb_cDir
,"exist?", rb_file_directory_p
, 1);
3381 rb_define_singleton_method(rb_cDir
,"empty?", rb_dir_s_empty_p
, 1);
3383 rb_define_singleton_method(rb_cFile
,"fnmatch", file_s_fnmatch
, -1);
3384 rb_define_singleton_method(rb_cFile
,"fnmatch?", file_s_fnmatch
, -1);
3386 /* Document-const: File::Constants::FNM_NOESCAPE
3388 * Disables escapes in File.fnmatch and Dir.glob patterns
3390 rb_file_const("FNM_NOESCAPE", INT2FIX(FNM_NOESCAPE
));
3392 /* Document-const: File::Constants::FNM_PATHNAME
3394 * Wildcards in File.fnmatch and Dir.glob patterns do not match directory
3397 rb_file_const("FNM_PATHNAME", INT2FIX(FNM_PATHNAME
));
3399 /* Document-const: File::Constants::FNM_DOTMATCH
3401 * The '*' wildcard matches filenames starting with "." in File.fnmatch
3402 * and Dir.glob patterns
3404 rb_file_const("FNM_DOTMATCH", INT2FIX(FNM_DOTMATCH
));
3406 /* Document-const: File::Constants::FNM_CASEFOLD
3408 * Makes File.fnmatch patterns case insensitive (but not Dir.glob
3411 rb_file_const("FNM_CASEFOLD", INT2FIX(FNM_CASEFOLD
));
3413 /* Document-const: File::Constants::FNM_EXTGLOB
3415 * Allows file globbing through "{a,b}" in File.fnmatch patterns.
3417 rb_file_const("FNM_EXTGLOB", INT2FIX(FNM_EXTGLOB
));
3419 /* Document-const: File::Constants::FNM_SYSCASE
3421 * System default case insensitiveness, equals to FNM_CASEFOLD or
3424 rb_file_const("FNM_SYSCASE", INT2FIX(FNM_SYSCASE
));
3426 /* Document-const: File::Constants::FNM_SHORTNAME
3428 * Makes patterns to match short names if existing. Valid only
3429 * on Microsoft Windows.
3431 rb_file_const("FNM_SHORTNAME", INT2FIX(FNM_SHORTNAME
));
3434 #include "dir.rbinc"