[ruby/etc] bump up to 1.3.1
[ruby-80x24.org.git] / dir.c
blob8bd03679f18d3c32a8bfbecc69e8248da88586b3
1 /**********************************************************************
3 dir.c -
5 $Author$
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"
16 #include <ctype.h>
17 #include <errno.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
21 #ifdef HAVE_UNISTD_H
22 #include <unistd.h>
23 #endif
25 #ifndef O_CLOEXEC
26 # define O_CLOEXEC 0
27 #endif
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
33 # else
34 # define USE_OPENDIR_AT 0
35 # endif
36 #endif
38 #if USE_OPENDIR_AT
39 # include <fcntl.h>
40 #endif
42 #undef HAVE_DIRENT_NAMLEN
43 #if defined HAVE_DIRENT_H && !defined _WIN32
44 # include <dirent.h>
45 # define NAMLEN(dirent) strlen((dirent)->d_name)
46 #elif defined HAVE_DIRECT_H && !defined _WIN32
47 # include <direct.h>
48 # define NAMLEN(dirent) strlen((dirent)->d_name)
49 #else
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>
55 # endif
56 # ifdef HAVE_SYS_DIR_H
57 # include <sys/dir.h>
58 # endif
59 # ifdef HAVE_NDIR_H
60 # include <ndir.h>
61 # endif
62 # ifdef _WIN32
63 # include "win32/dir.h"
64 # endif
65 #endif
67 #ifndef HAVE_STDLIB_H
68 char *getenv();
69 #endif
71 #ifndef HAVE_STRING_H
72 char *strchr(char*,char);
73 #endif
75 #ifdef HAVE_SYS_ATTR_H
76 #include <sys/attr.h>
77 #endif
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))
88 #elif defined _WIN32
89 # define USE_NAME_ON_FS USE_NAME_ON_FS_REAL_BASENAME
90 #elif defined DOSISH
91 # define USE_NAME_ON_FS USE_NAME_ON_FS_BY_FNMATCH
92 #else
93 # define USE_NAME_ON_FS 0
94 #endif
96 #ifdef __APPLE__
97 # define NORMALIZE_UTF8PATH 1
98 # include <sys/param.h>
99 # include <sys/mount.h>
100 # include <sys/vnode.h>
101 #else
102 # define NORMALIZE_UTF8PATH 0
103 #endif
105 #include "encindex.h"
106 #include "id.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"
121 #include "builtin.h"
123 #ifndef AT_FDCWD
124 # define AT_FDCWD -1
125 #endif
127 #define vm_initialized rb_cThread
129 /* define system APIs */
130 #ifdef _WIN32
131 # undef chdir
132 # define chdir(p) rb_w32_uchdir(p)
133 # undef mkdir
134 # define mkdir(p, m) rb_w32_umkdir((p), (m))
135 # undef rmdir
136 # define rmdir(p) rb_w32_urmdir(p)
137 # undef opendir
138 # define opendir(p) rb_w32_uopendir(p)
139 # define ruby_getcwd() rb_w32_ugetcwd(NULL, 0)
140 # define IS_WIN32 1
141 #else
142 # define IS_WIN32 0
143 #endif
145 #if NORMALIZE_UTF8PATH
146 # if defined HAVE_FGETATTRLIST || !defined HAVE_GETATTRLIST
147 # define need_normalization(dirp, path) need_normalization(dirp)
148 # else
149 # define need_normalization(dirp, path) need_normalization(path)
150 # endif
151 static inline int
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);
159 # else
160 int ret = getattrlist(path, &al, attrbuf, sizeof(attrbuf), 0);
161 # endif
162 if (!ret) {
163 const fsobj_tag_t *tag = (void *)(attrbuf+1);
164 switch (*tag) {
165 case VT_HFS:
166 case VT_CIFS:
167 return TRUE;
170 # endif
171 return FALSE;
174 static inline int
175 has_nonascii(const char *ptr, size_t len)
177 while (len > 0) {
178 if (!ISASCII(*ptr)) return 1;
179 ptr++;
180 --len;
182 return 0;
185 # define IF_NORMALIZE_UTF8PATH(something) something
186 #else
187 # define IF_NORMALIZE_UTF8PATH(something) /* nothing */
188 #endif
190 #ifndef IFTODT
191 # define IFTODT(m) (((m) & S_IFMT) / ((~S_IFMT & (S_IFMT-1)) + 1))
192 #endif
194 typedef enum {
195 #ifdef DT_UNKNOWN
196 path_exist = DT_UNKNOWN,
197 path_directory = DT_DIR,
198 path_regular = DT_REG,
199 path_symlink = DT_LNK,
200 #else
201 path_exist,
202 path_directory = IFTODT(S_IFDIR),
203 path_regular = IFTODT(S_IFREG),
204 path_symlink = IFTODT(S_IFLNK),
205 #endif
206 path_noent = -1,
207 path_unknown = -2
208 } rb_pathtype_t;
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
217 #else
218 #define FNM_SYSCASE 0
219 #endif
220 #ifdef _WIN32
221 #define FNM_SHORTNAME 0x20
222 #else
223 #define FNM_SHORTNAME 0
224 #endif
225 #define FNM_GLOB_NOSORT 0x40
226 #define FNM_GLOB_SKIPDOT 0x80
228 #define FNM_NOMATCH 1
229 #define FNM_ERROR 2
231 # define Next(p, e, enc) ((p)+ rb_enc_mbclen((p), (e), (enc)))
232 # define Inc(p, e, enc) ((p) = Next((p), (e), (enc)))
234 static char *
235 bracket(
236 const char *p, /* pattern (next to '[') */
237 const char *pend,
238 const char *s, /* string */
239 const char *send,
240 int flags,
241 rb_encoding *enc)
243 const int nocase = flags & FNM_CASEFOLD;
244 const int escape = !(flags & FNM_NOESCAPE);
245 unsigned int c1, c2;
246 int r;
247 int ok = 0, not = 0;
249 if (p >= pend) return NULL;
250 if (*p == '!' || *p == '^') {
251 not = 1;
252 p++;
255 while (*p != ']') {
256 const char *t1 = p;
257 if (escape && *t1 == '\\')
258 t1++;
259 if (!*t1)
260 return NULL;
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;
265 int r2;
266 if (escape && *t2 == '\\')
267 t2++;
268 if (!*t2)
269 return NULL;
270 p = t2 + (r2 = rb_enc_mbclen(t2, pend, enc));
271 if (ok) continue;
272 if ((r <= (send-s) && memcmp(t1, s, r) == 0) ||
273 (r2 <= (send-s) && memcmp(t2, s, r2) == 0)) {
274 ok = 1;
275 continue;
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;
286 else {
287 if (ok) continue;
288 if (r <= (send-s) && memcmp(t1, s, r) == 0) {
289 ok = 1;
290 continue;
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;
297 ok = 1;
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);
312 static int
313 fnmatch_helper(
314 const char **pcur, /* pattern */
315 const char **scur, /* string */
316 int flags,
317 rb_encoding *enc)
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);
332 int r;
334 if (period && *s == '.' && *UNESCAPE(p) != '.') /* leading period */
335 RETURN(FNM_NOMATCH);
337 while (1) {
338 switch (*p) {
339 case '*':
340 do { p++; } while (*p == '*');
341 if (ISEND(UNESCAPE(p))) {
342 p = UNESCAPE(p);
343 RETURN(0);
345 if (ISEND(s))
346 RETURN(FNM_NOMATCH);
347 ptmp = p;
348 stmp = s;
349 continue;
351 case '?':
352 if (ISEND(s))
353 RETURN(FNM_NOMATCH);
354 p++;
355 Inc(s, send, enc);
356 continue;
358 case '[': {
359 const char *t;
360 if (ISEND(s))
361 RETURN(FNM_NOMATCH);
362 if ((t = bracket(p + 1, pend, s, send, flags, enc)) != 0) {
363 p = t;
364 Inc(s, send, enc);
365 continue;
367 goto failed;
371 /* ordinary */
372 p = UNESCAPE(p);
373 if (ISEND(s))
374 RETURN(ISEND(p) ? 0 : FNM_NOMATCH);
375 if (ISEND(p))
376 goto failed;
377 r = rb_enc_precise_mbclen(p, pend, enc);
378 if (!MBCLEN_CHARFOUND_P(r))
379 goto failed;
380 if (r <= (send-s) && memcmp(p, s, r) == 0) {
381 p += r;
382 s += r;
383 continue;
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))
388 goto failed;
389 p += r;
390 Inc(s, send, enc);
391 continue;
393 failed: /* try next '*' position */
394 if (ptmp && stmp) {
395 p = ptmp;
396 Inc(stmp, send, enc); /* !ISEND(*stmp) */
397 s = stmp;
398 continue;
400 RETURN(FNM_NOMATCH);
404 static int
405 fnmatch(
406 const char *pattern,
407 rb_encoding *enc,
408 const char *string,
409 int flags)
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;
420 if (pathname) {
421 while (1) {
422 if (p[0] == '*' && p[1] == '*' && p[2] == '/') {
423 do { p += 3; } while (p[0] == '*' && p[1] == '*' && p[2] == '/');
424 ptmp = p;
425 stmp = s;
427 if (fnmatch_helper(&p, &s, flags, enc) == 0) {
428 while (*s && *s != '/') Inc(s, send, enc);
429 if (*p && *s) {
430 p++;
431 s++;
432 continue;
434 if (!*p && !*s)
435 return 0;
437 /* failed : try next recursion */
438 if (ptmp && stmp && !(period && *stmp == '.')) {
439 while (*stmp && *stmp != '/') Inc(stmp, send, enc);
440 if (*stmp) {
441 p = ptmp;
442 stmp++;
443 s = stmp;
444 continue;
447 return FNM_NOMATCH;
450 else
451 return fnmatch_helper(&p, &s, flags, enc);
454 VALUE rb_cDir;
456 struct dir_data {
457 DIR *dir;
458 const VALUE path;
459 rb_encoding *enc;
462 static void
463 dir_mark(void *ptr)
465 struct dir_data *dir = ptr;
466 rb_gc_mark(dir->path);
469 static void
470 dir_free(void *ptr)
472 struct dir_data *dir = ptr;
474 if (dir->dir) closedir(dir->dir);
475 xfree(dir);
478 static size_t
479 dir_memsize(const void *ptr)
481 return sizeof(struct dir_data);
484 static const rb_data_type_t dir_data_type = {
485 "dir",
486 {dir_mark, dir_free, dir_memsize,},
487 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
490 static VALUE dir_close(VALUE);
492 static 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);
498 dirp->dir = NULL;
499 RB_OBJ_WRITE(obj, &dirp->path, Qnil);
500 dirp->enc = NULL;
502 return obj;
505 static void *
506 nogvl_opendir(void *ptr)
508 const char *path = ptr;
510 return (void *)opendir(path);
513 static DIR *
514 opendir_without_gvl(const char *path)
516 if (vm_initialized) {
517 union { const void *in; void *out; } u;
519 u.in = path;
521 return rb_thread_call_without_gvl(nogvl_opendir, u.out, RUBY_UBF_IO, 0);
523 else
524 return opendir(path);
527 static VALUE
528 dir_initialize(rb_execution_context_t *ec, VALUE dir, VALUE dirname, VALUE enc)
530 struct dir_data *dp;
531 VALUE orig;
532 const char *path;
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);
542 dp->dir = NULL;
543 RB_OBJ_WRITE(dir, &dp->path, Qnil);
544 dp->enc = fsenc;
545 path = RSTRING_PTR(dirname);
546 dp->dir = opendir_without_gvl(path);
547 if (dp->dir == NULL) {
548 int e = errno;
549 if (rb_gc_for_fd(e)) {
550 dp->dir = opendir_without_gvl(path);
552 #ifdef HAVE_GETATTRLIST
553 else if (e == EIO) {
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);
560 #endif
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);
568 return dir;
571 static VALUE
572 dir_s_open(rb_execution_context_t *ec, VALUE klass, VALUE dirname, VALUE enc)
574 struct dir_data *dp;
575 VALUE dir = TypedData_Make_Struct(klass, struct dir_data, &dir_data_type, dp);
577 dir_initialize(ec, dir, dirname, enc);
579 return dir;
582 static VALUE
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));
590 static void
591 dir_closed(void)
593 rb_raise(rb_eIOError, "closed directory");
596 static struct dir_data *
597 dir_get(VALUE dir)
599 rb_check_frozen(dir);
600 return rb_check_typeddata(dir, &dir_data_type);
603 static struct dir_data *
604 dir_check(VALUE dir)
606 struct dir_data *dirp = dir_get(dir);
607 if (!dirp->dir) dir_closed();
608 return dirp;
611 #define GetDIR(obj, dirp) ((dirp) = dir_check(obj))
615 * call-seq:
616 * dir.inspect -> string
618 * Return a string describing this Dir object.
620 static VALUE
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, ">");
632 return 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
647 # endif
648 #endif
650 #ifdef HAVE_DIRFD
652 * call-seq:
653 * dir.fileno -> integer
655 * Returns the file descriptor used in <em>dir</em>.
657 * d = Dir.new("..")
658 * d.fileno #=> 8
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.
665 static VALUE
666 dir_fileno(VALUE dir)
668 struct dir_data *dirp;
669 int fd;
671 GetDIR(dir, dirp);
672 fd = dirfd(dirp->dir);
673 if (fd == -1)
674 rb_sys_fail("dirfd");
675 return INT2NUM(fd);
677 #else
678 #define dir_fileno rb_f_notimplement
679 #endif
682 * call-seq:
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.
688 * d = Dir.new("..")
689 * d.path #=> ".."
691 static VALUE
692 dir_path(VALUE dir)
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);
701 #if defined _WIN32
702 static int
703 fundamental_encoding_p(rb_encoding *enc)
705 switch (rb_enc_to_index(enc)) {
706 case ENCINDEX_ASCII:
707 case ENCINDEX_US_ASCII:
708 case ENCINDEX_UTF_8:
709 return TRUE;
710 default:
711 return FALSE;
714 # define READDIR(dir, enc) rb_w32_readdir((dir), (enc))
715 #else
716 # define READDIR(dir, enc) readdir((dir))
717 #endif
719 /* safe to use without GVL */
720 static int
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)) {
727 case 2:
728 if (name[1] != '.') return FALSE;
729 case 1:
730 return TRUE;
731 default:
732 break;
734 #else
735 if (!name[1]) return TRUE;
736 if (name[1] != '.') return FALSE;
737 if (!name[2]) return TRUE;
738 #endif
739 return FALSE;
743 * call-seq:
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")
750 * d.read #=> "."
751 * d.read #=> ".."
752 * d.read #=> "config.h"
754 static VALUE
755 dir_read(VALUE dir)
757 struct dir_data *dirp;
758 struct dirent *dp;
760 GetDIR(dir, dirp);
761 errno = 0;
762 if ((dp = READDIR(dirp->dir, dirp->enc)) != NULL) {
763 return rb_external_str_new_with_enc(dp->d_name, NAMLEN(dp), dirp->enc);
765 else {
766 int e = errno;
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);
774 static VALUE
775 dir_yield(VALUE arg, VALUE path)
777 return rb_yield(path);
781 * call-seq:
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}" }
793 * <em>produces:</em>
795 * Got .
796 * Got ..
797 * Got config.h
798 * Got main.rb
800 static VALUE
801 dir_each(VALUE dir)
803 RETURN_ENUMERATOR(dir, 0, 0);
804 return dir_each_entry(dir, dir_yield, Qnil, FALSE);
807 static VALUE
808 dir_each_entry(VALUE dir, VALUE (*each)(VALUE, VALUE), VALUE arg, int children_only)
810 struct dir_data *dirp;
811 struct dirent *dp;
812 IF_NORMALIZE_UTF8PATH(int norm_p);
814 GetDIR(dir, dirp);
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);
820 VALUE path;
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);
831 else
832 #endif
833 path = rb_external_str_new_with_enc(name, namlen, dirp->enc);
834 (*each)(arg, path);
836 return dir;
839 #ifdef HAVE_TELLDIR
841 * call-seq:
842 * dir.pos -> integer
843 * dir.tell -> integer
845 * Returns the current position in <em>dir</em>. See also Dir#seek.
847 * d = Dir.new("testdir")
848 * d.tell #=> 0
849 * d.read #=> "."
850 * d.tell #=> 12
852 static VALUE
853 dir_tell(VALUE dir)
855 struct dir_data *dirp;
856 long pos;
858 GetDIR(dir, dirp);
859 pos = telldir(dirp->dir);
860 return rb_int2inum(pos);
862 #else
863 #define dir_tell rb_f_notimplement
864 #endif
866 #ifdef HAVE_SEEKDIR
868 * call-seq:
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>
875 * d.read #=> "."
876 * i = d.tell #=> 12
877 * d.read #=> ".."
878 * d.seek(i) #=> #<Dir:0x401b3c40>
879 * d.read #=> ".."
881 static VALUE
882 dir_seek(VALUE dir, VALUE pos)
884 struct dir_data *dirp;
885 long p = NUM2LONG(pos);
887 GetDIR(dir, dirp);
888 seekdir(dirp->dir, p);
889 return dir;
891 #else
892 #define dir_seek rb_f_notimplement
893 #endif
895 #ifdef HAVE_SEEKDIR
897 * call-seq:
898 * dir.pos = integer -> integer
900 * Synonym for Dir#seek, but returns the position parameter.
902 * d = Dir.new("testdir") #=> #<Dir:0x401b3c40>
903 * d.read #=> "."
904 * i = d.pos #=> 12
905 * d.read #=> ".."
906 * d.pos = i #=> 12
907 * d.read #=> ".."
909 static VALUE
910 dir_set_pos(VALUE dir, VALUE pos)
912 dir_seek(dir, pos);
913 return pos;
915 #else
916 #define dir_set_pos rb_f_notimplement
917 #endif
920 * call-seq:
921 * dir.rewind -> dir
923 * Repositions <em>dir</em> to the first entry.
925 * d = Dir.new("testdir")
926 * d.read #=> "."
927 * d.rewind #=> #<Dir:0x401b3fb0>
928 * d.read #=> "."
930 static VALUE
931 dir_rewind(VALUE dir)
933 struct dir_data *dirp;
935 GetDIR(dir, dirp);
936 rewinddir(dirp->dir);
937 return dir;
941 * call-seq:
942 * dir.close -> nil
944 * Closes the directory stream.
945 * Calling this method on closed Dir object is ignored since Ruby 2.3.
947 * d = Dir.new("testdir")
948 * d.close #=> nil
950 static VALUE
951 dir_close(VALUE dir)
953 struct dir_data *dirp;
955 dirp = dir_get(dir);
956 if (!dirp->dir) return Qnil;
957 closedir(dirp->dir);
958 dirp->dir = NULL;
960 return Qnil;
963 static void *
964 nogvl_chdir(void *ptr)
966 const char *path = ptr;
968 return (void *)(VALUE)chdir(path);
971 static void
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;
981 struct chdir_data {
982 VALUE old_path, new_path;
983 int done;
986 static VALUE
987 chdir_yield(VALUE v)
989 struct chdir_data *args = (void *)v;
990 dir_chdir(args->new_path);
991 args->done = TRUE;
992 chdir_blocking++;
993 if (NIL_P(chdir_thread))
994 chdir_thread = rb_thread_current();
995 return rb_yield(args->new_path);
998 static VALUE
999 chdir_restore(VALUE v)
1001 struct chdir_data *args = (void *)v;
1002 if (args->done) {
1003 chdir_blocking--;
1004 if (chdir_blocking == 0)
1005 chdir_thread = Qnil;
1006 dir_chdir(args->old_path);
1008 return Qnil;
1012 * call-seq:
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")
1033 * puts Dir.pwd
1034 * Dir.chdir("/tmp") do
1035 * puts Dir.pwd
1036 * Dir.chdir("/usr") do
1037 * puts Dir.pwd
1038 * end
1039 * puts Dir.pwd
1040 * end
1041 * puts Dir.pwd
1043 * <em>produces:</em>
1045 * /var/spool/mail
1046 * /tmp
1047 * /usr
1048 * /tmp
1049 * /var/spool/mail
1051 static VALUE
1052 dir_s_chdir(int argc, VALUE *argv, VALUE obj)
1054 VALUE path = Qnil;
1056 if (rb_check_arity(argc, 0, 1) == 1) {
1057 path = rb_str_encode_ospath(rb_get_path(argv[0]));
1059 else {
1060 const char *dist = getenv("HOME");
1061 if (!dist) {
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;
1080 args.done = FALSE;
1081 return rb_ensure(chdir_yield, (VALUE)&args, chdir_restore, (VALUE)&args);
1083 else {
1084 char *p = RSTRING_PTR(path);
1085 int r = (int)(VALUE)rb_thread_call_without_gvl(nogvl_chdir, p,
1086 RUBY_UBF_IO, 0);
1087 if (r < 0)
1088 rb_sys_fail_path(path);
1091 return INT2FIX(0);
1094 #ifndef _WIN32
1095 VALUE
1096 rb_dir_getwd_ospath(void)
1098 char *path;
1099 VALUE cwd;
1100 VALUE path_guard;
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;
1107 #ifdef __APPLE__
1108 cwd = rb_str_normalize_ospath(path, strlen(path));
1109 #else
1110 cwd = rb_str_new2(path);
1111 #endif
1112 DATA_PTR(path_guard) = 0;
1114 xfree(path);
1115 return cwd;
1117 #endif
1119 VALUE
1120 rb_dir_getwd(void)
1122 rb_encoding *fs = rb_filesystem_encoding();
1123 int fsenc = rb_enc_to_index(fs);
1124 VALUE cwd = rb_dir_getwd_ospath();
1126 switch (fsenc) {
1127 case ENCINDEX_US_ASCII:
1128 fsenc = ENCINDEX_ASCII;
1129 case ENCINDEX_ASCII:
1130 break;
1131 #if defined _WIN32 || defined __APPLE__
1132 default:
1133 return rb_str_conv_enc(cwd, NULL, fs);
1134 #endif
1136 return rb_enc_associate_index(cwd, fsenc);
1140 * call-seq:
1141 * Dir.getwd -> string
1142 * Dir.pwd -> string
1144 * Returns the path to the current working directory of this process as
1145 * a string.
1147 * Dir.chdir("/tmp") #=> 0
1148 * Dir.getwd #=> "/tmp"
1149 * Dir.pwd #=> "/tmp"
1151 static VALUE
1152 dir_s_getwd(VALUE dir)
1154 return rb_dir_getwd();
1157 static VALUE
1158 check_dirname(VALUE dir)
1160 VALUE d = dir;
1161 char *path, *pend;
1162 long len;
1163 rb_encoding *enc;
1165 FilePathValue(d);
1166 enc = rb_enc_get(d);
1167 RSTRING_GETMEM(d, path, len);
1168 pend = 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);
1172 StringValueCStr(d);
1174 return rb_str_encode_ospath(d);
1177 #if defined(HAVE_CHROOT)
1179 * call-seq:
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
1185 * information.
1187 static VALUE
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);
1194 return INT2FIX(0);
1196 #else
1197 #define dir_s_chroot rb_f_notimplement
1198 #endif
1200 struct mkdir_arg {
1201 const char *path;
1202 mode_t mode;
1205 static void *
1206 nogvl_mkdir(void *ptr)
1208 struct mkdir_arg *m = ptr;
1210 return (void *)(VALUE)mkdir(m->path, m->mode);
1214 * call-seq:
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
1227 static VALUE
1228 dir_s_mkdir(int argc, VALUE *argv, VALUE obj)
1230 struct mkdir_arg m;
1231 VALUE path, vmode;
1232 int r;
1234 if (rb_scan_args(argc, argv, "11", &path, &vmode) == 2) {
1235 m.mode = NUM2MODET(vmode);
1237 else {
1238 m.mode = 0777;
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);
1244 if (r < 0)
1245 rb_sys_fail_path(path);
1247 return INT2FIX(0);
1250 static void *
1251 nogvl_rmdir(void *ptr)
1253 const char *path = ptr;
1255 return (void *)(VALUE)rmdir(path);
1259 * call-seq:
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.
1267 static VALUE
1268 dir_s_rmdir(VALUE obj, VALUE dir)
1270 const char *p;
1271 int r;
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);
1276 if (r < 0)
1277 rb_sys_fail_path(dir);
1279 return INT2FIX(0);
1282 struct warning_args {
1283 #ifdef RUBY_FUNCTION_NAME_STRING
1284 const char *func;
1285 #endif
1286 const char *mesg;
1287 rb_encoding *enc;
1290 #ifndef RUBY_FUNCTION_NAME_STRING
1291 #define sys_enc_warning_in(func, mesg, enc) sys_enc_warning(mesg, enc)
1292 #endif
1294 static VALUE
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);
1300 #else
1301 rb_sys_enc_warning(arg->enc, "%s", arg->mesg);
1302 #endif
1303 return Qnil;
1306 static void
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
1311 arg.func = func;
1312 #endif
1313 arg.mesg = mesg;
1314 arg.enc = enc;
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)
1325 size_t z;
1326 if (rb_mul_size_overflow(x, y, SSIZE_MAX, &z)) {
1327 rb_memerror(); /* or...? */
1329 else {
1330 return z;
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));
1358 static inline int
1359 to_be_ignored(int e)
1361 return e == ENOENT || e == ENOTDIR;
1364 #ifdef _WIN32
1365 #define STAT(p, s) rb_w32_ustati128((p), (s))
1366 #undef lstat
1367 #define lstat(p, s) rb_w32_ulstati128((p), (s))
1368 #else
1369 #define STAT(p, s) stat((p), (s))
1370 #endif
1372 typedef int ruby_glob_errfunc(const char*, VALUE, const void*, int);
1373 typedef struct {
1374 ruby_glob_func *match;
1375 ruby_glob_errfunc *error;
1376 } ruby_glob_funcs_t;
1378 static const char *
1379 at_subpath(int fd, size_t baselen, const char *path)
1381 #if USE_OPENDIR_AT
1382 if (fd != (int)AT_FDCWD && baselen > 0) {
1383 path += baselen;
1384 if (*path == '/') ++path;
1386 #endif
1387 return *path ? path : ".";
1390 /* System call with warning */
1391 static int
1392 do_stat(int fd, size_t baselen, const char *path, struct stat *pst, int flags, rb_encoding *enc)
1394 #if USE_OPENDIR_AT
1395 int ret = fstatat(fd, at_subpath(fd, baselen, path), pst, 0);
1396 #else
1397 int ret = STAT(path, pst);
1398 #endif
1399 if (ret < 0 && !to_be_ignored(errno))
1400 sys_warning(path, enc);
1402 return ret;
1405 #if defined HAVE_LSTAT || defined lstat || USE_OPENDIR_AT
1406 static int
1407 do_lstat(int fd, size_t baselen, const char *path, struct stat *pst, int flags, rb_encoding *enc)
1409 #if USE_OPENDIR_AT
1410 int ret = fstatat(fd, at_subpath(fd, baselen, path), pst, AT_SYMLINK_NOFOLLOW);
1411 #else
1412 int ret = lstat(path, pst);
1413 #endif
1414 if (ret < 0 && !to_be_ignored(errno))
1415 sys_warning(path, enc);
1417 return ret;
1419 #else
1420 #define do_lstat do_stat
1421 #endif
1423 struct opendir_at_arg {
1424 int basefd;
1425 const char *path;
1428 static void *
1429 with_gvl_gc_for_fd(void *ptr)
1431 int *e = ptr;
1433 return (void *)RBOOL(rb_gc_for_fd(*e));
1436 static int
1437 gc_for_fd_with_gvl(int e)
1439 if (vm_initialized)
1440 return (int)(VALUE)rb_thread_call_with_gvl(with_gvl_gc_for_fd, &e);
1441 else
1442 return RBOOL(rb_gc_for_fd(e));
1445 static void *
1446 nogvl_opendir_at(void *ptr)
1448 const struct opendir_at_arg *oaa = ptr;
1449 DIR *dirp;
1451 #if USE_OPENDIR_AT
1452 const int opendir_flags = (O_RDONLY|O_CLOEXEC|
1453 # ifdef O_DIRECTORY
1454 O_DIRECTORY|
1455 # endif /* O_DIRECTORY */
1457 int fd = openat(oaa->basefd, oaa->path, opendir_flags);
1459 dirp = fd >= 0 ? fdopendir(fd) : 0;
1460 if (!dirp) {
1461 int e = errno;
1463 switch (gc_for_fd_with_gvl(e)) {
1464 default:
1465 if (fd < 0) fd = openat(oaa->basefd, oaa->path, opendir_flags);
1466 if (fd >= 0) dirp = fdopendir(fd);
1467 if (dirp) return dirp;
1469 e = errno;
1470 /* fallthrough*/
1471 case 0:
1472 if (fd >= 0) close(fd);
1473 errno = e;
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 */
1482 return dirp;
1485 static DIR *
1486 opendir_at(int basefd, const char *path)
1488 struct opendir_at_arg oaa;
1490 oaa.basefd = basefd;
1491 oaa.path = path;
1493 if (vm_initialized)
1494 return rb_thread_call_without_gvl(nogvl_opendir_at, &oaa, RUBY_UBF_IO, 0);
1495 else
1496 return nogvl_opendir_at(&oaa);
1499 static DIR *
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)
1503 DIR *dirp;
1504 #ifdef _WIN32
1505 VALUE tmp = 0;
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);
1511 #endif
1512 dirp = opendir_at(basefd, at_subpath(basefd, baselen, path));
1513 if (!dirp) {
1514 int e = errno;
1516 *status = 0;
1517 if (!to_be_ignored(e)) {
1518 if (errfunc) {
1519 *status = (*errfunc)(path, arg, enc, e);
1521 else {
1522 sys_warning(path, enc);
1526 #ifdef _WIN32
1527 if (tmp) rb_str_resize(tmp, 0); /* GC guard */
1528 #endif
1530 return dirp;
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);
1541 int hasalpha = 0;
1542 int hasmagical = 0;
1544 register char c;
1546 while (p < pend && (c = *p++) != 0) {
1547 switch (c) {
1548 case '{':
1549 return BRACE;
1551 case '*':
1552 case '?':
1553 case '[':
1554 hasmagical = 1;
1555 break;
1557 case '\\':
1558 if (escape && p++ >= pend)
1559 continue;
1560 break;
1562 #ifdef _WIN32
1563 case '.':
1564 break;
1566 case '~':
1567 hasalpha = 1;
1568 break;
1569 #endif
1570 default:
1571 if (IS_WIN32 || ISALPHA(c)) {
1572 hasalpha = 1;
1574 break;
1577 p = Next(p-1, pend, enc);
1580 return hasmagical ? MAGICAL : hasalpha ? ALPHA : PLAIN;
1583 /* Find separator in globbing pattern. */
1584 static char *
1585 find_dirsep(const char *p, const char *pend, int flags, rb_encoding *enc)
1587 const int escape = !(flags & FNM_NOESCAPE);
1589 register char c;
1590 int open = 0;
1592 while ((c = *p++) != 0) {
1593 switch (c) {
1594 case '[':
1595 open = 1;
1596 continue;
1597 case ']':
1598 open = 0;
1599 continue;
1601 case '{':
1602 open = 1;
1603 continue;
1604 case '}':
1605 open = 0;
1606 continue;
1608 case '/':
1609 if (!open)
1610 return (char *)p-1;
1611 continue;
1613 case '\\':
1614 if (escape && !(c = *p++))
1615 return (char *)p-1;
1616 continue;
1619 p = Next(p-1, pend, enc);
1622 return (char *)p-1;
1625 /* Remove escaping backslashes */
1626 static char *
1627 remove_backslashes(char *p, register const char *pend, rb_encoding *enc)
1629 char *t = p;
1630 char *s = p;
1632 while (*p) {
1633 if (*p == '\\') {
1634 if (t != s)
1635 memmove(t, s, p - s);
1636 t += p - s;
1637 s = ++p;
1638 if (!*p) break;
1640 Inc(p, pend, enc);
1643 while (*p++);
1645 if (t != s)
1646 memmove(t, s, p - s); /* move '\0' too */
1648 return p;
1651 struct glob_pattern {
1652 char *str;
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 '/' */
1664 int recursive = 0;
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;
1673 tmp->str = 0;
1674 dirsep = 1;
1675 recursive = 1;
1677 else {
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;
1681 char *buf;
1683 if (!(FNM_SYSCASE || magic > non_magic) && !recursive && *m) {
1684 const char *m2;
1685 while (has_magic(m+1, m2 = find_dirsep(m+1, e, flags, enc), flags, enc) <= non_magic &&
1686 *m2) {
1687 m = m2;
1690 buf = GLOB_ALLOC_N(char, m-p+1);
1691 if (!buf) {
1692 GLOB_FREE(tmp);
1693 goto error;
1695 memcpy(buf, p, m-p);
1696 buf[m-p] = '\0';
1697 tmp->type = magic > MAGICAL ? MAGICAL : magic > non_magic ? magic : PLAIN;
1698 tmp->str = buf;
1699 if (*m) {
1700 dirsep = 1;
1701 p = m + 1;
1703 else {
1704 dirsep = 0;
1705 p = m;
1708 *tail = tmp;
1709 tail = &tmp->next;
1712 tmp = GLOB_ALLOC(struct glob_pattern);
1713 if (!tmp) {
1714 goto error;
1716 tmp->type = dirsep ? MATCH_DIR : MATCH_ALL;
1717 tmp->str = 0;
1718 *tail = tmp;
1719 tmp->next = 0;
1721 return list;
1723 error:
1724 *tail = 0;
1725 glob_free_pattern(list);
1726 return 0;
1729 static void
1730 glob_free_pattern(struct glob_pattern *list)
1732 while (list) {
1733 struct glob_pattern *tmp = list;
1734 list = list->next;
1735 if (tmp->str)
1736 GLOB_FREE(tmp->str);
1737 GLOB_FREE(tmp);
1741 static char *
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);
1746 if (!buf) return 0;
1747 memcpy(buf, path, len);
1748 if (dirsep) {
1749 buf[len++] = '/';
1751 memcpy(buf+len, name, namlen);
1752 buf[len+namlen] = '\0';
1753 return buf;
1756 #ifdef HAVE_GETATTRLIST
1757 # if defined HAVE_FGETATTRLIST
1758 # define is_case_sensitive(dirp, path) is_case_sensitive(dirp)
1759 # else
1760 # define is_case_sensitive(dirp, path) is_case_sensitive(path)
1761 # endif
1762 static int
1763 is_case_sensitive(DIR *dirp, const char *path)
1765 struct {
1766 u_int32_t length;
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))
1776 return -1;
1777 # else
1778 if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW))
1779 return -1;
1780 # endif
1781 if (!(cap->valid[idx] & mask))
1782 return -1;
1783 return (cap->capabilities[idx] & mask) != 0;
1786 static char *
1787 replace_real_basename(char *path, long base, rb_encoding *enc, int norm_p, int flags, rb_pathtype_t *type)
1789 struct {
1790 u_int32_t length;
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;
1797 const char *name;
1798 long len;
1799 char *tmp;
1800 IF_NORMALIZE_UTF8PATH(VALUE utf8str = Qnil);
1802 *type = path_noent;
1803 if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW)) {
1804 if (!to_be_ignored(errno))
1805 sys_warning(path, enc);
1806 return path;
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))
1818 return path;
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);
1826 # endif
1828 tmp = GLOB_REALLOC(path, base + len + 1);
1829 if (tmp) {
1830 path = tmp;
1831 memcpy(path + base, name, len);
1832 path[base + len] = '\0';
1834 IF_NORMALIZE_UTF8PATH(if (!NIL_P(utf8str)) rb_str_resize(utf8str, 0));
1835 return path;
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);
1841 static char *
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;
1848 WCHAR *wplain;
1849 HANDLE h = INVALID_HANDLE_VALUE;
1850 long wlen;
1851 int e = 0;
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;
1868 free(wplain);
1869 if (h == INVALID_HANDLE_VALUE) {
1870 *type = path_noent;
1871 if (e && !to_be_ignored(e)) {
1872 errno = e;
1873 sys_warning(path, enc);
1875 return path;
1877 FindClose(h);
1878 *type =
1879 (fa.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ? path_symlink :
1880 (fa.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? path_directory :
1881 path_regular;
1882 if (tmp) {
1883 char *buf;
1884 tmp = rb_w32_conv_from_wchar(fd.cFileName, enc);
1885 wlen = RSTRING_LEN(tmp);
1886 buf = GLOB_REALLOC(path, base + wlen + 1);
1887 if (buf) {
1888 path = buf;
1889 memcpy(path + base, RSTRING_PTR(tmp), wlen);
1890 path[base + wlen] = 0;
1892 rb_str_resize(tmp, 0);
1894 else {
1895 char *utf8filename;
1896 wlen = WideCharToMultiByte(CP_UTF8, 0, fd.cFileName, -1, NULL, 0, NULL, NULL);
1897 utf8filename = GLOB_REALLOC(0, wlen);
1898 if (utf8filename) {
1899 char *buf;
1900 WideCharToMultiByte(CP_UTF8, 0, fd.cFileName, -1, utf8filename, wlen, NULL, NULL);
1901 buf = GLOB_REALLOC(path, base + wlen + 1);
1902 if (buf) {
1903 path = buf;
1904 memcpy(path + base, utf8filename, wlen);
1905 path[base + wlen] = 0;
1907 GLOB_FREE(utf8filename);
1910 return path;
1912 #elif USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME
1913 # error not implemented
1914 #endif
1916 #ifndef S_ISDIR
1917 # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
1918 #endif
1920 #ifndef S_ISLNK
1921 # ifndef S_IFLNK
1922 # define S_ISLNK(m) (0)
1923 # else
1924 # define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
1925 # endif
1926 #endif
1928 struct glob_args {
1929 void (*func)(const char *, VALUE, void *);
1930 const char *path;
1931 const char *base;
1932 size_t baselen;
1933 VALUE value;
1934 rb_encoding *enc;
1937 #define glob_call_func(func, path, arg, enc) (*(func))((path), (arg), (void *)(enc))
1939 static VALUE
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);
1945 return Qnil;
1948 struct glob_error_args {
1949 const char *path;
1950 rb_encoding *enc;
1951 int error;
1954 static VALUE
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);
1959 return Qnil;
1962 #if 0
1963 static int
1964 rb_glob_warning(const char *path, VALUE a, const void *enc, int error)
1966 int status;
1967 struct glob_error_args args;
1969 args.path = path;
1970 args.enc = enc;
1971 args.error = error;
1972 rb_protect(glob_func_warning, (VALUE)&args, &status);
1973 return status;
1975 #endif
1977 NORETURN(static VALUE glob_func_error(VALUE val));
1979 static VALUE
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);
1988 static int
1989 rb_glob_error(const char *path, VALUE a, const void *enc, int error)
1991 int status;
1992 struct glob_error_args args;
1993 VALUE (*errfunc)(VALUE) = glob_func_error;
1995 if (error == EACCES) {
1996 errfunc = glob_func_warning;
1998 args.path = path;
1999 args.enc = enc;
2000 args.error = error;
2001 rb_protect(errfunc, (VALUE)&args, &status);
2002 return status;
2005 typedef struct rb_dirent {
2006 long d_namlen;
2007 const char *d_name;
2008 #ifdef _WIN32
2009 const char *d_altname;
2010 #endif
2011 uint8_t d_type;
2012 } rb_dirent_t;
2014 static inline int
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;
2018 #ifdef _WIN32
2019 if (dp->d_altname && (flags & FNM_SHORTNAME)) {
2020 if (fnmatch(pat, enc, dp->d_altname, flags) == 0) return 1;
2022 #endif
2023 return 0;
2026 struct push_glob_args {
2027 int fd;
2028 const char *path;
2029 size_t baselen;
2030 size_t namelen;
2031 int dirsep; /* '/' should be placed before appending child entry's name to 'path'. */
2032 rb_pathtype_t pathtype; /* type of 'path' */
2033 int flags;
2034 const ruby_glob_funcs_t *funcs;
2035 VALUE arg;
2038 struct dirent_brace_args {
2039 const char *name;
2040 const rb_dirent_t *dp;
2041 int flags;
2044 static int
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() */
2053 static char*
2054 join_path_from_pattern(struct glob_pattern **beg)
2056 struct glob_pattern *p;
2057 char *path = NULL;
2058 size_t path_len = 0;
2060 for (p = *beg; p; p = p->next) {
2061 const char *str;
2062 switch (p->type) {
2063 case RECURSIVE:
2064 str = "**";
2065 break;
2066 case MATCH_DIR:
2067 /* append last slash */
2068 str = "";
2069 break;
2070 default:
2071 str = p->str;
2072 if (!str) continue;
2074 if (!path) {
2075 path_len = strlen(str);
2076 path = GLOB_ALLOC_N(char, path_len + 1);
2077 if (path) {
2078 memcpy(path, str, path_len);
2079 path[path_len] = '\0';
2082 else {
2083 size_t len = strlen(str);
2084 char *tmp;
2085 tmp = GLOB_REALLOC(path, path_len + len + 2);
2086 if (tmp) {
2087 path = tmp;
2088 path[path_len++] = '/';
2089 memcpy(path + path_len, str, len);
2090 path_len += len;
2091 path[path_len] = '\0';
2095 return path;
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 =
2112 #ifdef _WIN32
2113 dp->d_altlen ? dp->d_altlen + 1 :
2114 #endif
2116 rb_dirent_t *newrdp = rdp;
2117 if (!rdp && !(newrdp = malloc(rb_dirent_name_offset + namlen + 1 + altlen)))
2118 return NULL;
2119 newrdp->d_namlen = namlen;
2120 if (!rdp) {
2121 char *name = (char *)newrdp + rb_dirent_name_offset;
2122 memcpy(name, dp->d_name, namlen);
2123 name[namlen] = '\0';
2124 #ifdef _WIN32
2125 newrdp->d_altname = NULL;
2126 if (altlen) {
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;
2132 #endif
2133 newrdp->d_name = name;
2135 else {
2136 newrdp->d_name = dp->d_name;
2137 #ifdef _WIN32
2138 newrdp->d_altname = dp->d_altname;
2139 #endif
2141 #ifdef DT_UNKNOWN
2142 newrdp->d_type = dp->d_type;
2143 #else
2144 newrdp->d_type = 0;
2145 #endif
2146 return newrdp;
2149 typedef union {
2150 struct {
2151 DIR *dirp;
2152 rb_dirent_t ent;
2153 } nosort;
2154 struct {
2155 size_t count, idx;
2156 rb_dirent_t **entries;
2157 } sort;
2158 } ruby_glob_entries_t;
2160 static int
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);
2168 static void
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;
2191 return ent;
2193 else {
2194 void *newp;
2195 struct dirent *dp;
2196 size_t count = 0, capacity = 0;
2197 ent->sort.count = 0;
2198 ent->sort.idx = 0;
2199 ent->sort.entries = 0;
2200 #ifdef _WIN32
2201 if ((capacity = dirp->nfiles) > 0) {
2202 if (!(newp = GLOB_ALLOC_N(rb_dirent_t, capacity))) {
2203 closedir(dirp);
2204 return NULL;
2206 ent->sort.entries = newp;
2208 #endif
2209 while ((dp = READDIR(dirp, enc)) != NULL) {
2210 rb_dirent_t *rdp = dirent_copy(dp, NULL);
2211 if (!rdp) {
2212 goto nomem;
2214 if (count >= capacity) {
2215 capacity += 256;
2216 if (!(newp = GLOB_REALLOC_N(ent->sort.entries, capacity)))
2217 goto nomem;
2218 ent->sort.entries = newp;
2220 ent->sort.entries[count++] = rdp;
2221 ent->sort.count = count;
2223 closedir(dirp);
2224 if (count < capacity) {
2225 if (!(newp = GLOB_REALLOC_N(ent->sort.entries, count))) {
2226 glob_dir_finish(ent, 0);
2227 return NULL;
2229 ent->sort.entries = newp;
2231 ruby_qsort(ent->sort.entries, ent->sort.count, sizeof(ent->sort.entries[0]),
2232 glob_sort_cmp, NULL);
2233 return ent;
2236 nomem:
2237 glob_dir_finish(ent, 0);
2238 closedir(dirp);
2239 return NULL;
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++];
2251 else {
2252 return NULL;
2256 static int
2257 glob_helper(
2258 int fd,
2259 const char *path,
2260 size_t baselen,
2261 size_t namelen,
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,
2266 int flags,
2267 const ruby_glob_funcs_t *funcs,
2268 VALUE arg,
2269 rb_encoding *enc)
2271 struct stat st;
2272 int status = 0;
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) {
2283 recursive = 1;
2284 p = p->next;
2286 switch (p->type) {
2287 case PLAIN:
2288 plain = 1;
2289 break;
2290 case ALPHA:
2291 #if USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME
2292 plain = 1;
2293 #else
2294 magical = 1;
2295 #endif
2296 break;
2297 case BRACE:
2298 if (!recursive) {
2299 brace = 1;
2301 break;
2302 case MAGICAL:
2303 magical = 2;
2304 break;
2305 case MATCH_ALL:
2306 match_all = 1;
2307 break;
2308 case MATCH_DIR:
2309 match_dir = 1;
2310 break;
2311 case RECURSIVE:
2312 rb_bug("continuous RECURSIVEs");
2316 if (brace) {
2317 struct push_glob_args args;
2318 char* brace_path = join_path_from_pattern(beg);
2319 if (!brace_path) return -1;
2320 args.fd = fd;
2321 args.path = path;
2322 args.baselen = baselen;
2323 args.namelen = namelen;
2324 args.dirsep = dirsep;
2325 args.pathtype = pathtype;
2326 args.flags = flags;
2327 args.funcs = funcs;
2328 args.arg = arg;
2329 status = ruby_brace_expand(brace_path, flags, push_caller, (VALUE)&args, enc, Qfalse);
2330 GLOB_FREE(brace_path);
2331 return status;
2334 if (*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);
2339 else {
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);
2347 else {
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);
2362 GLOB_FREE(tmp);
2363 if (status) return status;
2367 if (pathtype == path_noent) return 0;
2369 if (magical || recursive) {
2370 rb_dirent_t *dp;
2371 DIR *dirp;
2372 # if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH
2373 char *plainname = 0;
2374 # endif
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);
2383 else
2384 # else
2386 # endif
2387 dirp = do_opendir(fd, baselen, path, flags, enc, funcs->error, arg, &status);
2388 if (dirp == NULL) {
2389 # if FNM_SYSCASE || NORMALIZE_UTF8PATH
2390 if ((magical < 2) && !recursive && (errno == EACCES)) {
2391 /* no read permission, fallback */
2392 goto literally;
2394 # endif
2395 return status;
2397 IF_NORMALIZE_UTF8PATH(norm_p = need_normalization(dirp, *path ? path : "."));
2399 # if NORMALIZE_UTF8PATH
2400 if (!(norm_p || magical || recursive)) {
2401 closedir(dirp);
2402 goto literally;
2404 # endif
2405 # ifdef HAVE_GETATTRLIST
2406 if (is_case_sensitive(dirp, path) == 0)
2407 flags |= FNM_CASEFOLD;
2408 # endif
2409 ruby_glob_entries_t globent;
2410 if (!glob_opendir(&globent, dirp, flags, enc)) {
2411 status = 0;
2412 if (funcs->error) {
2413 status = (*funcs->error)(path, arg, enc, ENOMEM);
2415 else {
2416 sys_warning(path, enc);
2418 return status;
2421 int skipdot = (flags & FNM_GLOB_SKIPDOT);
2422 flags |= FNM_GLOB_SKIPDOT;
2424 while ((dp = glob_getent(&globent, flags, enc)) != NULL) {
2425 char *buf;
2426 rb_pathtype_t new_pathtype = path_unknown;
2427 const char *name;
2428 size_t namlen;
2429 int dotfile = 0;
2430 IF_NORMALIZE_UTF8PATH(VALUE utf8str = Qnil);
2432 name = dp->d_name;
2433 namlen = dp->d_namlen;
2434 if (name[0] == '.') {
2435 ++dotfile;
2436 if (namlen == 1) {
2437 /* unless DOTMATCH, skip current directories not to recurse infinitely */
2438 if (recursive && !(flags & FNM_DOTMATCH)) continue;
2439 if (skipdot) continue;
2440 ++dotfile;
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 */
2445 continue;
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);
2455 # endif
2456 buf = join_path(path, pathlen, dirsep, name, namlen);
2457 IF_NORMALIZE_UTF8PATH(if (!NIL_P(utf8str)) rb_str_resize(utf8str, 0));
2458 if (!buf) {
2459 status = -1;
2460 break;
2462 name = buf + pathlen + (dirsep != 0);
2463 #ifdef DT_UNKNOWN
2464 if (dp->d_type != DT_UNKNOWN) {
2465 /* Got it. We need no more lstat. */
2466 new_pathtype = dp->d_type;
2468 #endif
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);
2474 else
2475 new_pathtype = path_noent;
2478 new_beg = new_end = GLOB_ALLOC_N(struct glob_pattern *, (end - beg) * 2);
2479 if (!new_beg) {
2480 GLOB_FREE(buf);
2481 status = -1;
2482 break;
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 */
2496 switch (p->type) {
2497 case BRACE:
2498 args.name = name;
2499 args.dp = dp;
2500 args.flags = flags;
2501 if (ruby_brace_expand(p->str, flags, dirent_match_brace,
2502 (VALUE)&args, enc, Qfalse) > 0)
2503 *new_end++ = p->next;
2504 break;
2505 case ALPHA:
2506 # if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH
2507 if (plainname) {
2508 *new_end++ = p->next;
2509 break;
2511 # endif
2512 case PLAIN:
2513 case MAGICAL:
2514 if (dirent_match(p->str, enc, name, dp, flags))
2515 *new_end++ = p->next;
2516 default:
2517 break;
2521 status = glob_helper(fd, buf, baselen, name - buf - baselen + namlen, 1,
2522 new_pathtype, new_beg, new_end,
2523 flags, funcs, arg, enc);
2524 GLOB_FREE(buf);
2525 GLOB_FREE(new_beg);
2526 if (status) break;
2529 glob_dir_finish(&globent, flags);
2531 else if (plain) {
2532 struct glob_pattern **copy_beg, **copy_end, **cur2;
2534 # if FNM_SYSCASE || NORMALIZE_UTF8PATH
2535 literally:
2536 # endif
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) {
2543 if (*cur) {
2544 rb_pathtype_t new_pathtype = path_unknown;
2545 char *buf;
2546 char *name;
2547 size_t len = strlen((*cur)->str) + 1;
2548 name = GLOB_ALLOC_N(char, len);
2549 if (!name) {
2550 status = -1;
2551 break;
2553 memcpy(name, (*cur)->str, len);
2554 if (escape)
2555 len = remove_backslashes(name, name+len-1, enc) - name;
2557 new_beg = new_end = GLOB_ALLOC_N(struct glob_pattern *, end - beg);
2558 if (!new_beg) {
2559 GLOB_FREE(name);
2560 status = -1;
2561 break;
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;
2567 *cur2 = 0;
2571 buf = join_path(path, pathlen, dirsep, name, len);
2572 GLOB_FREE(name);
2573 if (!buf) {
2574 GLOB_FREE(new_beg);
2575 status = -1;
2576 break;
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);
2583 if (!buf) break;
2585 #endif
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);
2590 GLOB_FREE(buf);
2591 GLOB_FREE(new_beg);
2592 if (status) break;
2596 GLOB_FREE(copy_beg);
2599 return status;
2602 static int
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;
2607 int status;
2609 list = glob_make_pattern(path, path + strlen(path), arg->flags, enc);
2610 if (!list) {
2611 return -1;
2613 status = glob_helper(arg->fd, arg->path, arg->baselen, arg->namelen, arg->dirsep,
2614 arg->pathtype, &list, &list + 1, arg->flags, arg->funcs,
2615 arg->arg, enc);
2616 glob_free_pattern(list);
2617 return status;
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 {
2624 int fd;
2625 const char *base;
2626 int flags;
2627 const ruby_glob_funcs_t *funcs;
2628 VALUE arg;
2631 static int
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);
2638 static int
2639 ruby_glob0(const char *path, int fd, const char *base, int flags,
2640 const ruby_glob_funcs_t *funcs, VALUE arg,
2641 rb_encoding *enc)
2643 struct glob_pattern *list;
2644 const char *root, *start;
2645 char *buf;
2646 size_t n, baselen = 0;
2647 int status, dirsep = FALSE;
2649 start = root = path;
2651 if (*root == '{') {
2652 struct push_glob0_args args;
2653 args.fd = fd;
2654 args.base = base;
2655 args.flags = flags;
2656 args.funcs = funcs;
2657 args.arg = arg;
2658 return ruby_brace_expand(path, flags, push_glob0_caller, (VALUE)&args, enc, Qfalse);
2661 flags |= FNM_SYSCASE;
2662 #if defined DOSISH
2663 root = rb_enc_path_skip_prefix(root, root + strlen(root), enc);
2664 #endif
2666 if (*root == '/') root++;
2668 n = root - start;
2669 if (!n && base) {
2670 n = strlen(base);
2671 baselen = n;
2672 start = base;
2673 dirsep = TRUE;
2675 buf = GLOB_ALLOC_N(char, n + 1);
2676 if (!buf) return -1;
2677 MEMCPY(buf, start, char, n);
2678 buf[n] = '\0';
2680 list = glob_make_pattern(root, root + strlen(root), flags, enc);
2681 if (!list) {
2682 GLOB_FREE(buf);
2683 return -1;
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);
2689 GLOB_FREE(buf);
2691 return status;
2695 ruby_glob(const char *path, int flags, ruby_glob_func *func, VALUE arg)
2697 ruby_glob_funcs_t funcs;
2698 funcs.match = func;
2699 funcs.error = 0;
2700 return ruby_glob0(path, AT_FDCWD, 0, flags & ~GLOB_VERBOSE,
2701 &funcs, arg, rb_ascii8bit_encoding());
2704 static int
2705 rb_glob_caller(const char *path, VALUE a, void *enc)
2707 int status;
2708 struct glob_args *args = (struct glob_args *)a;
2710 args->path = path;
2711 rb_protect(glob_func_caller, a, &status);
2712 return status;
2715 static const ruby_glob_funcs_t rb_glob_funcs = {
2716 rb_glob_caller, rb_glob_error,
2719 void
2720 rb_glob(const char *path, void (*func)(const char *, VALUE, void *), VALUE arg)
2722 struct glob_args args;
2723 int status;
2725 args.func = func;
2726 args.value = arg;
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);
2734 static void
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);
2741 #else
2742 VALUE name = rb_external_str_new_with_enc(path, strlen(path), enc);
2743 #endif
2744 rb_ary_push(ary, name);
2747 static int
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);
2754 const char *s = p;
2755 const char *lbrace = 0, *rbrace = 0;
2756 int nest = 0, status = 0;
2758 while (*p) {
2759 if (*p == '{' && nest++ == 0) {
2760 lbrace = p;
2762 if (*p == '}' && lbrace && --nest == 0) {
2763 rbrace = p;
2764 break;
2766 if (*p == '\\' && escape) {
2767 if (!*++p) break;
2769 Inc(p, pend, enc);
2772 if (lbrace && rbrace) {
2773 size_t len = strlen(s) + 1;
2774 char *buf = GLOB_ALLOC_N(char, len);
2775 long shift;
2777 if (!buf) return -1;
2778 memcpy(buf, s, lbrace-s);
2779 shift = (lbrace-s);
2780 p = lbrace;
2781 while (p < rbrace) {
2782 const char *t = ++p;
2783 nest = 0;
2784 while (p < rbrace && !(*p == ',' && nest == 0)) {
2785 if (*p == '{') nest++;
2786 if (*p == '}') nest--;
2787 if (*p == '\\' && escape) {
2788 if (++p == rbrace) break;
2790 Inc(p, pend, enc);
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);
2795 if (status) break;
2797 GLOB_FREE(buf);
2799 else if (!lbrace && !rbrace) {
2800 status = glob_call_func(func, s, arg, enc);
2803 RB_GC_GUARD(var);
2804 return status;
2807 struct brace_args {
2808 ruby_glob_funcs_t funcs;
2809 VALUE value;
2810 int flags;
2813 static int
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;
2829 args.value = arg;
2830 args.flags = flags;
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());
2840 static int
2841 push_glob(VALUE ary, VALUE str, VALUE base, int flags)
2843 struct glob_args args;
2844 int fd;
2845 rb_encoding *enc = rb_enc_get(str);
2847 #if defined _WIN32 || defined __APPLE__
2848 str = rb_str_encode_ospath(str);
2849 #endif
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;
2856 args.value = ary;
2857 args.enc = enc;
2858 args.base = 0;
2859 fd = AT_FDCWD;
2860 if (!NIL_P(base)) {
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();
2864 #ifdef HAVE_DIRFD
2865 if ((fd = dirfd(dirp->dir)) == -1)
2866 rb_sys_fail_path(dir_inspect(base));
2867 #endif
2868 base = dirp->path;
2870 args.base = RSTRING_PTR(base);
2872 #if defined _WIN32 || defined __APPLE__
2873 enc = rb_utf8_encoding();
2874 #endif
2876 return ruby_glob0(RSTRING_PTR(str), fd, args.base, flags, &rb_glob_funcs,
2877 (VALUE)&args, enc);
2880 static VALUE
2881 rb_push_glob(VALUE str, VALUE base, int flags) /* '\0' is delimiter */
2883 VALUE ary;
2884 int status;
2886 /* can contain null bytes as separators */
2887 if (!RB_TYPE_P(str, T_STRING)) {
2888 FilePathValue(str);
2890 else if (!rb_str_to_cstr(str)) {
2891 rb_raise(rb_eArgError, "nul-separated glob pattern is deprecated");
2893 else {
2894 rb_enc_check(str, rb_enc_from_encoding(rb_usascii_encoding()));
2896 ary = rb_ary_new();
2898 status = push_glob(ary, str, base, flags);
2899 if (status) GLOB_JUMP_TAG(status);
2901 return ary;
2904 static VALUE
2905 dir_globs(VALUE args, VALUE base, int flags)
2907 VALUE ary = rb_ary_new();
2908 long i;
2910 for (i = 0; i < RARRAY_LEN(args); ++i) {
2911 int status;
2912 VALUE str = RARRAY_AREF(args, i);
2913 FilePathValue(str);
2914 status = push_glob(ary, str, base, flags);
2915 if (status) GLOB_JUMP_TAG(status);
2917 RB_GC_GUARD(args);
2919 return ary;
2922 static VALUE
2923 dir_glob_option_base(VALUE base)
2925 if (base == Qundef || NIL_P(base)) {
2926 return Qnil;
2928 #if USE_OPENDIR_AT
2929 if (rb_typeddata_is_kind_of(base, &dir_data_type)) {
2930 return base;
2932 #endif
2933 FilePathValue(base);
2934 if (!RSTRING_LEN(base)) return Qnil;
2935 return base;
2938 static int
2939 dir_glob_option_sort(VALUE sort)
2941 return (rb_bool_expected(sort, "sort") ? 0 : FNM_GLOB_NOSORT);
2944 static VALUE
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);
2955 static VALUE
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);
2961 if (NIL_P(ary)) {
2962 ary = rb_push_glob(str, base, flags);
2964 else {
2965 ary = dir_globs(ary, base, flags);
2968 if (rb_block_given_p()) {
2969 rb_ary_each(ary);
2970 return Qnil;
2972 return ary;
2975 static VALUE
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);
2981 return dir;
2986 * call-seq:
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>
3001 * Got .
3002 * Got ..
3003 * Got config.h
3004 * Got main.rb
3007 static VALUE
3008 dir_foreach(int argc, VALUE *argv, VALUE io)
3010 VALUE dir;
3012 RETURN_ENUMERATOR(io, argc, argv);
3013 dir = dir_open_dir(argc, argv);
3014 rb_ensure(dir_each, dir, dir_close, dir);
3015 return Qnil;
3018 static VALUE
3019 dir_collect(VALUE dir)
3021 VALUE ary = rb_ary_new();
3022 dir_each_entry(dir, rb_ary_push, ary, FALSE);
3023 return ary;
3027 * call-seq:
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
3033 * doesn't exist.
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"]
3041 static VALUE
3042 dir_entries(int argc, VALUE *argv, VALUE io)
3044 VALUE dir;
3046 dir = dir_open_dir(argc, argv);
3047 return rb_ensure(dir_collect, dir, dir_close, dir);
3050 static VALUE
3051 dir_each_child(VALUE dir)
3053 return dir_each_entry(dir, dir_yield, Qnil, TRUE);
3057 * call-seq:
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
3065 * to the block.
3067 * If no block is given, an enumerator is returned instead.
3069 * Dir.each_child("testdir") {|x| puts "Got #{x}" }
3071 * <em>produces:</em>
3073 * Got config.h
3074 * Got main.rb
3077 static VALUE
3078 dir_s_each_child(int argc, VALUE *argv, VALUE io)
3080 VALUE dir;
3082 RETURN_ENUMERATOR(io, argc, argv);
3083 dir = dir_open_dir(argc, argv);
3084 rb_ensure(dir_each_child, dir, dir_close, dir);
3085 return Qnil;
3089 * call-seq:
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
3095 * to the block.
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>
3104 * Got config.h
3105 * Got main.rb
3108 static VALUE
3109 dir_each_child_m(VALUE dir)
3111 RETURN_ENUMERATOR(dir, 0, 0);
3112 return dir_each_entry(dir, dir_yield, Qnil, TRUE);
3116 * call-seq:
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"]
3126 static VALUE
3127 dir_collect_children(VALUE dir)
3129 VALUE ary = rb_ary_new();
3130 dir_each_entry(dir, rb_ary_push, ary, TRUE);
3131 return ary;
3135 * call-seq:
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"]
3149 static VALUE
3150 dir_s_children(int argc, VALUE *argv, VALUE io)
3152 VALUE dir;
3154 dir = dir_open_dir(argc, argv);
3155 return rb_ensure(dir_collect_children, dir, dir_close, dir);
3158 static int
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))
3168 return FNM_NOMATCH;
3169 if (!rb_enc_asciicompat(enc_path))
3170 return FNM_NOMATCH;
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)
3176 return FNM_NOMATCH;
3177 if (cr != ENC_CODERANGE_7BIT)
3178 return FNM_NOMATCH;
3181 return (fnmatch(pattern, enc, RSTRING_PTR(path), arg->flags) == 0);
3184 /* :nodoc: */
3185 static VALUE
3186 file_s_fnmatch(int argc, VALUE *argv, VALUE obj)
3188 VALUE pattern, path;
3189 VALUE rflags;
3190 int flags;
3192 if (rb_scan_args(argc, argv, "21", &pattern, &path, &rflags) == 3)
3193 flags = NUM2INT(rflags);
3194 else
3195 flags = 0;
3197 StringValueCStr(pattern);
3198 FilePathStringValue(path);
3200 if (flags & FNM_EXTGLOB) {
3201 struct brace_args args;
3203 args.value = path;
3204 args.flags = flags;
3205 if (ruby_brace_expand(RSTRING_PTR(pattern), flags, fnmatch_brace,
3206 (VALUE)&args, rb_enc_get(pattern), pattern) > 0)
3207 return Qtrue;
3209 else {
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)
3213 return Qtrue;
3215 RB_GC_GUARD(pattern);
3217 return Qfalse;
3221 * call-seq:
3222 * Dir.home() -> "/home/me"
3223 * Dir.home("root") -> "/root"
3225 * Returns the home directory of the current user or the named user
3226 * if given.
3228 static VALUE
3229 dir_s_home(int argc, VALUE *argv, VALUE obj)
3231 VALUE user;
3232 const char *u = 0;
3234 rb_check_arity(argc, 0, 1);
3235 user = (argc > 0) ? argv[0] : Qnil;
3236 if (!NIL_P(user)) {
3237 SafeStringValue(user);
3238 rb_must_asciicompat(user);
3239 u = StringValueCStr(user);
3240 if (*u) {
3241 return rb_home_dir_of(user, rb_str_new(0, 0));
3244 return rb_default_home_dir(rb_str_new(0, 0));
3248 #if 0
3250 * call-seq:
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.
3257 VALUE
3258 rb_file_directory_p(void)
3261 #endif
3263 static void *
3264 nogvl_dir_empty_p(void *ptr)
3266 const char *path = ptr;
3267 DIR *dir = opendir(path);
3268 struct dirent *dp;
3269 VALUE result = Qtrue;
3271 if (!dir) {
3272 int e = errno;
3273 switch (gc_for_fd_with_gvl(e)) {
3274 default:
3275 dir = opendir(path);
3276 if (dir) break;
3277 e = errno;
3278 /* fall through */
3279 case 0:
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)) {
3287 result = Qfalse;
3288 break;
3291 closedir(dir);
3292 return (void *)result;
3296 * call-seq:
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.
3302 static VALUE
3303 rb_dir_s_empty_p(VALUE obj, VALUE dirname)
3305 VALUE result, orig;
3306 const char *path;
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) {
3322 al.commonattr = 0;
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);
3332 #endif
3334 result = (VALUE)rb_thread_call_without_gvl(nogvl_dir_empty_p, (void *)path,
3335 RUBY_UBF_IO, 0);
3336 if (result == Qundef) {
3337 rb_sys_fail_path(orig);
3339 return result;
3342 void
3343 Init_Dir(void)
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
3395 * separators
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
3409 * patterns).
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
3422 * 0.
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"