Tempfile document updated.
[ruby.git] / dir.c
blob84ef5ee6f57c5a26b39f3f8ac0f5406f38ae7ac6
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 #if defined(IFTODT) && defined(DT_UNKNOWN)
191 # define EMULATE_IFTODT 0
192 #else
193 # define EMULATE_IFTODT 1
194 #endif
196 #if EMULATE_IFTODT
197 # define IFTODT(m) (((m) & S_IFMT) / ((~S_IFMT & (S_IFMT-1)) + 1))
198 #endif
200 typedef enum {
201 #if !EMULATE_IFTODT
202 path_exist = DT_UNKNOWN,
203 path_directory = DT_DIR,
204 path_regular = DT_REG,
205 path_symlink = DT_LNK,
206 #else
207 path_exist,
208 path_directory = IFTODT(S_IFDIR),
209 path_regular = IFTODT(S_IFREG),
210 path_symlink = IFTODT(S_IFLNK),
211 #endif
212 path_noent = -1,
213 path_unknown = -2
214 } rb_pathtype_t;
216 #define FNM_NOESCAPE 0x01
217 #define FNM_PATHNAME 0x02
218 #define FNM_DOTMATCH 0x04
219 #define FNM_CASEFOLD 0x08
220 #define FNM_EXTGLOB 0x10
221 #if CASEFOLD_FILESYSTEM
222 #define FNM_SYSCASE FNM_CASEFOLD
223 #else
224 #define FNM_SYSCASE 0
225 #endif
226 #ifdef _WIN32
227 #define FNM_SHORTNAME 0x20
228 #else
229 #define FNM_SHORTNAME 0
230 #endif
231 #define FNM_GLOB_NOSORT 0x40
232 #define FNM_GLOB_SKIPDOT 0x80
234 #define FNM_NOMATCH 1
235 #define FNM_ERROR 2
237 # define Next(p, e, enc) ((p)+ rb_enc_mbclen((p), (e), (enc)))
238 # define Inc(p, e, enc) ((p) = Next((p), (e), (enc)))
240 static char *
241 bracket(
242 const char *p, /* pattern (next to '[') */
243 const char *pend,
244 const char *s, /* string */
245 const char *send,
246 int flags,
247 rb_encoding *enc)
249 const int nocase = flags & FNM_CASEFOLD;
250 const int escape = !(flags & FNM_NOESCAPE);
251 unsigned int c1, c2;
252 int r;
253 int ok = 0, not = 0;
255 if (p >= pend) return NULL;
256 if (*p == '!' || *p == '^') {
257 not = 1;
258 p++;
261 while (*p != ']') {
262 const char *t1 = p;
263 if (escape && *t1 == '\\')
264 t1++;
265 if (!*t1)
266 return NULL;
267 p = t1 + (r = rb_enc_mbclen(t1, pend, enc));
268 if (p >= pend) return NULL;
269 if (p[0] == '-' && p[1] != ']') {
270 const char *t2 = p + 1;
271 int r2;
272 if (escape && *t2 == '\\')
273 t2++;
274 if (!*t2)
275 return NULL;
276 p = t2 + (r2 = rb_enc_mbclen(t2, pend, enc));
277 if (ok) continue;
278 if ((r <= (send-s) && memcmp(t1, s, r) == 0) ||
279 (r2 <= (send-s) && memcmp(t2, s, r2) == 0)) {
280 ok = 1;
281 continue;
283 c1 = rb_enc_codepoint(s, send, enc);
284 if (nocase) c1 = rb_enc_toupper(c1, enc);
285 c2 = rb_enc_codepoint(t1, pend, enc);
286 if (nocase) c2 = rb_enc_toupper(c2, enc);
287 if (c1 < c2) continue;
288 c2 = rb_enc_codepoint(t2, pend, enc);
289 if (nocase) c2 = rb_enc_toupper(c2, enc);
290 if (c1 > c2) continue;
292 else {
293 if (ok) continue;
294 if (r <= (send-s) && memcmp(t1, s, r) == 0) {
295 ok = 1;
296 continue;
298 if (!nocase) continue;
299 c1 = rb_enc_toupper(rb_enc_codepoint(s, send, enc), enc);
300 c2 = rb_enc_toupper(rb_enc_codepoint(p, pend, enc), enc);
301 if (c1 != c2) continue;
303 ok = 1;
306 return ok == not ? NULL : (char *)p + 1;
309 /* If FNM_PATHNAME is set, only path element will be matched. (up to '/' or '\0')
310 Otherwise, entire string will be matched.
311 End marker itself won't be compared.
312 And if function succeeds, *pcur reaches end marker.
314 #define UNESCAPE(p) (escape && *(p) == '\\' ? (p) + 1 : (p))
315 #define ISEND(p) (!*(p) || (pathname && *(p) == '/'))
316 #define RETURN(val) return *pcur = p, *scur = s, (val);
318 static int
319 fnmatch_helper(
320 const char **pcur, /* pattern */
321 const char **scur, /* string */
322 int flags,
323 rb_encoding *enc)
325 const int period = !(flags & FNM_DOTMATCH);
326 const int pathname = flags & FNM_PATHNAME;
327 const int escape = !(flags & FNM_NOESCAPE);
328 const int nocase = flags & FNM_CASEFOLD;
330 const char *ptmp = 0;
331 const char *stmp = 0;
333 const char *p = *pcur;
334 const char *pend = p + strlen(p);
335 const char *s = *scur;
336 const char *send = s + strlen(s);
338 int r;
340 if (period && *s == '.' && *UNESCAPE(p) != '.') /* leading period */
341 RETURN(FNM_NOMATCH);
343 while (1) {
344 switch (*p) {
345 case '*':
346 do { p++; } while (*p == '*');
347 if (ISEND(UNESCAPE(p))) {
348 p = UNESCAPE(p);
349 RETURN(0);
351 if (ISEND(s))
352 RETURN(FNM_NOMATCH);
353 ptmp = p;
354 stmp = s;
355 continue;
357 case '?':
358 if (ISEND(s))
359 RETURN(FNM_NOMATCH);
360 p++;
361 Inc(s, send, enc);
362 continue;
364 case '[': {
365 const char *t;
366 if (ISEND(s))
367 RETURN(FNM_NOMATCH);
368 if ((t = bracket(p + 1, pend, s, send, flags, enc)) != 0) {
369 p = t;
370 Inc(s, send, enc);
371 continue;
373 goto failed;
377 /* ordinary */
378 p = UNESCAPE(p);
379 if (ISEND(s))
380 RETURN(ISEND(p) ? 0 : FNM_NOMATCH);
381 if (ISEND(p))
382 goto failed;
383 r = rb_enc_precise_mbclen(p, pend, enc);
384 if (!MBCLEN_CHARFOUND_P(r))
385 goto failed;
386 if (r <= (send-s) && memcmp(p, s, r) == 0) {
387 p += r;
388 s += r;
389 continue;
391 if (!nocase) goto failed;
392 if (rb_enc_toupper(rb_enc_codepoint(p, pend, enc), enc) !=
393 rb_enc_toupper(rb_enc_codepoint(s, send, enc), enc))
394 goto failed;
395 p += r;
396 Inc(s, send, enc);
397 continue;
399 failed: /* try next '*' position */
400 if (ptmp && stmp) {
401 p = ptmp;
402 Inc(stmp, send, enc); /* !ISEND(*stmp) */
403 s = stmp;
404 continue;
406 RETURN(FNM_NOMATCH);
410 static int
411 fnmatch(
412 const char *pattern,
413 rb_encoding *enc,
414 const char *string,
415 int flags)
417 const char *p = pattern;
418 const char *s = string;
419 const char *send = s + strlen(string);
420 const int period = !(flags & FNM_DOTMATCH);
421 const int pathname = flags & FNM_PATHNAME;
423 const char *ptmp = 0;
424 const char *stmp = 0;
426 if (pathname) {
427 while (1) {
428 if (p[0] == '*' && p[1] == '*' && p[2] == '/') {
429 do { p += 3; } while (p[0] == '*' && p[1] == '*' && p[2] == '/');
430 ptmp = p;
431 stmp = s;
433 if (fnmatch_helper(&p, &s, flags, enc) == 0) {
434 while (*s && *s != '/') Inc(s, send, enc);
435 if (*p && *s) {
436 p++;
437 s++;
438 continue;
440 if (!*p && !*s)
441 return 0;
443 /* failed : try next recursion */
444 if (ptmp && stmp && !(period && *stmp == '.')) {
445 while (*stmp && *stmp != '/') Inc(stmp, send, enc);
446 if (*stmp) {
447 p = ptmp;
448 stmp++;
449 s = stmp;
450 continue;
453 return FNM_NOMATCH;
456 else
457 return fnmatch_helper(&p, &s, flags, enc);
460 VALUE rb_cDir;
462 struct dir_data {
463 DIR *dir;
464 const VALUE path;
465 rb_encoding *enc;
468 static void
469 dir_free(void *ptr)
471 struct dir_data *dir = ptr;
473 if (dir->dir) closedir(dir->dir);
476 RUBY_REFERENCES(dir_refs) = {
477 RUBY_REF_EDGE(struct dir_data, path),
478 RUBY_REF_END
481 static const rb_data_type_t dir_data_type = {
482 "dir",
484 RUBY_REFS_LIST_PTR(dir_refs),
485 dir_free,
486 NULL, // Nothing allocated externally, so don't need a memsize function
488 0, NULL, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_DECL_MARKING | RUBY_TYPED_EMBEDDABLE
491 static VALUE dir_close(VALUE);
493 static VALUE
494 dir_s_alloc(VALUE klass)
496 struct dir_data *dirp;
497 VALUE obj = TypedData_Make_Struct(klass, struct dir_data, &dir_data_type, dirp);
499 dirp->dir = NULL;
500 RB_OBJ_WRITE(obj, &dirp->path, Qnil);
501 dirp->enc = NULL;
503 return obj;
506 static void *
507 nogvl_opendir(void *ptr)
509 const char *path = ptr;
511 return (void *)opendir(path);
514 static DIR *
515 opendir_without_gvl(const char *path)
517 if (vm_initialized) {
518 union { const void *in; void *out; } u;
520 u.in = path;
522 return IO_WITHOUT_GVL(nogvl_opendir, u.out);
524 else
525 return opendir(path);
528 static VALUE
529 dir_initialize(rb_execution_context_t *ec, VALUE dir, VALUE dirname, VALUE enc)
531 struct dir_data *dp;
532 VALUE orig;
533 const char *path;
534 rb_encoding *fsenc = NIL_P(enc) ? rb_filesystem_encoding() : rb_to_encoding(enc);
536 FilePathValue(dirname);
537 orig = rb_str_dup_frozen(dirname);
538 dirname = rb_str_encode_ospath(dirname);
539 dirname = rb_str_dup_frozen(dirname);
541 TypedData_Get_Struct(dir, struct dir_data, &dir_data_type, dp);
542 if (dp->dir) closedir(dp->dir);
543 dp->dir = NULL;
544 RB_OBJ_WRITE(dir, &dp->path, Qnil);
545 dp->enc = fsenc;
546 path = RSTRING_PTR(dirname);
547 dp->dir = opendir_without_gvl(path);
548 if (dp->dir == NULL) {
549 int e = errno;
550 if (rb_gc_for_fd(e)) {
551 dp->dir = opendir_without_gvl(path);
553 #ifdef HAVE_GETATTRLIST
554 else if (e == EIO) {
555 u_int32_t attrbuf[1];
556 struct attrlist al = {ATTR_BIT_MAP_COUNT, 0};
557 if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW) == 0) {
558 dp->dir = opendir_without_gvl(path);
561 #endif
562 if (dp->dir == NULL) {
563 RB_GC_GUARD(dirname);
564 rb_syserr_fail_path(e, orig);
567 RB_OBJ_WRITE(dir, &dp->path, orig);
569 return dir;
572 static VALUE
573 dir_s_open(rb_execution_context_t *ec, VALUE klass, VALUE dirname, VALUE enc)
575 struct dir_data *dp;
576 VALUE dir = TypedData_Make_Struct(klass, struct dir_data, &dir_data_type, dp);
578 dir_initialize(ec, dir, dirname, enc);
580 return dir;
583 static VALUE
584 dir_s_close(rb_execution_context_t *ec, VALUE klass, VALUE dir)
586 return dir_close(dir);
589 # if defined(HAVE_FDOPENDIR) && defined(HAVE_DIRFD)
591 * call-seq:
592 * Dir.for_fd(fd) -> dir
594 * Returns a new \Dir object representing the directory specified by the given
595 * integer directory file descriptor +fd+:
597 * d0 = Dir.new('..')
598 * d1 = Dir.for_fd(d0.fileno)
600 * Note that the returned +d1+ does not have an associated path:
602 * d0.path # => '..'
603 * d1.path # => nil
605 * This method uses the
606 * {fdopendir()}[https://www.man7.org/linux/man-pages/man3/fdopendir.3p.html]
607 * function defined by POSIX 2008;
608 * the method is not implemented on non-POSIX platforms (raises NotImplementedError).
610 static VALUE
611 dir_s_for_fd(VALUE klass, VALUE fd)
613 struct dir_data *dp;
614 VALUE dir = TypedData_Make_Struct(klass, struct dir_data, &dir_data_type, dp);
616 if (!(dp->dir = fdopendir(NUM2INT(fd)))) {
617 rb_sys_fail("fdopendir");
618 UNREACHABLE_RETURN(Qnil);
621 RB_OBJ_WRITE(dir, &dp->path, Qnil);
622 return dir;
624 #else
625 #define dir_s_for_fd rb_f_notimplement
626 #endif
628 NORETURN(static void dir_closed(void));
630 static void
631 dir_closed(void)
633 rb_raise(rb_eIOError, "closed directory");
636 static struct dir_data *
637 dir_get(VALUE dir)
639 rb_check_frozen(dir);
640 return rb_check_typeddata(dir, &dir_data_type);
643 static struct dir_data *
644 dir_check(VALUE dir)
646 struct dir_data *dirp = dir_get(dir);
647 if (!dirp->dir) dir_closed();
648 return dirp;
651 #define GetDIR(obj, dirp) ((dirp) = dir_check(obj))
655 * call-seq:
656 * inspect -> string
658 * Returns a string description of +self+:
660 * Dir.new('example').inspect # => "#<Dir:example>"
663 static VALUE
664 dir_inspect(VALUE dir)
666 struct dir_data *dirp;
668 TypedData_Get_Struct(dir, struct dir_data, &dir_data_type, dirp);
669 if (!NIL_P(dirp->path)) {
670 VALUE str = rb_str_new_cstr("#<");
671 rb_str_append(str, rb_class_name(CLASS_OF(dir)));
672 rb_str_cat2(str, ":");
673 rb_str_append(str, dirp->path);
674 rb_str_cat2(str, ">");
675 return str;
677 return rb_funcallv(dir, idTo_s, 0, 0);
680 /* Workaround for Solaris 10 that does not have dirfd.
681 Note: Solaris 11 (POSIX.1-2008 compliant) has dirfd(3C).
683 #if defined(__sun) && !defined(HAVE_DIRFD)
684 # if defined(HAVE_DIR_D_FD)
685 # define dirfd(x) ((x)->d_fd)
686 # define HAVE_DIRFD 1
687 # elif defined(HAVE_DIR_DD_FD)
688 # define dirfd(x) ((x)->dd_fd)
689 # define HAVE_DIRFD 1
690 # endif
691 #endif
693 #ifdef HAVE_DIRFD
695 * call-seq:
696 * fileno -> integer
698 * Returns the file descriptor used in <em>dir</em>.
700 * d = Dir.new('..')
701 * d.fileno # => 8
703 * This method uses the
704 * {dirfd()}[https://www.man7.org/linux/man-pages/man3/dirfd.3.html]
705 * function defined by POSIX 2008;
706 * the method is not implemented on non-POSIX platforms (raises NotImplementedError).
708 static VALUE
709 dir_fileno(VALUE dir)
711 struct dir_data *dirp;
712 int fd;
714 GetDIR(dir, dirp);
715 fd = dirfd(dirp->dir);
716 if (fd == -1)
717 rb_sys_fail("dirfd");
718 return INT2NUM(fd);
720 #else
721 #define dir_fileno rb_f_notimplement
722 #endif
725 * call-seq:
726 * path -> string or nil
728 * Returns the +dirpath+ string that was used to create +self+
729 * (or +nil+ if created by method Dir.for_fd):
731 * Dir.new('example').path # => "example"
734 static VALUE
735 dir_path(VALUE dir)
737 struct dir_data *dirp;
739 TypedData_Get_Struct(dir, struct dir_data, &dir_data_type, dirp);
740 if (NIL_P(dirp->path)) return Qnil;
741 return rb_str_dup(dirp->path);
744 #if defined _WIN32
745 static int
746 fundamental_encoding_p(rb_encoding *enc)
748 switch (rb_enc_to_index(enc)) {
749 case ENCINDEX_ASCII_8BIT:
750 case ENCINDEX_US_ASCII:
751 case ENCINDEX_UTF_8:
752 return TRUE;
753 default:
754 return FALSE;
757 # define READDIR(dir, enc) rb_w32_readdir((dir), (enc))
758 #else
759 # define READDIR(dir, enc) readdir((dir))
760 #endif
762 /* safe to use without GVL */
763 static int
764 to_be_skipped(const struct dirent *dp)
766 const char *name = dp->d_name;
767 if (name[0] != '.') return FALSE;
768 #ifdef HAVE_DIRENT_NAMLEN
769 switch (NAMLEN(dp)) {
770 case 2:
771 if (name[1] != '.') return FALSE;
772 case 1:
773 return TRUE;
774 default:
775 break;
777 #else
778 if (!name[1]) return TRUE;
779 if (name[1] != '.') return FALSE;
780 if (!name[2]) return TRUE;
781 #endif
782 return FALSE;
786 * call-seq:
787 * read -> string or nil
789 * Reads and returns the next entry name from +self+;
790 * returns +nil+ if at end-of-stream;
791 * see {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like]:
793 * dir = Dir.new('example')
794 * dir.read # => "."
795 * dir.read # => ".."
796 * dir.read # => "config.h"
799 static VALUE
800 dir_read(VALUE dir)
802 struct dir_data *dirp;
803 struct dirent *dp;
805 GetDIR(dir, dirp);
806 rb_errno_set(0);
807 if ((dp = READDIR(dirp->dir, dirp->enc)) != NULL) {
808 return rb_external_str_new_with_enc(dp->d_name, NAMLEN(dp), dirp->enc);
810 else {
811 int e = errno;
812 if (e != 0) rb_syserr_fail(e, 0);
813 return Qnil; /* end of stream */
817 static VALUE dir_each_entry(VALUE, VALUE (*)(VALUE, VALUE), VALUE, int);
819 static VALUE
820 dir_yield(VALUE arg, VALUE path)
822 return rb_yield(path);
826 * call-seq:
827 * each {|entry_name| ... } -> self
829 * Calls the block with each entry name in +self+:
831 * Dir.new('example').each {|entry_name| p entry_name }
833 * Output:
835 * "."
836 * ".."
837 * "config.h"
838 * "lib"
839 * "main.rb"
841 * With no block given, returns an Enumerator.
844 static VALUE
845 dir_each(VALUE dir)
847 RETURN_ENUMERATOR(dir, 0, 0);
848 return dir_each_entry(dir, dir_yield, Qnil, FALSE);
851 static VALUE
852 dir_each_entry(VALUE dir, VALUE (*each)(VALUE, VALUE), VALUE arg, int children_only)
854 struct dir_data *dirp;
855 struct dirent *dp;
856 IF_NORMALIZE_UTF8PATH(int norm_p);
858 GetDIR(dir, dirp);
859 rewinddir(dirp->dir);
860 IF_NORMALIZE_UTF8PATH(norm_p = need_normalization(dirp->dir, RSTRING_PTR(dirp->path)));
861 while ((dp = READDIR(dirp->dir, dirp->enc)) != NULL) {
862 const char *name = dp->d_name;
863 size_t namlen = NAMLEN(dp);
864 VALUE path;
866 if (children_only && name[0] == '.') {
867 if (namlen == 1) continue; /* current directory */
868 if (namlen == 2 && name[1] == '.') continue; /* parent directory */
870 #if NORMALIZE_UTF8PATH
871 if (norm_p && has_nonascii(name, namlen) &&
872 !NIL_P(path = rb_str_normalize_ospath(name, namlen))) {
873 path = rb_external_str_with_enc(path, dirp->enc);
875 else
876 #endif
877 path = rb_external_str_new_with_enc(name, namlen, dirp->enc);
878 (*each)(arg, path);
880 return dir;
883 #ifdef HAVE_TELLDIR
885 * call-seq:
886 * tell -> integer
888 * Returns the current position of +self+;
889 * see {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like]:
891 * dir = Dir.new('example')
892 * dir.tell # => 0
893 * dir.read # => "."
894 * dir.tell # => 1
897 static VALUE
898 dir_tell(VALUE dir)
900 struct dir_data *dirp;
901 long pos;
903 GetDIR(dir, dirp);
904 pos = telldir(dirp->dir);
905 return rb_int2inum(pos);
907 #else
908 #define dir_tell rb_f_notimplement
909 #endif
911 #ifdef HAVE_SEEKDIR
913 * call-seq:
914 * seek(position) -> self
916 * Sets the position in +self+ and returns +self+.
917 * The value of +position+ should have been returned from an earlier call to #tell;
918 * if not, the return values from subsequent calls to #read are unspecified.
920 * See {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like].
922 * Examples:
924 * dir = Dir.new('example')
925 * dir.pos # => 0
926 * dir.seek(3) # => #<Dir:example>
927 * dir.pos # => 3
928 * dir.seek(30) # => #<Dir:example>
929 * dir.pos # => 5
932 static VALUE
933 dir_seek(VALUE dir, VALUE pos)
935 struct dir_data *dirp;
936 long p = NUM2LONG(pos);
938 GetDIR(dir, dirp);
939 seekdir(dirp->dir, p);
940 return dir;
942 #else
943 #define dir_seek rb_f_notimplement
944 #endif
946 #ifdef HAVE_SEEKDIR
948 * call-seq:
949 * pos = position -> integer
951 * Sets the position in +self+ and returns +position+.
952 * The value of +position+ should have been returned from an earlier call to #tell;
953 * if not, the return values from subsequent calls to #read are unspecified.
955 * See {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like].
957 * Examples:
959 * dir = Dir.new('example')
960 * dir.pos # => 0
961 * dir.pos = 3 # => 3
962 * dir.pos # => 3
963 * dir.pos = 30 # => 30
964 * dir.pos # => 5
967 static VALUE
968 dir_set_pos(VALUE dir, VALUE pos)
970 dir_seek(dir, pos);
971 return pos;
973 #else
974 #define dir_set_pos rb_f_notimplement
975 #endif
978 * call-seq:
979 * rewind -> self
981 * Sets the position in +self+ to zero;
982 * see {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like]:
984 * dir = Dir.new('example')
985 * dir.read # => "."
986 * dir.read # => ".."
987 * dir.pos # => 2
988 * dir.rewind # => #<Dir:example>
989 * dir.pos # => 0
992 static VALUE
993 dir_rewind(VALUE dir)
995 struct dir_data *dirp;
997 GetDIR(dir, dirp);
998 rewinddir(dirp->dir);
999 return dir;
1003 * call-seq:
1004 * close -> nil
1006 * Closes the stream in +self+, if it is open, and returns +nil+;
1007 * ignored if +self+ is already closed:
1009 * dir = Dir.new('example')
1010 * dir.read # => "."
1011 * dir.close # => nil
1012 * dir.close # => nil
1013 * dir.read # Raises IOError.
1016 static VALUE
1017 dir_close(VALUE dir)
1019 struct dir_data *dirp;
1021 dirp = dir_get(dir);
1022 if (!dirp->dir) return Qnil;
1023 closedir(dirp->dir);
1024 dirp->dir = NULL;
1026 return Qnil;
1029 static void *
1030 nogvl_chdir(void *ptr)
1032 const char *path = ptr;
1034 return (void *)(VALUE)chdir(path);
1037 static void
1038 dir_chdir0(VALUE path)
1040 if (chdir(RSTRING_PTR(path)) < 0)
1041 rb_sys_fail_path(path);
1044 static struct {
1045 VALUE thread;
1046 VALUE path;
1047 int line;
1048 int blocking;
1049 } chdir_lock = {
1050 .blocking = 0, .thread = Qnil,
1051 .path = Qnil, .line = 0,
1054 static void
1055 chdir_enter(void)
1057 if (chdir_lock.blocking == 0) {
1058 chdir_lock.path = rb_source_location(&chdir_lock.line);
1060 chdir_lock.blocking++;
1061 if (NIL_P(chdir_lock.thread)) {
1062 chdir_lock.thread = rb_thread_current();
1066 static void
1067 chdir_leave(void)
1069 chdir_lock.blocking--;
1070 if (chdir_lock.blocking == 0) {
1071 chdir_lock.thread = Qnil;
1072 chdir_lock.path = Qnil;
1073 chdir_lock.line = 0;
1077 static int
1078 chdir_alone_block_p(void)
1080 int block_given = rb_block_given_p();
1081 if (chdir_lock.blocking > 0) {
1082 if (rb_thread_current() != chdir_lock.thread)
1083 rb_raise(rb_eRuntimeError, "conflicting chdir during another chdir block");
1084 if (!block_given) {
1085 if (!NIL_P(chdir_lock.path)) {
1086 rb_warn("conflicting chdir during another chdir block\n"
1087 "%" PRIsVALUE ":%d: note: previous chdir was here",
1088 chdir_lock.path, chdir_lock.line);
1090 else {
1091 rb_warn("conflicting chdir during another chdir block");
1095 return block_given;
1098 struct chdir_data {
1099 VALUE old_path, new_path;
1100 int done;
1101 bool yield_path;
1104 static VALUE
1105 chdir_yield(VALUE v)
1107 struct chdir_data *args = (void *)v;
1108 dir_chdir0(args->new_path);
1109 args->done = TRUE;
1110 chdir_enter();
1111 return args->yield_path ? rb_yield(args->new_path) : rb_yield_values2(0, NULL);
1114 static VALUE
1115 chdir_restore(VALUE v)
1117 struct chdir_data *args = (void *)v;
1118 if (args->done) {
1119 chdir_leave();
1120 dir_chdir0(args->old_path);
1122 return Qnil;
1125 static VALUE
1126 chdir_path(VALUE path, bool yield_path)
1128 if (chdir_alone_block_p()) {
1129 struct chdir_data args;
1131 args.old_path = rb_str_encode_ospath(rb_dir_getwd());
1132 args.new_path = path;
1133 args.done = FALSE;
1134 args.yield_path = yield_path;
1135 return rb_ensure(chdir_yield, (VALUE)&args, chdir_restore, (VALUE)&args);
1137 else {
1138 char *p = RSTRING_PTR(path);
1139 int r = IO_WITHOUT_GVL_INT(nogvl_chdir, p);
1140 if (r < 0)
1141 rb_sys_fail_path(path);
1144 return INT2FIX(0);
1148 * call-seq:
1149 * Dir.chdir(new_dirpath) -> 0
1150 * Dir.chdir -> 0
1151 * Dir.chdir(new_dirpath) {|new_dirpath| ... } -> object
1152 * Dir.chdir {|cur_dirpath| ... } -> object
1154 * Changes the current working directory.
1156 * With argument +new_dirpath+ and no block,
1157 * changes to the given +dirpath+:
1159 * Dir.pwd # => "/example"
1160 * Dir.chdir('..') # => 0
1161 * Dir.pwd # => "/"
1163 * With no argument and no block:
1165 * - Changes to the value of environment variable +HOME+ if defined.
1166 * - Otherwise changes to the value of environment variable +LOGDIR+ if defined.
1167 * - Otherwise makes no change.
1169 * With argument +new_dirpath+ and a block, temporarily changes the working directory:
1171 * - Calls the block with the argument.
1172 * - Changes to the given directory.
1173 * - Executes the block (yielding the new path).
1174 * - Restores the previous working directory.
1175 * - Returns the block's return value.
1177 * Example:
1179 * Dir.chdir('/var/spool/mail')
1180 * Dir.pwd # => "/var/spool/mail"
1181 * Dir.chdir('/tmp') do
1182 * Dir.pwd # => "/tmp"
1183 * end
1184 * Dir.pwd # => "/var/spool/mail"
1186 * With no argument and a block,
1187 * calls the block with the current working directory (string)
1188 * and returns the block's return value.
1190 * Calls to \Dir.chdir with blocks may be nested:
1192 * Dir.chdir('/var/spool/mail')
1193 * Dir.pwd # => "/var/spool/mail"
1194 * Dir.chdir('/tmp') do
1195 * Dir.pwd # => "/tmp"
1196 * Dir.chdir('/usr') do
1197 * Dir.pwd # => "/usr"
1198 * end
1199 * Dir.pwd # => "/tmp"
1200 * end
1201 * Dir.pwd # => "/var/spool/mail"
1203 * In a multi-threaded program an error is raised if a thread attempts
1204 * to open a +chdir+ block while another thread has one open,
1205 * or a call to +chdir+ without a block occurs inside
1206 * a block passed to +chdir+ (even in the same thread).
1208 * Raises an exception if the target directory does not exist.
1210 static VALUE
1211 dir_s_chdir(int argc, VALUE *argv, VALUE obj)
1213 VALUE path = Qnil;
1215 if (rb_check_arity(argc, 0, 1) == 1) {
1216 path = rb_str_encode_ospath(rb_get_path(argv[0]));
1218 else {
1219 const char *dist = getenv("HOME");
1220 if (!dist) {
1221 dist = getenv("LOGDIR");
1222 if (!dist) rb_raise(rb_eArgError, "HOME/LOGDIR not set");
1224 path = rb_str_new2(dist);
1227 return chdir_path(path, true);
1230 #if defined(HAVE_FCHDIR) && defined(HAVE_DIRFD) && HAVE_FCHDIR && HAVE_DIRFD
1231 static void *
1232 nogvl_fchdir(void *ptr)
1234 const int *fd = ptr;
1236 return (void *)(VALUE)fchdir(*fd);
1239 static void
1240 dir_fchdir(int fd)
1242 if (fchdir(fd) < 0)
1243 rb_sys_fail("fchdir");
1246 struct fchdir_data {
1247 VALUE old_dir;
1248 int fd;
1249 int done;
1252 static VALUE
1253 fchdir_yield(VALUE v)
1255 struct fchdir_data *args = (void *)v;
1256 dir_fchdir(args->fd);
1257 args->done = TRUE;
1258 chdir_enter();
1259 return rb_yield_values(0);
1262 static VALUE
1263 fchdir_restore(VALUE v)
1265 struct fchdir_data *args = (void *)v;
1266 if (args->done) {
1267 chdir_leave();
1268 dir_fchdir(RB_NUM2INT(dir_fileno(args->old_dir)));
1270 dir_close(args->old_dir);
1271 return Qnil;
1275 * call-seq:
1276 * Dir.fchdir(fd) -> 0
1277 * Dir.fchdir(fd) { ... } -> object
1279 * Changes the current working directory to the directory
1280 * specified by the integer file descriptor +fd+.
1282 * When passing a file descriptor over a UNIX socket or to a child process,
1283 * using +fchdir+ instead of +chdir+ avoids the
1284 * {time-of-check to time-of-use vulnerability}[https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use]
1286 * With no block, changes to the directory given by +fd+:
1288 * Dir.chdir('/var/spool/mail')
1289 * Dir.pwd # => "/var/spool/mail"
1290 * dir = Dir.new('/usr')
1291 * fd = dir.fileno
1292 * Dir.fchdir(fd)
1293 * Dir.pwd # => "/usr"
1295 * With a block, temporarily changes the working directory:
1297 * - Calls the block with the argument.
1298 * - Changes to the given directory.
1299 * - Executes the block (yields no args).
1300 * - Restores the previous working directory.
1301 * - Returns the block's return value.
1303 * Example:
1305 * Dir.chdir('/var/spool/mail')
1306 * Dir.pwd # => "/var/spool/mail"
1307 * dir = Dir.new('/tmp')
1308 * fd = dir.fileno
1309 * Dir.fchdir(fd) do
1310 * Dir.pwd # => "/tmp"
1311 * end
1312 * Dir.pwd # => "/var/spool/mail"
1314 * This method uses the
1315 * {fchdir()}[https://www.man7.org/linux/man-pages/man3/fchdir.3p.html]
1316 * function defined by POSIX 2008;
1317 * the method is not implemented on non-POSIX platforms (raises NotImplementedError).
1319 * Raises an exception if the file descriptor is not valid.
1321 * In a multi-threaded program an error is raised if a thread attempts
1322 * to open a +chdir+ block while another thread has one open,
1323 * or a call to +chdir+ without a block occurs inside
1324 * a block passed to +chdir+ (even in the same thread).
1326 static VALUE
1327 dir_s_fchdir(VALUE klass, VALUE fd_value)
1329 int fd = RB_NUM2INT(fd_value);
1331 if (chdir_alone_block_p()) {
1332 struct fchdir_data args;
1333 args.old_dir = dir_s_alloc(klass);
1334 dir_initialize(NULL, args.old_dir, rb_fstring_cstr("."), Qnil);
1335 args.fd = fd;
1336 args.done = FALSE;
1337 return rb_ensure(fchdir_yield, (VALUE)&args, fchdir_restore, (VALUE)&args);
1339 else {
1340 int r = IO_WITHOUT_GVL_INT(nogvl_fchdir, &fd);
1341 if (r < 0)
1342 rb_sys_fail("fchdir");
1345 return INT2FIX(0);
1347 #else
1348 #define dir_s_fchdir rb_f_notimplement
1349 #endif
1352 * call-seq:
1353 * chdir -> 0
1354 * chdir { ... } -> object
1356 * Changes the current working directory to +self+:
1358 * Dir.pwd # => "/"
1359 * dir = Dir.new('example')
1360 * dir.chdir
1361 * Dir.pwd # => "/example"
1363 * With a block, temporarily changes the working directory:
1365 * - Calls the block.
1366 * - Changes to the given directory.
1367 * - Executes the block (yields no args).
1368 * - Restores the previous working directory.
1369 * - Returns the block's return value.
1371 * Uses Dir.fchdir if available, and Dir.chdir if not, see those
1372 * methods for caveats.
1374 static VALUE
1375 dir_chdir(VALUE dir)
1377 #if defined(HAVE_FCHDIR) && defined(HAVE_DIRFD) && HAVE_FCHDIR && HAVE_DIRFD
1378 return dir_s_fchdir(rb_cDir, dir_fileno(dir));
1379 #else
1380 return chdir_path(dir_get(dir)->path, false);
1381 #endif
1384 #ifndef _WIN32
1385 VALUE
1386 rb_dir_getwd_ospath(void)
1388 char *path;
1389 VALUE cwd;
1390 VALUE path_guard;
1392 #undef RUBY_UNTYPED_DATA_WARNING
1393 #define RUBY_UNTYPED_DATA_WARNING 0
1394 path_guard = Data_Wrap_Struct((VALUE)0, NULL, RUBY_DEFAULT_FREE, NULL);
1395 path = ruby_getcwd();
1396 DATA_PTR(path_guard) = path;
1397 #ifdef __APPLE__
1398 cwd = rb_str_normalize_ospath(path, strlen(path));
1399 #else
1400 cwd = rb_str_new2(path);
1401 #endif
1402 DATA_PTR(path_guard) = 0;
1404 xfree(path);
1405 return cwd;
1407 #endif
1409 VALUE
1410 rb_dir_getwd(void)
1412 rb_encoding *fs = rb_filesystem_encoding();
1413 int fsenc = rb_enc_to_index(fs);
1414 VALUE cwd = rb_dir_getwd_ospath();
1416 switch (fsenc) {
1417 case ENCINDEX_US_ASCII:
1418 fsenc = ENCINDEX_ASCII_8BIT;
1419 case ENCINDEX_ASCII_8BIT:
1420 break;
1421 #if defined _WIN32 || defined __APPLE__
1422 default:
1423 return rb_str_conv_enc(cwd, NULL, fs);
1424 #endif
1426 return rb_enc_associate_index(cwd, fsenc);
1430 * call-seq:
1431 * Dir.pwd -> string
1433 * Returns the path to the current working directory:
1435 * Dir.chdir("/tmp") # => 0
1436 * Dir.pwd # => "/tmp"
1439 static VALUE
1440 dir_s_getwd(VALUE dir)
1442 return rb_dir_getwd();
1445 static VALUE
1446 check_dirname(VALUE dir)
1448 VALUE d = dir;
1449 char *path, *pend;
1450 long len;
1451 rb_encoding *enc;
1453 FilePathValue(d);
1454 enc = rb_enc_get(d);
1455 RSTRING_GETMEM(d, path, len);
1456 pend = path + len;
1457 pend = rb_enc_path_end(rb_enc_path_skip_prefix(path, pend, enc), pend, enc);
1458 if (pend - path < len) {
1459 d = rb_str_subseq(d, 0, pend - path);
1460 StringValueCStr(d);
1462 return rb_str_encode_ospath(d);
1465 #if defined(HAVE_CHROOT)
1467 * call-seq:
1468 * Dir.chroot(dirpath) -> 0
1470 * Changes the root directory of the calling process to that specified in +dirpath+.
1471 * The new root directory is used for pathnames beginning with <tt>'/'</tt>.
1472 * The root directory is inherited by all children of the calling process.
1474 * Only a privileged process may call +chroot+.
1476 * See {Linux chroot}[https://man7.org/linux/man-pages/man2/chroot.2.html].
1478 static VALUE
1479 dir_s_chroot(VALUE dir, VALUE path)
1481 path = check_dirname(path);
1482 if (chroot(RSTRING_PTR(path)) == -1)
1483 rb_sys_fail_path(path);
1485 return INT2FIX(0);
1487 #else
1488 #define dir_s_chroot rb_f_notimplement
1489 #endif
1491 struct mkdir_arg {
1492 const char *path;
1493 mode_t mode;
1496 static void *
1497 nogvl_mkdir(void *ptr)
1499 struct mkdir_arg *m = ptr;
1501 return (void *)(VALUE)mkdir(m->path, m->mode);
1505 * call-seq:
1506 * Dir.mkdir(dirpath, permissions = 0775) -> 0
1508 * Creates a directory in the underlying file system
1509 * at +dirpath+ with the given +permissions+;
1510 * returns zero:
1512 * Dir.mkdir('foo')
1513 * File.stat(Dir.new('foo')).mode.to_s(8)[1..4] # => "0755"
1514 * Dir.mkdir('bar', 0644)
1515 * File.stat(Dir.new('bar')).mode.to_s(8)[1..4] # => "0644"
1517 * See {File Permissions}[rdoc-ref:File@File+Permissions].
1518 * Note that argument +permissions+ is ignored on Windows.
1520 static VALUE
1521 dir_s_mkdir(int argc, VALUE *argv, VALUE obj)
1523 struct mkdir_arg m;
1524 VALUE path, vmode;
1525 int r;
1527 if (rb_scan_args(argc, argv, "11", &path, &vmode) == 2) {
1528 m.mode = NUM2MODET(vmode);
1530 else {
1531 m.mode = 0777;
1534 path = check_dirname(path);
1535 m.path = RSTRING_PTR(path);
1536 r = IO_WITHOUT_GVL_INT(nogvl_mkdir, &m);
1537 if (r < 0)
1538 rb_sys_fail_path(path);
1540 return INT2FIX(0);
1543 static void *
1544 nogvl_rmdir(void *ptr)
1546 const char *path = ptr;
1548 return (void *)(VALUE)rmdir(path);
1552 * call-seq:
1553 * Dir.rmdir(dirpath) -> 0
1555 * Removes the directory at +dirpath+ from the underlying file system:
1557 * Dir.rmdir('foo') # => 0
1559 * Raises an exception if the directory is not empty.
1561 static VALUE
1562 dir_s_rmdir(VALUE obj, VALUE dir)
1564 const char *p;
1565 int r;
1567 dir = check_dirname(dir);
1568 p = RSTRING_PTR(dir);
1569 r = IO_WITHOUT_GVL_INT(nogvl_rmdir, (void *)p);
1570 if (r < 0)
1571 rb_sys_fail_path(dir);
1573 return INT2FIX(0);
1576 struct warning_args {
1577 #ifdef RUBY_FUNCTION_NAME_STRING
1578 const char *func;
1579 #endif
1580 const char *mesg;
1581 rb_encoding *enc;
1584 #ifndef RUBY_FUNCTION_NAME_STRING
1585 #define sys_enc_warning_in(func, mesg, enc) sys_enc_warning(mesg, enc)
1586 #endif
1588 static VALUE
1589 sys_warning_1(VALUE mesg)
1591 const struct warning_args *arg = (struct warning_args *)mesg;
1592 #ifdef RUBY_FUNCTION_NAME_STRING
1593 rb_sys_enc_warning(arg->enc, "%s: %s", arg->func, arg->mesg);
1594 #else
1595 rb_sys_enc_warning(arg->enc, "%s", arg->mesg);
1596 #endif
1597 return Qnil;
1600 static void
1601 sys_enc_warning_in(const char *func, const char *mesg, rb_encoding *enc)
1603 struct warning_args arg;
1604 #ifdef RUBY_FUNCTION_NAME_STRING
1605 arg.func = func;
1606 #endif
1607 arg.mesg = mesg;
1608 arg.enc = enc;
1609 rb_protect(sys_warning_1, (VALUE)&arg, 0);
1612 #define GLOB_VERBOSE (1U << (sizeof(int) * CHAR_BIT - 1))
1613 #define sys_warning(val, enc) \
1614 ((flags & GLOB_VERBOSE) ? sys_enc_warning_in(RUBY_FUNCTION_NAME_STRING, (val), (enc)) :(void)0)
1616 static inline size_t
1617 glob_alloc_size(size_t x, size_t y)
1619 size_t z;
1620 if (rb_mul_size_overflow(x, y, SSIZE_MAX, &z)) {
1621 rb_memerror(); /* or...? */
1623 else {
1624 return z;
1628 static inline void *
1629 glob_alloc_n(size_t x, size_t y)
1631 return malloc(glob_alloc_size(x, y));
1634 static inline void *
1635 glob_realloc_n(void *p, size_t x, size_t y)
1637 return realloc(p, glob_alloc_size(x, y));
1640 #define GLOB_ALLOC(type) ((type *)malloc(sizeof(type)))
1641 #define GLOB_ALLOC_N(type, n) ((type *)glob_alloc_n(sizeof(type), n))
1642 #define GLOB_REALLOC(ptr, size) realloc((ptr), (size))
1643 #define GLOB_REALLOC_N(ptr, n) glob_realloc_n(ptr, sizeof(*(ptr)), n)
1644 #define GLOB_FREE(ptr) free(ptr)
1645 #define GLOB_JUMP_TAG(status) (((status) == -1) ? rb_memerror() : rb_jump_tag(status))
1648 * ENOTDIR can be returned by stat(2) if a non-leaf element of the path
1649 * is not a directory.
1651 ALWAYS_INLINE(static int to_be_ignored(int e));
1652 static inline int
1653 to_be_ignored(int e)
1655 return e == ENOENT || e == ENOTDIR;
1658 #ifdef _WIN32
1659 #define STAT(p, s) rb_w32_ustati128((p), (s))
1660 #undef lstat
1661 #define lstat(p, s) rb_w32_ulstati128((p), (s))
1662 #else
1663 #define STAT(p, s) stat((p), (s))
1664 #endif
1666 typedef int ruby_glob_errfunc(const char*, VALUE, const void*, int);
1667 typedef struct {
1668 ruby_glob_func *match;
1669 ruby_glob_errfunc *error;
1670 } ruby_glob_funcs_t;
1672 static const char *
1673 at_subpath(int fd, size_t baselen, const char *path)
1675 #if USE_OPENDIR_AT
1676 if (fd != (int)AT_FDCWD && baselen > 0) {
1677 path += baselen;
1678 if (*path == '/') ++path;
1680 #endif
1681 return *path ? path : ".";
1684 /* System call with warning */
1685 static int
1686 do_stat(int fd, size_t baselen, const char *path, struct stat *pst, int flags, rb_encoding *enc)
1688 #if USE_OPENDIR_AT
1689 int ret = fstatat(fd, at_subpath(fd, baselen, path), pst, 0);
1690 #else
1691 int ret = STAT(path, pst);
1692 #endif
1693 if (ret < 0 && !to_be_ignored(errno))
1694 sys_warning(path, enc);
1696 return ret;
1699 #if defined HAVE_LSTAT || defined lstat || USE_OPENDIR_AT
1700 static int
1701 do_lstat(int fd, size_t baselen, const char *path, struct stat *pst, int flags, rb_encoding *enc)
1703 #if USE_OPENDIR_AT
1704 int ret = fstatat(fd, at_subpath(fd, baselen, path), pst, AT_SYMLINK_NOFOLLOW);
1705 #else
1706 int ret = lstat(path, pst);
1707 #endif
1708 if (ret < 0 && !to_be_ignored(errno))
1709 sys_warning(path, enc);
1711 return ret;
1713 #else
1714 #define do_lstat do_stat
1715 #endif
1717 struct opendir_at_arg {
1718 int basefd;
1719 const char *path;
1722 static void *
1723 with_gvl_gc_for_fd(void *ptr)
1725 int *e = ptr;
1727 return (void *)RBOOL(rb_gc_for_fd(*e));
1730 static int
1731 gc_for_fd_with_gvl(int e)
1733 if (vm_initialized)
1734 return (int)(VALUE)rb_thread_call_with_gvl(with_gvl_gc_for_fd, &e);
1735 else
1736 return RBOOL(rb_gc_for_fd(e));
1739 static void *
1740 nogvl_opendir_at(void *ptr)
1742 const struct opendir_at_arg *oaa = ptr;
1743 DIR *dirp;
1745 #if USE_OPENDIR_AT
1746 const int opendir_flags = (O_RDONLY|O_CLOEXEC|
1747 # ifdef O_DIRECTORY
1748 O_DIRECTORY|
1749 # endif /* O_DIRECTORY */
1751 int fd = openat(oaa->basefd, oaa->path, opendir_flags);
1753 dirp = fd >= 0 ? fdopendir(fd) : 0;
1754 if (!dirp) {
1755 int e = errno;
1757 switch (gc_for_fd_with_gvl(e)) {
1758 default:
1759 if (fd < 0) fd = openat(oaa->basefd, oaa->path, opendir_flags);
1760 if (fd >= 0) dirp = fdopendir(fd);
1761 if (dirp) return dirp;
1763 e = errno;
1764 /* fallthrough*/
1765 case 0:
1766 if (fd >= 0) close(fd);
1767 rb_errno_set(e);
1770 #else /* !USE_OPENDIR_AT */
1771 dirp = opendir(oaa->path);
1772 if (!dirp && gc_for_fd_with_gvl(errno))
1773 dirp = opendir(oaa->path);
1774 #endif /* !USE_OPENDIR_AT */
1776 return dirp;
1779 static DIR *
1780 opendir_at(int basefd, const char *path)
1782 struct opendir_at_arg oaa;
1784 oaa.basefd = basefd;
1785 oaa.path = path;
1787 if (vm_initialized)
1788 return IO_WITHOUT_GVL(nogvl_opendir_at, &oaa);
1789 else
1790 return nogvl_opendir_at(&oaa);
1793 static DIR *
1794 do_opendir(const int basefd, size_t baselen, const char *path, int flags, rb_encoding *enc,
1795 ruby_glob_errfunc *errfunc, VALUE arg, int *status)
1797 DIR *dirp;
1798 #ifdef _WIN32
1799 VALUE tmp = 0;
1800 if (!fundamental_encoding_p(enc)) {
1801 tmp = rb_enc_str_new(path, strlen(path), enc);
1802 tmp = rb_str_encode_ospath(tmp);
1803 path = RSTRING_PTR(tmp);
1805 #endif
1806 dirp = opendir_at(basefd, at_subpath(basefd, baselen, path));
1807 if (!dirp) {
1808 int e = errno;
1810 *status = 0;
1811 if (!to_be_ignored(e)) {
1812 if (errfunc) {
1813 *status = (*errfunc)(path, arg, enc, e);
1815 else {
1816 sys_warning(path, enc);
1820 #ifdef _WIN32
1821 if (tmp) rb_str_resize(tmp, 0); /* GC guard */
1822 #endif
1824 return dirp;
1827 /* Globing pattern */
1828 enum glob_pattern_type { PLAIN, ALPHA, BRACE, MAGICAL, RECURSIVE, MATCH_ALL, MATCH_DIR };
1830 /* Return nonzero if S has any special globbing chars in it. */
1831 static enum glob_pattern_type
1832 has_magic(const char *p, const char *pend, int flags, rb_encoding *enc)
1834 const int escape = !(flags & FNM_NOESCAPE);
1835 int hasalpha = 0;
1836 int hasmagical = 0;
1838 register char c;
1840 while (p < pend && (c = *p++) != 0) {
1841 switch (c) {
1842 case '{':
1843 return BRACE;
1845 case '*':
1846 case '?':
1847 case '[':
1848 hasmagical = 1;
1849 break;
1851 case '\\':
1852 if (escape && p++ >= pend)
1853 continue;
1854 break;
1856 #ifdef _WIN32
1857 case '.':
1858 break;
1860 case '~':
1861 hasalpha = 1;
1862 break;
1863 #endif
1864 default:
1865 if (IS_WIN32 || ISALPHA(c)) {
1866 hasalpha = 1;
1868 break;
1871 p = Next(p-1, pend, enc);
1874 return hasmagical ? MAGICAL : hasalpha ? ALPHA : PLAIN;
1877 /* Find separator in globbing pattern. */
1878 static char *
1879 find_dirsep(const char *p, const char *pend, int flags, rb_encoding *enc)
1881 const int escape = !(flags & FNM_NOESCAPE);
1883 register char c;
1884 int open = 0;
1886 while ((c = *p++) != 0) {
1887 switch (c) {
1888 case '[':
1889 open = 1;
1890 continue;
1891 case ']':
1892 open = 0;
1893 continue;
1895 case '{':
1896 open = 1;
1897 continue;
1898 case '}':
1899 open = 0;
1900 continue;
1902 case '/':
1903 if (!open)
1904 return (char *)p-1;
1905 continue;
1907 case '\\':
1908 if (escape && !(c = *p++))
1909 return (char *)p-1;
1910 continue;
1913 p = Next(p-1, pend, enc);
1916 return (char *)p-1;
1919 /* Remove escaping backslashes */
1920 static char *
1921 remove_backslashes(char *p, register const char *pend, rb_encoding *enc)
1923 char *t = p;
1924 char *s = p;
1926 while (*p) {
1927 if (*p == '\\') {
1928 if (t != s)
1929 memmove(t, s, p - s);
1930 t += p - s;
1931 s = ++p;
1932 if (!*p) break;
1934 Inc(p, pend, enc);
1937 while (*p++);
1939 if (t != s)
1940 memmove(t, s, p - s); /* move '\0' too */
1942 return p;
1945 struct glob_pattern {
1946 char *str;
1947 enum glob_pattern_type type;
1948 struct glob_pattern *next;
1951 static void glob_free_pattern(struct glob_pattern *list);
1953 static struct glob_pattern *
1954 glob_make_pattern(const char *p, const char *e, int flags, rb_encoding *enc)
1956 struct glob_pattern *list, *tmp, **tail = &list;
1957 int dirsep = 0; /* pattern is terminated with '/' */
1958 int recursive = 0;
1960 while (p < e && *p) {
1961 tmp = GLOB_ALLOC(struct glob_pattern);
1962 if (!tmp) goto error;
1963 if (p + 2 < e && p[0] == '*' && p[1] == '*' && p[2] == '/') {
1964 /* fold continuous RECURSIVEs (needed in glob_helper) */
1965 do { p += 3; while (*p == '/') p++; } while (p[0] == '*' && p[1] == '*' && p[2] == '/');
1966 tmp->type = RECURSIVE;
1967 tmp->str = 0;
1968 dirsep = 1;
1969 recursive = 1;
1971 else {
1972 const char *m = find_dirsep(p, e, flags, enc);
1973 const enum glob_pattern_type magic = has_magic(p, m, flags, enc);
1974 const enum glob_pattern_type non_magic = (USE_NAME_ON_FS || FNM_SYSCASE) ? PLAIN : ALPHA;
1975 char *buf;
1977 if (!(FNM_SYSCASE || magic > non_magic) && !recursive && *m) {
1978 const char *m2;
1979 while (has_magic(m+1, m2 = find_dirsep(m+1, e, flags, enc), flags, enc) <= non_magic &&
1980 *m2) {
1981 m = m2;
1984 buf = GLOB_ALLOC_N(char, m-p+1);
1985 if (!buf) {
1986 GLOB_FREE(tmp);
1987 goto error;
1989 memcpy(buf, p, m-p);
1990 buf[m-p] = '\0';
1991 tmp->type = magic > MAGICAL ? MAGICAL : magic > non_magic ? magic : PLAIN;
1992 tmp->str = buf;
1993 if (*m) {
1994 dirsep = 1;
1995 p = m + 1;
1997 else {
1998 dirsep = 0;
1999 p = m;
2002 *tail = tmp;
2003 tail = &tmp->next;
2006 tmp = GLOB_ALLOC(struct glob_pattern);
2007 if (!tmp) {
2008 goto error;
2010 tmp->type = dirsep ? MATCH_DIR : MATCH_ALL;
2011 tmp->str = 0;
2012 *tail = tmp;
2013 tmp->next = 0;
2015 return list;
2017 error:
2018 *tail = 0;
2019 glob_free_pattern(list);
2020 return 0;
2023 static void
2024 glob_free_pattern(struct glob_pattern *list)
2026 while (list) {
2027 struct glob_pattern *tmp = list;
2028 list = list->next;
2029 if (tmp->str)
2030 GLOB_FREE(tmp->str);
2031 GLOB_FREE(tmp);
2035 static char *
2036 join_path(const char *path, size_t len, int dirsep, const char *name, size_t namlen)
2038 char *buf = GLOB_ALLOC_N(char, len+namlen+(dirsep?1:0)+1);
2040 if (!buf) return 0;
2041 memcpy(buf, path, len);
2042 if (dirsep) {
2043 buf[len++] = '/';
2045 memcpy(buf+len, name, namlen);
2046 buf[len+namlen] = '\0';
2047 return buf;
2050 #ifdef HAVE_GETATTRLIST
2051 # if defined HAVE_FGETATTRLIST
2052 # define is_case_sensitive(dirp, path) is_case_sensitive(dirp)
2053 # else
2054 # define is_case_sensitive(dirp, path) is_case_sensitive(path)
2055 # endif
2056 static int
2057 is_case_sensitive(DIR *dirp, const char *path)
2059 struct {
2060 u_int32_t length;
2061 vol_capabilities_attr_t cap[1];
2062 } __attribute__((aligned(4), packed)) attrbuf[1];
2063 struct attrlist al = {ATTR_BIT_MAP_COUNT, 0, 0, ATTR_VOL_INFO|ATTR_VOL_CAPABILITIES};
2064 const vol_capabilities_attr_t *const cap = attrbuf[0].cap;
2065 const int idx = VOL_CAPABILITIES_FORMAT;
2066 const uint32_t mask = VOL_CAP_FMT_CASE_SENSITIVE;
2068 # if defined HAVE_FGETATTRLIST
2069 if (fgetattrlist(dirfd(dirp), &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW))
2070 return -1;
2071 # else
2072 if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW))
2073 return -1;
2074 # endif
2075 if (!(cap->valid[idx] & mask))
2076 return -1;
2077 return (cap->capabilities[idx] & mask) != 0;
2080 static char *
2081 replace_real_basename(char *path, long base, rb_encoding *enc, int norm_p, int flags, rb_pathtype_t *type)
2083 struct {
2084 u_int32_t length;
2085 attrreference_t ref[1];
2086 fsobj_type_t objtype;
2087 char path[MAXPATHLEN * 3];
2088 } __attribute__((aligned(4), packed)) attrbuf[1];
2089 struct attrlist al = {ATTR_BIT_MAP_COUNT, 0, ATTR_CMN_NAME|ATTR_CMN_OBJTYPE};
2090 const attrreference_t *const ar = attrbuf[0].ref;
2091 const char *name;
2092 long len;
2093 char *tmp;
2094 IF_NORMALIZE_UTF8PATH(VALUE utf8str = Qnil);
2096 *type = path_noent;
2097 if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW)) {
2098 if (!to_be_ignored(errno))
2099 sys_warning(path, enc);
2100 return path;
2103 switch (attrbuf[0].objtype) {
2104 case VREG: *type = path_regular; break;
2105 case VDIR: *type = path_directory; break;
2106 case VLNK: *type = path_symlink; break;
2107 default: *type = path_exist; break;
2109 name = (char *)ar + ar->attr_dataoffset;
2110 len = (long)ar->attr_length - 1;
2111 if (name + len > (char *)attrbuf + sizeof(attrbuf))
2112 return path;
2114 # if NORMALIZE_UTF8PATH
2115 if (norm_p && has_nonascii(name, len)) {
2116 if (!NIL_P(utf8str = rb_str_normalize_ospath(name, len))) {
2117 RSTRING_GETMEM(utf8str, name, len);
2120 # endif
2122 tmp = GLOB_REALLOC(path, base + len + 1);
2123 if (tmp) {
2124 path = tmp;
2125 memcpy(path + base, name, len);
2126 path[base + len] = '\0';
2128 IF_NORMALIZE_UTF8PATH(if (!NIL_P(utf8str)) rb_str_resize(utf8str, 0));
2129 return path;
2131 #elif defined _WIN32
2132 VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc);
2133 int rb_w32_reparse_symlink_p(const WCHAR *path);
2135 static char *
2136 replace_real_basename(char *path, long base, rb_encoding *enc, int norm_p, int flags, rb_pathtype_t *type)
2138 char *plainname = path;
2139 volatile VALUE tmp = 0;
2140 WIN32_FIND_DATAW fd;
2141 WIN32_FILE_ATTRIBUTE_DATA fa;
2142 WCHAR *wplain;
2143 HANDLE h = INVALID_HANDLE_VALUE;
2144 long wlen;
2145 int e = 0;
2146 if (!fundamental_encoding_p(enc)) {
2147 tmp = rb_enc_str_new_cstr(plainname, enc);
2148 tmp = rb_str_encode_ospath(tmp);
2149 plainname = RSTRING_PTR(tmp);
2151 wplain = rb_w32_mbstr_to_wstr(CP_UTF8, plainname, -1, &wlen);
2152 if (tmp) rb_str_resize(tmp, 0);
2153 if (!wplain) return path;
2154 if (GetFileAttributesExW(wplain, GetFileExInfoStandard, &fa)) {
2155 h = FindFirstFileW(wplain, &fd);
2156 e = rb_w32_map_errno(GetLastError());
2158 if (fa.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
2159 if (!rb_w32_reparse_symlink_p(wplain))
2160 fa.dwFileAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT;
2162 free(wplain);
2163 if (h == INVALID_HANDLE_VALUE) {
2164 *type = path_noent;
2165 if (e && !to_be_ignored(e)) {
2166 errno = e;
2167 sys_warning(path, enc);
2169 return path;
2171 FindClose(h);
2172 *type =
2173 (fa.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ? path_symlink :
2174 (fa.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? path_directory :
2175 path_regular;
2176 if (tmp) {
2177 char *buf;
2178 tmp = rb_w32_conv_from_wchar(fd.cFileName, enc);
2179 wlen = RSTRING_LEN(tmp);
2180 buf = GLOB_REALLOC(path, base + wlen + 1);
2181 if (buf) {
2182 path = buf;
2183 memcpy(path + base, RSTRING_PTR(tmp), wlen);
2184 path[base + wlen] = 0;
2186 rb_str_resize(tmp, 0);
2188 else {
2189 char *utf8filename;
2190 wlen = WideCharToMultiByte(CP_UTF8, 0, fd.cFileName, -1, NULL, 0, NULL, NULL);
2191 utf8filename = GLOB_REALLOC(0, wlen);
2192 if (utf8filename) {
2193 char *buf;
2194 WideCharToMultiByte(CP_UTF8, 0, fd.cFileName, -1, utf8filename, wlen, NULL, NULL);
2195 buf = GLOB_REALLOC(path, base + wlen + 1);
2196 if (buf) {
2197 path = buf;
2198 memcpy(path + base, utf8filename, wlen);
2199 path[base + wlen] = 0;
2201 GLOB_FREE(utf8filename);
2204 return path;
2206 #elif USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME
2207 # error not implemented
2208 #endif
2210 #ifndef S_ISDIR
2211 # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
2212 #endif
2214 #ifndef S_ISLNK
2215 # ifndef S_IFLNK
2216 # define S_ISLNK(m) (0)
2217 # else
2218 # define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
2219 # endif
2220 #endif
2222 struct glob_args {
2223 void (*func)(const char *, VALUE, void *);
2224 const char *path;
2225 const char *base;
2226 size_t baselen;
2227 VALUE value;
2228 rb_encoding *enc;
2231 #define glob_call_func(func, path, arg, enc) (*(func))((path), (arg), (void *)(enc))
2233 static VALUE
2234 glob_func_caller(VALUE val)
2236 struct glob_args *args = (struct glob_args *)val;
2238 glob_call_func(args->func, args->path, args->value, args->enc);
2239 return Qnil;
2242 struct glob_error_args {
2243 const char *path;
2244 rb_encoding *enc;
2245 int error;
2248 static VALUE
2249 glob_func_warning(VALUE val)
2251 struct glob_error_args *arg = (struct glob_error_args *)val;
2252 rb_syserr_enc_warning(arg->error, arg->enc, "%s", arg->path);
2253 return Qnil;
2256 #if 0
2257 static int
2258 rb_glob_warning(const char *path, VALUE a, const void *enc, int error)
2260 int status;
2261 struct glob_error_args args;
2263 args.path = path;
2264 args.enc = enc;
2265 args.error = error;
2266 rb_protect(glob_func_warning, (VALUE)&args, &status);
2267 return status;
2269 #endif
2271 NORETURN(static VALUE glob_func_error(VALUE val));
2273 static VALUE
2274 glob_func_error(VALUE val)
2276 struct glob_error_args *arg = (struct glob_error_args *)val;
2277 VALUE path = rb_enc_str_new_cstr(arg->path, arg->enc);
2278 rb_syserr_fail_str(arg->error, path);
2279 UNREACHABLE_RETURN(Qnil);
2282 static int
2283 rb_glob_error(const char *path, VALUE a, const void *enc, int error)
2285 int status;
2286 struct glob_error_args args;
2287 VALUE (*errfunc)(VALUE) = glob_func_error;
2289 switch (error) {
2290 case EACCES:
2291 #ifdef ENOTCAPABLE
2292 case ENOTCAPABLE:
2293 #endif
2294 errfunc = glob_func_warning;
2296 args.path = path;
2297 args.enc = enc;
2298 args.error = error;
2299 rb_protect(errfunc, (VALUE)&args, &status);
2300 return status;
2303 typedef struct rb_dirent {
2304 long d_namlen;
2305 const char *d_name;
2306 #ifdef _WIN32
2307 const char *d_altname;
2308 #endif
2309 uint8_t d_type;
2310 } rb_dirent_t;
2312 static inline int
2313 dirent_match(const char *pat, rb_encoding *enc, const char *name, const rb_dirent_t *dp, int flags)
2315 if (fnmatch(pat, enc, name, flags) == 0) return 1;
2316 #ifdef _WIN32
2317 if (dp->d_altname && (flags & FNM_SHORTNAME)) {
2318 if (fnmatch(pat, enc, dp->d_altname, flags) == 0) return 1;
2320 #endif
2321 return 0;
2324 struct push_glob_args {
2325 int fd;
2326 const char *path;
2327 size_t baselen;
2328 size_t namelen;
2329 int dirsep; /* '/' should be placed before appending child entry's name to 'path'. */
2330 rb_pathtype_t pathtype; /* type of 'path' */
2331 int flags;
2332 const ruby_glob_funcs_t *funcs;
2333 VALUE arg;
2336 struct dirent_brace_args {
2337 const char *name;
2338 const rb_dirent_t *dp;
2339 int flags;
2342 static int
2343 dirent_match_brace(const char *pattern, VALUE val, void *enc)
2345 struct dirent_brace_args *arg = (struct dirent_brace_args *)val;
2347 return dirent_match(pattern, enc, arg->name, arg->dp, arg->flags);
2350 /* join paths from pattern list of glob_make_pattern() */
2351 static char*
2352 join_path_from_pattern(struct glob_pattern **beg)
2354 struct glob_pattern *p;
2355 char *path = NULL;
2356 size_t path_len = 0;
2358 for (p = *beg; p; p = p->next) {
2359 const char *str;
2360 switch (p->type) {
2361 case RECURSIVE:
2362 str = "**";
2363 break;
2364 case MATCH_DIR:
2365 /* append last slash */
2366 str = "";
2367 break;
2368 default:
2369 str = p->str;
2370 if (!str) continue;
2372 if (!path) {
2373 path_len = strlen(str);
2374 path = GLOB_ALLOC_N(char, path_len + 1);
2375 if (path) {
2376 memcpy(path, str, path_len);
2377 path[path_len] = '\0';
2380 else {
2381 size_t len = strlen(str);
2382 char *tmp;
2383 tmp = GLOB_REALLOC(path, path_len + len + 2);
2384 if (tmp) {
2385 path = tmp;
2386 path[path_len++] = '/';
2387 memcpy(path + path_len, str, len);
2388 path_len += len;
2389 path[path_len] = '\0';
2393 return path;
2396 static int push_caller(const char *path, VALUE val, void *enc);
2398 static int ruby_brace_expand(const char *str, int flags, ruby_glob_func *func, VALUE arg,
2399 rb_encoding *enc, VALUE var);
2401 static const size_t rb_dirent_name_offset =
2402 offsetof(rb_dirent_t, d_type) + sizeof(uint8_t);
2404 static rb_dirent_t *
2405 dirent_copy(const struct dirent *dp, rb_dirent_t *rdp)
2407 if (!dp) return NULL;
2408 size_t namlen = NAMLEN(dp);
2409 const size_t altlen =
2410 #ifdef _WIN32
2411 dp->d_altlen ? dp->d_altlen + 1 :
2412 #endif
2414 rb_dirent_t *newrdp = rdp;
2415 if (!rdp && !(newrdp = malloc(rb_dirent_name_offset + namlen + 1 + altlen)))
2416 return NULL;
2417 newrdp->d_namlen = namlen;
2418 if (!rdp) {
2419 char *name = (char *)newrdp + rb_dirent_name_offset;
2420 memcpy(name, dp->d_name, namlen);
2421 name[namlen] = '\0';
2422 #ifdef _WIN32
2423 newrdp->d_altname = NULL;
2424 if (altlen) {
2425 char *const altname = name + namlen + 1;
2426 memcpy(altname, dp->d_altname, altlen - 1);
2427 altname[altlen - 1] = '\0';
2428 newrdp->d_altname = altname;
2430 #endif
2431 newrdp->d_name = name;
2433 else {
2434 newrdp->d_name = dp->d_name;
2435 #ifdef _WIN32
2436 newrdp->d_altname = dp->d_altname;
2437 #endif
2439 #if !EMULATE_IFTODT
2440 newrdp->d_type = dp->d_type;
2441 #else
2442 newrdp->d_type = 0;
2443 #endif
2444 return newrdp;
2447 typedef union {
2448 struct {
2449 DIR *dirp;
2450 rb_dirent_t ent;
2451 } nosort;
2452 struct {
2453 size_t count, idx;
2454 rb_dirent_t **entries;
2455 } sort;
2456 } ruby_glob_entries_t;
2458 static int
2459 glob_sort_cmp(const void *a, const void *b, void *e)
2461 const rb_dirent_t *ent1 = *(void **)a;
2462 const rb_dirent_t *ent2 = *(void **)b;
2463 return strcmp(ent1->d_name, ent2->d_name);
2466 static void
2467 glob_dir_finish(ruby_glob_entries_t *ent, int flags)
2469 if (flags & FNM_GLOB_NOSORT) {
2470 closedir(ent->nosort.dirp);
2471 ent->nosort.dirp = NULL;
2473 else if (ent->sort.entries) {
2474 for (size_t i = 0, count = ent->sort.count; i < count;) {
2475 GLOB_FREE(ent->sort.entries[i++]);
2477 GLOB_FREE(ent->sort.entries);
2478 ent->sort.entries = NULL;
2479 ent->sort.count = ent->sort.idx = 0;
2483 static ruby_glob_entries_t *
2484 glob_opendir(ruby_glob_entries_t *ent, DIR *dirp, int flags, rb_encoding *enc)
2486 MEMZERO(ent, ruby_glob_entries_t, 1);
2487 if (flags & FNM_GLOB_NOSORT) {
2488 ent->nosort.dirp = dirp;
2489 return ent;
2491 else {
2492 void *newp;
2493 struct dirent *dp;
2494 size_t count = 0, capacity = 0;
2495 ent->sort.count = 0;
2496 ent->sort.idx = 0;
2497 ent->sort.entries = 0;
2498 #ifdef _WIN32
2499 if ((capacity = dirp->nfiles) > 0) {
2500 if (!(newp = GLOB_ALLOC_N(rb_dirent_t, capacity))) {
2501 closedir(dirp);
2502 return NULL;
2504 ent->sort.entries = newp;
2506 #endif
2507 while ((dp = READDIR(dirp, enc)) != NULL) {
2508 rb_dirent_t *rdp = dirent_copy(dp, NULL);
2509 if (!rdp) {
2510 goto nomem;
2512 if (count >= capacity) {
2513 capacity += 256;
2514 if (!(newp = GLOB_REALLOC_N(ent->sort.entries, capacity)))
2515 goto nomem;
2516 ent->sort.entries = newp;
2518 ent->sort.entries[count++] = rdp;
2519 ent->sort.count = count;
2521 closedir(dirp);
2522 if (count < capacity) {
2523 if (!(newp = GLOB_REALLOC_N(ent->sort.entries, count))) {
2524 glob_dir_finish(ent, 0);
2525 return NULL;
2527 ent->sort.entries = newp;
2529 ruby_qsort(ent->sort.entries, ent->sort.count, sizeof(ent->sort.entries[0]),
2530 glob_sort_cmp, NULL);
2531 return ent;
2534 nomem:
2535 glob_dir_finish(ent, 0);
2536 closedir(dirp);
2537 return NULL;
2540 static rb_dirent_t *
2541 glob_getent(ruby_glob_entries_t *ent, int flags, rb_encoding *enc)
2543 if (flags & FNM_GLOB_NOSORT) {
2544 return dirent_copy(READDIR(ent->nosort.dirp, enc), &ent->nosort.ent);
2546 else if (ent->sort.idx < ent->sort.count) {
2547 return ent->sort.entries[ent->sort.idx++];
2549 else {
2550 return NULL;
2554 static int
2555 glob_helper(
2556 int fd,
2557 const char *path,
2558 size_t baselen,
2559 size_t namelen,
2560 int dirsep, /* '/' should be placed before appending child entry's name to 'path'. */
2561 rb_pathtype_t pathtype, /* type of 'path' */
2562 struct glob_pattern **beg,
2563 struct glob_pattern **end,
2564 int flags,
2565 const ruby_glob_funcs_t *funcs,
2566 VALUE arg,
2567 rb_encoding *enc)
2569 struct stat st;
2570 int status = 0;
2571 struct glob_pattern **cur, **new_beg, **new_end;
2572 int plain = 0, brace = 0, magical = 0, recursive = 0, match_all = 0, match_dir = 0;
2573 int escape = !(flags & FNM_NOESCAPE);
2574 size_t pathlen = baselen + namelen;
2576 rb_check_stack_overflow();
2578 for (cur = beg; cur < end; ++cur) {
2579 struct glob_pattern *p = *cur;
2580 if (p->type == RECURSIVE) {
2581 recursive = 1;
2582 p = p->next;
2584 switch (p->type) {
2585 case PLAIN:
2586 plain = 1;
2587 break;
2588 case ALPHA:
2589 #if USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME
2590 plain = 1;
2591 #else
2592 magical = 1;
2593 #endif
2594 break;
2595 case BRACE:
2596 if (!recursive || strchr(p->str, '/')) {
2597 brace = 1;
2599 break;
2600 case MAGICAL:
2601 magical = 2;
2602 break;
2603 case MATCH_ALL:
2604 match_all = 1;
2605 break;
2606 case MATCH_DIR:
2607 match_dir = 1;
2608 break;
2609 case RECURSIVE:
2610 rb_bug("continuous RECURSIVEs");
2614 if (brace) {
2615 struct push_glob_args args;
2616 char* brace_path = join_path_from_pattern(beg);
2617 if (!brace_path) return -1;
2618 args.fd = fd;
2619 args.path = path;
2620 args.baselen = baselen;
2621 args.namelen = namelen;
2622 args.dirsep = dirsep;
2623 args.pathtype = pathtype;
2624 args.flags = flags;
2625 args.funcs = funcs;
2626 args.arg = arg;
2627 status = ruby_brace_expand(brace_path, flags, push_caller, (VALUE)&args, enc, Qfalse);
2628 GLOB_FREE(brace_path);
2629 return status;
2632 if (*path) {
2633 if (match_all && pathtype == path_unknown) {
2634 if (do_lstat(fd, baselen, path, &st, flags, enc) == 0) {
2635 pathtype = IFTODT(st.st_mode);
2637 else {
2638 pathtype = path_noent;
2641 if (match_dir && (pathtype == path_unknown || pathtype == path_symlink)) {
2642 if (do_stat(fd, baselen, path, &st, flags, enc) == 0) {
2643 pathtype = IFTODT(st.st_mode);
2645 else {
2646 pathtype = path_noent;
2649 if (match_all && pathtype > path_noent) {
2650 const char *subpath = path + baselen + (baselen && path[baselen] == '/');
2651 status = glob_call_func(funcs->match, subpath, arg, enc);
2652 if (status) return status;
2654 if (match_dir && pathtype == path_directory) {
2655 int seplen = (baselen && path[baselen] == '/');
2656 const char *subpath = path + baselen + seplen;
2657 char *tmp = join_path(subpath, namelen - seplen, dirsep, "", 0);
2658 if (!tmp) return -1;
2659 status = glob_call_func(funcs->match, tmp, arg, enc);
2660 GLOB_FREE(tmp);
2661 if (status) return status;
2665 if (pathtype == path_noent) return 0;
2667 if (magical || recursive) {
2668 rb_dirent_t *dp;
2669 DIR *dirp;
2670 # if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH
2671 char *plainname = 0;
2672 # endif
2673 IF_NORMALIZE_UTF8PATH(int norm_p);
2674 # if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH
2675 if (cur + 1 == end && (*cur)->type <= ALPHA) {
2676 plainname = join_path(path, pathlen, dirsep, (*cur)->str, strlen((*cur)->str));
2677 if (!plainname) return -1;
2678 dirp = do_opendir(fd, basename, plainname, flags, enc, funcs->error, arg, &status);
2679 GLOB_FREE(plainname);
2681 else
2682 # else
2684 # endif
2685 dirp = do_opendir(fd, baselen, path, flags, enc, funcs->error, arg, &status);
2686 if (dirp == NULL) {
2687 # if FNM_SYSCASE || NORMALIZE_UTF8PATH
2688 if ((magical < 2) && !recursive && (errno == EACCES)) {
2689 /* no read permission, fallback */
2690 goto literally;
2692 # endif
2693 return status;
2695 IF_NORMALIZE_UTF8PATH(norm_p = need_normalization(dirp, *path ? path : "."));
2697 # if NORMALIZE_UTF8PATH
2698 if (!(norm_p || magical || recursive)) {
2699 closedir(dirp);
2700 goto literally;
2702 # endif
2703 # ifdef HAVE_GETATTRLIST
2704 if (is_case_sensitive(dirp, path) == 0)
2705 flags |= FNM_CASEFOLD;
2706 # endif
2707 ruby_glob_entries_t globent;
2708 if (!glob_opendir(&globent, dirp, flags, enc)) {
2709 status = 0;
2710 if (funcs->error) {
2711 status = (*funcs->error)(path, arg, enc, ENOMEM);
2713 else {
2714 sys_warning(path, enc);
2716 return status;
2719 int skipdot = (flags & FNM_GLOB_SKIPDOT);
2720 flags |= FNM_GLOB_SKIPDOT;
2722 while ((dp = glob_getent(&globent, flags, enc)) != NULL) {
2723 char *buf;
2724 rb_pathtype_t new_pathtype = path_unknown;
2725 const char *name;
2726 size_t namlen;
2727 int dotfile = 0;
2728 IF_NORMALIZE_UTF8PATH(VALUE utf8str = Qnil);
2730 name = dp->d_name;
2731 namlen = dp->d_namlen;
2732 if (name[0] == '.') {
2733 ++dotfile;
2734 if (namlen == 1) {
2735 /* unless DOTMATCH, skip current directories not to recurse infinitely */
2736 if (recursive && !(flags & FNM_DOTMATCH)) continue;
2737 if (skipdot) continue;
2738 ++dotfile;
2739 new_pathtype = path_directory; /* force to skip stat/lstat */
2741 else if (namlen == 2 && name[1] == '.') {
2742 /* always skip parent directories not to recurse infinitely */
2743 continue;
2747 # if NORMALIZE_UTF8PATH
2748 if (norm_p && has_nonascii(name, namlen)) {
2749 if (!NIL_P(utf8str = rb_str_normalize_ospath(name, namlen))) {
2750 RSTRING_GETMEM(utf8str, name, namlen);
2753 # endif
2754 buf = join_path(path, pathlen, dirsep, name, namlen);
2755 IF_NORMALIZE_UTF8PATH(if (!NIL_P(utf8str)) rb_str_resize(utf8str, 0));
2756 if (!buf) {
2757 status = -1;
2758 break;
2760 name = buf + pathlen + (dirsep != 0);
2761 #if !EMULATE_IFTODT
2762 if (dp->d_type != DT_UNKNOWN) {
2763 /* Got it. We need no more lstat. */
2764 new_pathtype = dp->d_type;
2766 #endif
2767 if (recursive && dotfile < ((flags & FNM_DOTMATCH) ? 2 : 1) &&
2768 new_pathtype == path_unknown) {
2769 /* RECURSIVE never match dot files unless FNM_DOTMATCH is set */
2770 if (do_lstat(fd, baselen, buf, &st, flags, enc) == 0)
2771 new_pathtype = IFTODT(st.st_mode);
2772 else
2773 new_pathtype = path_noent;
2776 new_beg = new_end = GLOB_ALLOC_N(struct glob_pattern *, (end - beg) * 2);
2777 if (!new_beg) {
2778 GLOB_FREE(buf);
2779 status = -1;
2780 break;
2783 for (cur = beg; cur < end; ++cur) {
2784 struct glob_pattern *p = *cur;
2785 struct dirent_brace_args args;
2786 if (p->type == RECURSIVE) {
2787 if (new_pathtype == path_directory || /* not symlink but real directory */
2788 new_pathtype == path_exist) {
2789 if (dotfile < ((flags & FNM_DOTMATCH) ? 2 : 1))
2790 *new_end++ = p; /* append recursive pattern */
2792 p = p->next; /* 0 times recursion */
2794 switch (p->type) {
2795 case BRACE:
2796 args.name = name;
2797 args.dp = dp;
2798 args.flags = flags;
2799 if (ruby_brace_expand(p->str, flags, dirent_match_brace,
2800 (VALUE)&args, enc, Qfalse) > 0)
2801 *new_end++ = p->next;
2802 break;
2803 case ALPHA:
2804 # if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH
2805 if (plainname) {
2806 *new_end++ = p->next;
2807 break;
2809 # endif
2810 case PLAIN:
2811 case MAGICAL:
2812 if (dirent_match(p->str, enc, name, dp, flags))
2813 *new_end++ = p->next;
2814 default:
2815 break;
2819 status = glob_helper(fd, buf, baselen, name - buf - baselen + namlen, 1,
2820 new_pathtype, new_beg, new_end,
2821 flags, funcs, arg, enc);
2822 GLOB_FREE(buf);
2823 GLOB_FREE(new_beg);
2824 if (status) break;
2827 glob_dir_finish(&globent, flags);
2829 else if (plain) {
2830 struct glob_pattern **copy_beg, **copy_end, **cur2;
2832 # if FNM_SYSCASE || NORMALIZE_UTF8PATH
2833 literally:
2834 # endif
2835 copy_beg = copy_end = GLOB_ALLOC_N(struct glob_pattern *, end - beg);
2836 if (!copy_beg) return -1;
2837 for (cur = beg; cur < end; ++cur)
2838 *copy_end++ = (*cur)->type <= ALPHA ? *cur : 0;
2840 for (cur = copy_beg; cur < copy_end; ++cur) {
2841 if (*cur) {
2842 rb_pathtype_t new_pathtype = path_unknown;
2843 char *buf;
2844 char *name;
2845 size_t len = strlen((*cur)->str) + 1;
2846 name = GLOB_ALLOC_N(char, len);
2847 if (!name) {
2848 status = -1;
2849 break;
2851 memcpy(name, (*cur)->str, len);
2852 if (escape)
2853 len = remove_backslashes(name, name+len-1, enc) - name;
2855 new_beg = new_end = GLOB_ALLOC_N(struct glob_pattern *, end - beg);
2856 if (!new_beg) {
2857 GLOB_FREE(name);
2858 status = -1;
2859 break;
2861 *new_end++ = (*cur)->next;
2862 for (cur2 = cur + 1; cur2 < copy_end; ++cur2) {
2863 if (*cur2 && fnmatch((*cur2)->str, enc, name, flags) == 0) {
2864 *new_end++ = (*cur2)->next;
2865 *cur2 = 0;
2869 buf = join_path(path, pathlen, dirsep, name, len);
2870 GLOB_FREE(name);
2871 if (!buf) {
2872 GLOB_FREE(new_beg);
2873 status = -1;
2874 break;
2876 #if USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME
2877 if ((*cur)->type == ALPHA) {
2878 buf = replace_real_basename(buf, pathlen + (dirsep != 0), enc,
2879 IF_NORMALIZE_UTF8PATH(1)+0,
2880 flags, &new_pathtype);
2881 if (!buf) break;
2883 #endif
2884 status = glob_helper(fd, buf, baselen,
2885 namelen + strlen(buf + pathlen), 1,
2886 new_pathtype, new_beg, new_end,
2887 flags, funcs, arg, enc);
2888 GLOB_FREE(buf);
2889 GLOB_FREE(new_beg);
2890 if (status) break;
2894 GLOB_FREE(copy_beg);
2897 return status;
2900 static int
2901 push_caller(const char *path, VALUE val, void *enc)
2903 struct push_glob_args *arg = (struct push_glob_args *)val;
2904 struct glob_pattern *list;
2905 int status;
2907 list = glob_make_pattern(path, path + strlen(path), arg->flags, enc);
2908 if (!list) {
2909 return -1;
2911 status = glob_helper(arg->fd, arg->path, arg->baselen, arg->namelen, arg->dirsep,
2912 arg->pathtype, &list, &list + 1, arg->flags, arg->funcs,
2913 arg->arg, enc);
2914 glob_free_pattern(list);
2915 return status;
2918 static int ruby_glob0(const char *path, int fd, const char *base, int flags,
2919 const ruby_glob_funcs_t *funcs, VALUE arg, rb_encoding *enc);
2921 struct push_glob0_args {
2922 int fd;
2923 const char *base;
2924 int flags;
2925 const ruby_glob_funcs_t *funcs;
2926 VALUE arg;
2929 static int
2930 push_glob0_caller(const char *path, VALUE val, void *enc)
2932 struct push_glob0_args *arg = (struct push_glob0_args *)val;
2933 return ruby_glob0(path, arg->fd, arg->base, arg->flags, arg->funcs, arg->arg, enc);
2936 static int
2937 ruby_glob0(const char *path, int fd, const char *base, int flags,
2938 const ruby_glob_funcs_t *funcs, VALUE arg,
2939 rb_encoding *enc)
2941 struct glob_pattern *list;
2942 const char *root, *start;
2943 char *buf;
2944 size_t n, baselen = 0;
2945 int status, dirsep = FALSE;
2947 start = root = path;
2949 if (*root == '{') {
2950 struct push_glob0_args args;
2951 args.fd = fd;
2952 args.base = base;
2953 args.flags = flags;
2954 args.funcs = funcs;
2955 args.arg = arg;
2956 return ruby_brace_expand(path, flags, push_glob0_caller, (VALUE)&args, enc, Qfalse);
2959 flags |= FNM_SYSCASE;
2960 #if defined DOSISH
2961 root = rb_enc_path_skip_prefix(root, root + strlen(root), enc);
2962 #endif
2964 if (*root == '/') root++;
2966 n = root - start;
2967 if (!n && base) {
2968 n = strlen(base);
2969 baselen = n;
2970 start = base;
2971 dirsep = TRUE;
2973 buf = GLOB_ALLOC_N(char, n + 1);
2974 if (!buf) return -1;
2975 MEMCPY(buf, start, char, n);
2976 buf[n] = '\0';
2978 list = glob_make_pattern(root, root + strlen(root), flags, enc);
2979 if (!list) {
2980 GLOB_FREE(buf);
2981 return -1;
2983 status = glob_helper(fd, buf, baselen, n-baselen, dirsep,
2984 path_unknown, &list, &list + 1,
2985 flags, funcs, arg, enc);
2986 glob_free_pattern(list);
2987 GLOB_FREE(buf);
2989 return status;
2993 ruby_glob(const char *path, int flags, ruby_glob_func *func, VALUE arg)
2995 ruby_glob_funcs_t funcs;
2996 funcs.match = func;
2997 funcs.error = 0;
2998 return ruby_glob0(path, AT_FDCWD, 0, flags & ~GLOB_VERBOSE,
2999 &funcs, arg, rb_ascii8bit_encoding());
3002 static int
3003 rb_glob_caller(const char *path, VALUE a, void *enc)
3005 int status;
3006 struct glob_args *args = (struct glob_args *)a;
3008 args->path = path;
3009 rb_protect(glob_func_caller, a, &status);
3010 return status;
3013 static const ruby_glob_funcs_t rb_glob_funcs = {
3014 rb_glob_caller, rb_glob_error,
3017 void
3018 rb_glob(const char *path, void (*func)(const char *, VALUE, void *), VALUE arg)
3020 struct glob_args args;
3021 int status;
3023 args.func = func;
3024 args.value = arg;
3025 args.enc = rb_ascii8bit_encoding();
3027 status = ruby_glob0(path, AT_FDCWD, 0, GLOB_VERBOSE, &rb_glob_funcs,
3028 (VALUE)&args, args.enc);
3029 if (status) GLOB_JUMP_TAG(status);
3032 static void
3033 push_pattern(const char *path, VALUE ary, void *enc)
3035 #if defined _WIN32 || defined __APPLE__
3036 VALUE name = rb_utf8_str_new_cstr(path);
3037 rb_encoding *eenc = rb_default_internal_encoding();
3038 name = rb_str_conv_enc(name, NULL, eenc ? eenc : enc);
3039 #else
3040 VALUE name = rb_external_str_new_with_enc(path, strlen(path), enc);
3041 #endif
3042 rb_ary_push(ary, name);
3045 static int
3046 ruby_brace_expand(const char *str, int flags, ruby_glob_func *func, VALUE arg,
3047 rb_encoding *enc, VALUE var)
3049 const int escape = !(flags & FNM_NOESCAPE);
3050 const char *p = str;
3051 const char *pend = p + strlen(p);
3052 const char *s = p;
3053 const char *lbrace = 0, *rbrace = 0;
3054 int nest = 0, status = 0;
3056 while (*p) {
3057 if (*p == '{' && nest++ == 0) {
3058 lbrace = p;
3060 if (*p == '}' && lbrace && --nest == 0) {
3061 rbrace = p;
3062 break;
3064 if (*p == '\\' && escape) {
3065 if (!*++p) break;
3067 Inc(p, pend, enc);
3070 if (lbrace && rbrace) {
3071 size_t len = strlen(s) + 1;
3072 char *buf = GLOB_ALLOC_N(char, len);
3073 long shift;
3075 if (!buf) return -1;
3076 memcpy(buf, s, lbrace-s);
3077 shift = (lbrace-s);
3078 p = lbrace;
3079 while (p < rbrace) {
3080 const char *t = ++p;
3081 nest = 0;
3082 while (p < rbrace && !(*p == ',' && nest == 0)) {
3083 if (*p == '{') nest++;
3084 if (*p == '}') nest--;
3085 if (*p == '\\' && escape) {
3086 if (++p == rbrace) break;
3088 Inc(p, pend, enc);
3090 memcpy(buf+shift, t, p-t);
3091 strlcpy(buf+shift+(p-t), rbrace+1, len-(shift+(p-t)));
3092 status = ruby_brace_expand(buf, flags, func, arg, enc, var);
3093 if (status) break;
3095 GLOB_FREE(buf);
3097 else if (!lbrace && !rbrace) {
3098 status = glob_call_func(func, s, arg, enc);
3101 RB_GC_GUARD(var);
3102 return status;
3105 struct brace_args {
3106 ruby_glob_funcs_t funcs;
3107 VALUE value;
3108 int flags;
3111 static int
3112 glob_brace(const char *path, VALUE val, void *enc)
3114 struct brace_args *arg = (struct brace_args *)val;
3116 return ruby_glob0(path, AT_FDCWD, 0, arg->flags, &arg->funcs, arg->value, enc);
3120 ruby_brace_glob_with_enc(const char *str, int flags, ruby_glob_func *func, VALUE arg, rb_encoding *enc)
3122 struct brace_args args;
3124 flags &= ~GLOB_VERBOSE;
3125 args.funcs.match = func;
3126 args.funcs.error = 0;
3127 args.value = arg;
3128 args.flags = flags;
3129 return ruby_brace_expand(str, flags, glob_brace, (VALUE)&args, enc, Qfalse);
3133 ruby_brace_glob(const char *str, int flags, ruby_glob_func *func, VALUE arg)
3135 return ruby_brace_glob_with_enc(str, flags, func, arg, rb_ascii8bit_encoding());
3138 static int
3139 push_glob(VALUE ary, VALUE str, VALUE base, int flags)
3141 struct glob_args args;
3142 int fd;
3143 rb_encoding *enc = rb_enc_get(str);
3145 #if defined _WIN32 || defined __APPLE__
3146 str = rb_str_encode_ospath(str);
3147 #endif
3148 if (rb_enc_to_index(enc) == ENCINDEX_US_ASCII)
3149 enc = rb_filesystem_encoding();
3150 if (rb_enc_to_index(enc) == ENCINDEX_US_ASCII)
3151 enc = rb_ascii8bit_encoding();
3152 flags |= GLOB_VERBOSE;
3153 args.func = push_pattern;
3154 args.value = ary;
3155 args.enc = enc;
3156 args.base = 0;
3157 fd = AT_FDCWD;
3158 if (!NIL_P(base)) {
3159 if (!RB_TYPE_P(base, T_STRING) || !rb_enc_check(str, base)) {
3160 struct dir_data *dirp = RTYPEDDATA_GET_DATA(base);
3161 if (!dirp->dir) dir_closed();
3162 #ifdef HAVE_DIRFD
3163 if ((fd = dirfd(dirp->dir)) == -1)
3164 rb_sys_fail_path(dir_inspect(base));
3165 #endif
3166 base = dirp->path;
3168 args.base = RSTRING_PTR(base);
3170 #if defined _WIN32 || defined __APPLE__
3171 enc = rb_utf8_encoding();
3172 #endif
3174 return ruby_glob0(RSTRING_PTR(str), fd, args.base, flags, &rb_glob_funcs,
3175 (VALUE)&args, enc);
3178 static VALUE
3179 rb_push_glob(VALUE str, VALUE base, int flags) /* '\0' is delimiter */
3181 VALUE ary;
3182 int status;
3184 /* can contain null bytes as separators */
3185 if (!RB_TYPE_P(str, T_STRING)) {
3186 FilePathValue(str);
3188 else if (!rb_str_to_cstr(str)) {
3189 rb_raise(rb_eArgError, "nul-separated glob pattern is deprecated");
3191 else {
3192 rb_enc_check(str, rb_enc_from_encoding(rb_usascii_encoding()));
3194 ary = rb_ary_new();
3196 status = push_glob(ary, str, base, flags);
3197 if (status) GLOB_JUMP_TAG(status);
3199 return ary;
3202 static VALUE
3203 dir_globs(VALUE args, VALUE base, int flags)
3205 VALUE ary = rb_ary_new();
3206 long i;
3208 for (i = 0; i < RARRAY_LEN(args); ++i) {
3209 int status;
3210 VALUE str = RARRAY_AREF(args, i);
3211 FilePathValue(str);
3212 status = push_glob(ary, str, base, flags);
3213 if (status) GLOB_JUMP_TAG(status);
3215 RB_GC_GUARD(args);
3217 return ary;
3220 static VALUE
3221 dir_glob_option_base(VALUE base)
3223 if (NIL_OR_UNDEF_P(base)) {
3224 return Qnil;
3226 #if USE_OPENDIR_AT
3227 if (rb_typeddata_is_kind_of(base, &dir_data_type)) {
3228 return base;
3230 #endif
3231 FilePathValue(base);
3232 if (!RSTRING_LEN(base)) return Qnil;
3233 return base;
3236 static int
3237 dir_glob_option_sort(VALUE sort)
3239 return (rb_bool_expected(sort, "sort", TRUE) ? 0 : FNM_GLOB_NOSORT);
3242 static VALUE
3243 dir_s_aref(rb_execution_context_t *ec, VALUE obj, VALUE args, VALUE base, VALUE sort)
3245 const int flags = dir_glob_option_sort(sort);
3246 base = dir_glob_option_base(base);
3247 if (RARRAY_LEN(args) == 1) {
3248 return rb_push_glob(RARRAY_AREF(args, 0), base, flags);
3250 return dir_globs(args, base, flags);
3253 static VALUE
3254 dir_s_glob(rb_execution_context_t *ec, VALUE obj, VALUE str, VALUE rflags, VALUE base, VALUE sort)
3256 VALUE ary = rb_check_array_type(str);
3257 const int flags = (NUM2INT(rflags) | dir_glob_option_sort(sort)) & ~FNM_CASEFOLD;
3258 base = dir_glob_option_base(base);
3259 if (NIL_P(ary)) {
3260 ary = rb_push_glob(str, base, flags);
3262 else {
3263 ary = dir_globs(ary, base, flags);
3266 if (rb_block_given_p()) {
3267 rb_ary_each(ary);
3268 return Qnil;
3270 return ary;
3273 static VALUE
3274 dir_open_dir(int argc, VALUE *argv)
3276 VALUE dir = rb_funcallv_kw(rb_cDir, rb_intern("open"), argc, argv, RB_PASS_CALLED_KEYWORDS);
3278 rb_check_typeddata(dir, &dir_data_type);
3279 return dir;
3284 * call-seq:
3285 * Dir.foreach(dirpath, encoding: 'UTF-8') {|entry_name| ... } -> nil
3287 * Calls the block with each entry name in the directory at +dirpath+;
3288 * sets the given encoding onto each passed +entry_name+:
3290 * Dir.foreach('/example') {|entry_name| p entry_name }
3292 * Output:
3294 * "config.h"
3295 * "lib"
3296 * "main.rb"
3297 * ".."
3298 * "."
3300 * Encoding:
3302 * Dir.foreach('/example') {|entry_name| p entry_name.encoding; break }
3303 * Dir.foreach('/example', encoding: 'US-ASCII') {|entry_name| p entry_name.encoding; break }
3305 * Output:
3307 * #<Encoding:UTF-8>
3308 * #<Encoding:US-ASCII>
3310 * See {String Encoding}[rdoc-ref:encodings.rdoc@String+Encoding].
3312 * Returns an enumerator if no block is given.
3314 static VALUE
3315 dir_foreach(int argc, VALUE *argv, VALUE io)
3317 VALUE dir;
3319 RETURN_ENUMERATOR(io, argc, argv);
3320 dir = dir_open_dir(argc, argv);
3321 rb_ensure(dir_each, dir, dir_close, dir);
3322 return Qnil;
3325 static VALUE
3326 dir_collect(VALUE dir)
3328 VALUE ary = rb_ary_new();
3329 dir_each_entry(dir, rb_ary_push, ary, FALSE);
3330 return ary;
3334 * call-seq:
3335 * Dir.entries(dirname, encoding: 'UTF-8') -> array
3337 * Returns an array of the entry names in the directory at +dirpath+;
3338 * sets the given encoding onto each returned entry name:
3340 * Dir.entries('/example') # => ["config.h", "lib", "main.rb", "..", "."]
3341 * Dir.entries('/example').first.encoding
3342 * # => #<Encoding:UTF-8>
3343 * Dir.entries('/example', encoding: 'US-ASCII').first.encoding
3344 * # => #<Encoding:US-ASCII>
3346 * See {String Encoding}[rdoc-ref:encodings.rdoc@String+Encoding].
3348 * Raises an exception if the directory does not exist.
3350 static VALUE
3351 dir_entries(int argc, VALUE *argv, VALUE io)
3353 VALUE dir;
3355 dir = dir_open_dir(argc, argv);
3356 return rb_ensure(dir_collect, dir, dir_close, dir);
3359 static VALUE
3360 dir_each_child(VALUE dir)
3362 return dir_each_entry(dir, dir_yield, Qnil, TRUE);
3366 * call-seq:
3367 * Dir.each_child(dirpath) {|entry_name| ... } -> nil
3368 * Dir.each_child(dirpath, encoding: 'UTF-8') {|entry_name| ... } -> nil
3370 * Like Dir.foreach, except that entries <tt>'.'</tt> and <tt>'..'</tt>
3371 * are not included.
3373 static VALUE
3374 dir_s_each_child(int argc, VALUE *argv, VALUE io)
3376 VALUE dir;
3378 RETURN_ENUMERATOR(io, argc, argv);
3379 dir = dir_open_dir(argc, argv);
3380 rb_ensure(dir_each_child, dir, dir_close, dir);
3381 return Qnil;
3385 * call-seq:
3386 * each_child {|entry_name| ... } -> self
3388 * Calls the block with each entry name in +self+
3389 * except <tt>'.'</tt> and <tt>'..'</tt>:
3391 * dir = Dir.new('/example')
3392 * dir.each_child {|entry_name| p entry_name }
3394 * Output:
3396 * "config.h"
3397 * "lib"
3398 * "main.rb"
3400 * If no block is given, returns an enumerator.
3402 static VALUE
3403 dir_each_child_m(VALUE dir)
3405 RETURN_ENUMERATOR(dir, 0, 0);
3406 return dir_each_entry(dir, dir_yield, Qnil, TRUE);
3410 * call-seq:
3411 * children -> array
3413 * Returns an array of the entry names in +self+
3414 * except for <tt>'.'</tt> and <tt>'..'</tt>:
3416 * dir = Dir.new('/example')
3417 * dir.children # => ["config.h", "lib", "main.rb"]
3420 static VALUE
3421 dir_collect_children(VALUE dir)
3423 VALUE ary = rb_ary_new();
3424 dir_each_entry(dir, rb_ary_push, ary, TRUE);
3425 return ary;
3429 * call-seq:
3430 * Dir.children(dirpath) -> array
3431 * Dir.children(dirpath, encoding: 'UTF-8') -> array
3433 * Returns an array of the entry names in the directory at +dirpath+
3434 * except for <tt>'.'</tt> and <tt>'..'</tt>;
3435 * sets the given encoding onto each returned entry name:
3437 * Dir.children('/example') # => ["config.h", "lib", "main.rb"]
3438 * Dir.children('/example').first.encoding
3439 * # => #<Encoding:UTF-8>
3440 * Dir.children('/example', encoding: 'US-ASCII').first.encoding
3441 * # => #<Encoding:US-ASCII>
3443 * See {String Encoding}[rdoc-ref:encodings.rdoc@String+Encoding].
3445 * Raises an exception if the directory does not exist.
3447 static VALUE
3448 dir_s_children(int argc, VALUE *argv, VALUE io)
3450 VALUE dir;
3452 dir = dir_open_dir(argc, argv);
3453 return rb_ensure(dir_collect_children, dir, dir_close, dir);
3456 static int
3457 fnmatch_brace(const char *pattern, VALUE val, void *enc)
3459 struct brace_args *arg = (struct brace_args *)val;
3460 VALUE path = arg->value;
3461 rb_encoding *enc_pattern = enc;
3462 rb_encoding *enc_path = rb_enc_get(path);
3464 if (enc_pattern != enc_path) {
3465 if (!rb_enc_asciicompat(enc_pattern))
3466 return FNM_NOMATCH;
3467 if (!rb_enc_asciicompat(enc_path))
3468 return FNM_NOMATCH;
3469 if (!rb_enc_str_asciionly_p(path)) {
3470 int cr = ENC_CODERANGE_7BIT;
3471 long len = strlen(pattern);
3472 if (rb_str_coderange_scan_restartable(pattern, pattern + len,
3473 enc_pattern, &cr) != len)
3474 return FNM_NOMATCH;
3475 if (cr != ENC_CODERANGE_7BIT)
3476 return FNM_NOMATCH;
3479 return (fnmatch(pattern, enc, RSTRING_PTR(path), arg->flags) == 0);
3482 /* :nodoc: */
3483 static VALUE
3484 file_s_fnmatch(int argc, VALUE *argv, VALUE obj)
3486 VALUE pattern, path;
3487 VALUE rflags;
3488 int flags;
3490 if (rb_scan_args(argc, argv, "21", &pattern, &path, &rflags) == 3)
3491 flags = NUM2INT(rflags);
3492 else
3493 flags = 0;
3495 StringValueCStr(pattern);
3496 FilePathStringValue(path);
3498 if (flags & FNM_EXTGLOB) {
3499 struct brace_args args;
3501 args.value = path;
3502 args.flags = flags;
3503 if (ruby_brace_expand(RSTRING_PTR(pattern), flags, fnmatch_brace,
3504 (VALUE)&args, rb_enc_get(pattern), pattern) > 0)
3505 return Qtrue;
3507 else {
3508 rb_encoding *enc = rb_enc_compatible(pattern, path);
3509 if (!enc) return Qfalse;
3510 if (fnmatch(RSTRING_PTR(pattern), enc, RSTRING_PTR(path), flags) == 0)
3511 return Qtrue;
3513 RB_GC_GUARD(pattern);
3515 return Qfalse;
3519 * call-seq:
3520 * Dir.home(user_name = nil) -> dirpath
3522 * Retruns the home directory path of the user specified with +user_name+
3523 * if it is not +nil+, or the current login user:
3525 * Dir.home # => "/home/me"
3526 * Dir.home('root') # => "/root"
3528 * Raises ArgumentError if +user_name+ is not a user name.
3530 static VALUE
3531 dir_s_home(int argc, VALUE *argv, VALUE obj)
3533 VALUE user;
3534 const char *u = 0;
3536 rb_check_arity(argc, 0, 1);
3537 user = (argc > 0) ? argv[0] : Qnil;
3538 if (!NIL_P(user)) {
3539 StringValue(user);
3540 rb_must_asciicompat(user);
3541 u = StringValueCStr(user);
3542 if (*u) {
3543 return rb_home_dir_of(user, rb_str_new(0, 0));
3546 return rb_default_home_dir(rb_str_new(0, 0));
3550 #if 0
3552 * call-seq:
3553 * Dir.exist?(dirpath) -> true or false
3555 * Returns whether +dirpath+ is a directory in the underlying file system:
3557 * Dir.exist?('/example') # => true
3558 * Dir.exist?('/nosuch') # => false
3559 * Dir.exist?('/example/main.rb') # => false
3561 * Same as File.directory?.
3564 VALUE
3565 rb_file_directory_p(void)
3568 #endif
3570 static void *
3571 nogvl_dir_empty_p(void *ptr)
3573 const char *path = ptr;
3574 DIR *dir = opendir(path);
3575 struct dirent *dp;
3576 VALUE result = Qtrue;
3578 if (!dir) {
3579 int e = errno;
3580 switch (gc_for_fd_with_gvl(e)) {
3581 default:
3582 dir = opendir(path);
3583 if (dir) break;
3584 e = errno;
3585 /* fall through */
3586 case 0:
3587 if (e == ENOTDIR) return (void *)Qfalse;
3588 return (void *)INT2FIX(e);
3591 while ((dp = READDIR(dir, NULL)) != NULL) {
3592 if (!to_be_skipped(dp)) {
3593 result = Qfalse;
3594 break;
3597 closedir(dir);
3598 return (void *)result;
3602 * call-seq:
3603 * Dir.empty?(dirpath) -> true or false
3605 * Returns whether +dirpath+ specifies an empty directory:
3607 * dirpath = '/tmp/foo'
3608 * Dir.mkdir(dirpath)
3609 * Dir.empty?(dirpath) # => true
3610 * Dir.empty?('/example') # => false
3611 * Dir.empty?('/example/main.rb') # => false
3613 * Raises an exception if +dirpath+ does not specify a directory or file
3614 * in the underlying file system.
3616 static VALUE
3617 rb_dir_s_empty_p(VALUE obj, VALUE dirname)
3619 VALUE result, orig;
3620 const char *path;
3621 enum {false_on_notdir = 1};
3623 FilePathValue(dirname);
3624 orig = rb_str_dup_frozen(dirname);
3625 dirname = rb_str_encode_ospath(dirname);
3626 dirname = rb_str_dup_frozen(dirname);
3627 path = RSTRING_PTR(dirname);
3629 #if defined HAVE_GETATTRLIST && defined ATTR_DIR_ENTRYCOUNT
3631 u_int32_t attrbuf[SIZEUP32(fsobj_tag_t)];
3632 struct attrlist al = {ATTR_BIT_MAP_COUNT, 0, ATTR_CMN_OBJTAG,};
3633 if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), 0) != 0)
3634 rb_sys_fail_path(orig);
3635 if (*(const fsobj_tag_t *)(attrbuf+1) == VT_HFS) {
3636 al.commonattr = 0;
3637 al.dirattr = ATTR_DIR_ENTRYCOUNT;
3638 if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), 0) == 0) {
3639 if (attrbuf[0] >= 2 * sizeof(u_int32_t))
3640 return RBOOL(attrbuf[1] == 0);
3641 if (false_on_notdir) return Qfalse;
3643 rb_sys_fail_path(orig);
3646 #endif
3648 result = (VALUE)IO_WITHOUT_GVL(nogvl_dir_empty_p, (void *)path);
3649 if (FIXNUM_P(result)) {
3650 rb_syserr_fail_path((int)FIX2LONG(result), orig);
3652 return result;
3655 void
3656 Init_Dir(void)
3658 rb_gc_register_address(&chdir_lock.path);
3659 rb_gc_register_address(&chdir_lock.thread);
3661 rb_cDir = rb_define_class("Dir", rb_cObject);
3663 rb_include_module(rb_cDir, rb_mEnumerable);
3665 rb_define_alloc_func(rb_cDir, dir_s_alloc);
3666 rb_define_singleton_method(rb_cDir,"for_fd", dir_s_for_fd, 1);
3667 rb_define_singleton_method(rb_cDir, "foreach", dir_foreach, -1);
3668 rb_define_singleton_method(rb_cDir, "entries", dir_entries, -1);
3669 rb_define_singleton_method(rb_cDir, "each_child", dir_s_each_child, -1);
3670 rb_define_singleton_method(rb_cDir, "children", dir_s_children, -1);
3672 rb_define_method(rb_cDir,"fileno", dir_fileno, 0);
3673 rb_define_method(rb_cDir,"path", dir_path, 0);
3674 rb_define_method(rb_cDir,"to_path", dir_path, 0);
3675 rb_define_method(rb_cDir,"inspect", dir_inspect, 0);
3676 rb_define_method(rb_cDir,"read", dir_read, 0);
3677 rb_define_method(rb_cDir,"each", dir_each, 0);
3678 rb_define_method(rb_cDir,"each_child", dir_each_child_m, 0);
3679 rb_define_method(rb_cDir,"children", dir_collect_children, 0);
3680 rb_define_method(rb_cDir,"rewind", dir_rewind, 0);
3681 rb_define_method(rb_cDir,"tell", dir_tell, 0);
3682 rb_define_method(rb_cDir,"seek", dir_seek, 1);
3683 rb_define_method(rb_cDir,"pos", dir_tell, 0);
3684 rb_define_method(rb_cDir,"pos=", dir_set_pos, 1);
3685 rb_define_method(rb_cDir,"close", dir_close, 0);
3686 rb_define_method(rb_cDir,"chdir", dir_chdir, 0);
3688 rb_define_singleton_method(rb_cDir,"fchdir", dir_s_fchdir, 1);
3689 rb_define_singleton_method(rb_cDir,"chdir", dir_s_chdir, -1);
3690 rb_define_singleton_method(rb_cDir,"getwd", dir_s_getwd, 0);
3691 rb_define_singleton_method(rb_cDir,"pwd", dir_s_getwd, 0);
3692 rb_define_singleton_method(rb_cDir,"chroot", dir_s_chroot, 1);
3693 rb_define_singleton_method(rb_cDir,"mkdir", dir_s_mkdir, -1);
3694 rb_define_singleton_method(rb_cDir,"rmdir", dir_s_rmdir, 1);
3695 rb_define_singleton_method(rb_cDir,"delete", dir_s_rmdir, 1);
3696 rb_define_singleton_method(rb_cDir,"unlink", dir_s_rmdir, 1);
3697 rb_define_singleton_method(rb_cDir,"home", dir_s_home, -1);
3699 rb_define_singleton_method(rb_cDir,"exist?", rb_file_directory_p, 1);
3700 rb_define_singleton_method(rb_cDir,"empty?", rb_dir_s_empty_p, 1);
3702 rb_define_singleton_method(rb_cFile,"fnmatch", file_s_fnmatch, -1);
3703 rb_define_singleton_method(rb_cFile,"fnmatch?", file_s_fnmatch, -1);
3705 /* Document-const: FNM_NOESCAPE
3706 * {File::FNM_NOESCAPE}[rdoc-ref:File::Constants@File-3A-3AFNM_NOESCAPE] */
3707 rb_file_const("FNM_NOESCAPE", INT2FIX(FNM_NOESCAPE));
3708 /* Document-const: FNM_PATHNAME
3709 * {File::FNM_PATHNAME}[rdoc-ref:File::Constants@File-3A-3AFNM_PATHNAME] */
3710 rb_file_const("FNM_PATHNAME", INT2FIX(FNM_PATHNAME));
3711 /* Document-const: FNM_DOTMATCH
3712 * {File::FNM_DOTMATCH}[rdoc-ref:File::Constants@File-3A-3AFNM_DOTMATCH] */
3713 rb_file_const("FNM_DOTMATCH", INT2FIX(FNM_DOTMATCH));
3714 /* Document-const: FNM_CASEFOLD
3715 * {File::FNM_CASEFOLD}[rdoc-ref:File::Constants@File-3A-3AFNM_CASEFOLD] */
3716 rb_file_const("FNM_CASEFOLD", INT2FIX(FNM_CASEFOLD));
3717 /* Document-const: FNM_EXTGLOB
3718 * {File::FNM_EXTGLOB}[rdoc-ref:File::Constants@File-3A-3AFNM_EXTGLOB] */
3719 rb_file_const("FNM_EXTGLOB", INT2FIX(FNM_EXTGLOB));
3720 /* Document-const: FNM_SYSCASE
3721 * {File::FNM_SYSCASE}[rdoc-ref:File::Constants@File-3A-3AFNM_SYSCASE] */
3722 rb_file_const("FNM_SYSCASE", INT2FIX(FNM_SYSCASE));
3723 /* Document-const: FNM_SHORTNAME
3724 * {File::FNM_SHORTNAME}[rdoc-ref:File::Constants@File-3A-3AFNM_SHORTNAME] */
3725 rb_file_const("FNM_SHORTNAME", INT2FIX(FNM_SHORTNAME));
3728 #include "dir.rbinc"